chef 10.24.4 → 10.26.0.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/distro/common/html/chef-client.8.html +3 -3
  2. data/distro/common/html/chef-expander.8.html +3 -3
  3. data/distro/common/html/chef-expanderctl.8.html +3 -3
  4. data/distro/common/html/chef-server-webui.8.html +3 -3
  5. data/distro/common/html/chef-server.8.html +3 -3
  6. data/distro/common/html/chef-solo.8.html +3 -3
  7. data/distro/common/html/chef-solr.8.html +3 -3
  8. data/distro/common/html/knife-bootstrap.1.html +3 -3
  9. data/distro/common/html/knife-client.1.html +3 -3
  10. data/distro/common/html/knife-configure.1.html +3 -3
  11. data/distro/common/html/knife-cookbook-site.1.html +3 -3
  12. data/distro/common/html/knife-cookbook.1.html +3 -3
  13. data/distro/common/html/knife-data-bag.1.html +3 -3
  14. data/distro/common/html/knife-environment.1.html +3 -3
  15. data/distro/common/html/knife-exec.1.html +3 -3
  16. data/distro/common/html/knife-index.1.html +3 -3
  17. data/distro/common/html/knife-node.1.html +3 -3
  18. data/distro/common/html/knife-role.1.html +3 -3
  19. data/distro/common/html/knife-search.1.html +3 -3
  20. data/distro/common/html/knife-ssh.1.html +3 -3
  21. data/distro/common/html/knife-status.1.html +3 -3
  22. data/distro/common/html/knife-tag.1.html +3 -3
  23. data/distro/common/html/knife.1.html +3 -3
  24. data/distro/common/html/shef.1.html +3 -3
  25. data/distro/common/man/man1/knife-bootstrap.1 +1 -1
  26. data/distro/common/man/man1/knife-client.1 +1 -1
  27. data/distro/common/man/man1/knife-configure.1 +1 -1
  28. data/distro/common/man/man1/knife-cookbook-site.1 +1 -1
  29. data/distro/common/man/man1/knife-cookbook.1 +1 -1
  30. data/distro/common/man/man1/knife-data-bag.1 +1 -1
  31. data/distro/common/man/man1/knife-environment.1 +1 -1
  32. data/distro/common/man/man1/knife-exec.1 +1 -1
  33. data/distro/common/man/man1/knife-index.1 +1 -1
  34. data/distro/common/man/man1/knife-node.1 +1 -1
  35. data/distro/common/man/man1/knife-role.1 +1 -1
  36. data/distro/common/man/man1/knife-search.1 +1 -1
  37. data/distro/common/man/man1/knife-ssh.1 +1 -1
  38. data/distro/common/man/man1/knife-status.1 +1 -1
  39. data/distro/common/man/man1/knife-tag.1 +1 -1
  40. data/distro/common/man/man1/knife.1 +1 -1
  41. data/distro/common/man/man1/shef.1 +1 -1
  42. data/distro/common/man/man8/chef-client.8 +1 -1
  43. data/distro/common/man/man8/chef-expander.8 +1 -1
  44. data/distro/common/man/man8/chef-expanderctl.8 +1 -1
  45. data/distro/common/man/man8/chef-server-webui.8 +1 -1
  46. data/distro/common/man/man8/chef-server.8 +1 -1
  47. data/distro/common/man/man8/chef-solo.8 +1 -1
  48. data/distro/common/man/man8/chef-solr.8 +1 -1
  49. data/lib/chef/application.rb +0 -7
  50. data/lib/chef/application/client.rb +8 -3
  51. data/lib/chef/application/windows_service.rb +1 -4
  52. data/lib/chef/client.rb +1 -1
  53. data/lib/chef/cookbook_uploader.rb +10 -1
  54. data/lib/chef/knife/cookbook_upload.rb +3 -9
  55. data/lib/chef/platform.rb +18 -5
  56. data/lib/chef/provider/user/solaris.rb +90 -0
  57. data/lib/chef/provider/user/useradd.rb +38 -29
  58. data/lib/chef/providers.rb +1 -0
  59. data/lib/chef/resource.rb +10 -1
  60. data/lib/chef/shef/shef_session.rb +2 -2
  61. data/lib/chef/version.rb +1 -1
  62. data/spec/unit/application/client_spec.rb +36 -1
  63. data/spec/unit/knife/cookbook_upload_spec.rb +9 -5
  64. data/spec/unit/platform_spec.rb +2 -1
  65. data/spec/unit/provider/user/solaris_spec.rb +414 -0
  66. data/spec/unit/provider/user/useradd_spec.rb +1 -1
  67. data/spec/unit/shef/shef_session_spec.rb +16 -3
  68. metadata +7 -5
@@ -73,6 +73,14 @@ class Chef
73
73
  :mdadm => Chef::Provider::Mdadm
74
74
  }
75
75
  },
76
+ :gcel => {
77
+ :default => {
78
+ :package => Chef::Provider::Package::Apt,
79
+ :service => Chef::Provider::Service::Debian,
80
+ :cron => Chef::Provider::Cron,
81
+ :mdadm => Chef::Provider::Mdadm
82
+ }
83
+ },
76
84
  :linaro => {
77
85
  :default => {
78
86
  :package => Chef::Provider::Package::Apt,
@@ -245,7 +253,8 @@ class Chef
245
253
  :service => Chef::Provider::Service::Solaris,
246
254
  :package => Chef::Provider::Package::Ips,
247
255
  :cron => Chef::Provider::Cron::Solaris,
248
- :group => Chef::Provider::Group::Usermod
256
+ :group => Chef::Provider::Group::Usermod,
257
+ :user => Chef::Provider::User::Solaris,
249
258
  }
250
259
  },
251
260
  :solaris2 => {
@@ -253,19 +262,22 @@ class Chef
253
262
  :service => Chef::Provider::Service::Solaris,
254
263
  :package => Chef::Provider::Package::Ips,
255
264
  :cron => Chef::Provider::Cron::Solaris,
256
- :group => Chef::Provider::Group::Usermod
265
+ :group => Chef::Provider::Group::Usermod,
266
+ :user => Chef::Provider::User::Solaris
257
267
  },
258
268
  "5.9" => {
259
269
  :service => Chef::Provider::Service::Solaris,
260
270
  :package => Chef::Provider::Package::Solaris,
261
271
  :cron => Chef::Provider::Cron::Solaris,
262
- :group => Chef::Provider::Group::Usermod
272
+ :group => Chef::Provider::Group::Usermod,
273
+ :user => Chef::Provider::User::Solaris
263
274
  },
264
275
  "5.10" => {
265
276
  :service => Chef::Provider::Service::Solaris,
266
277
  :package => Chef::Provider::Package::Solaris,
267
278
  :cron => Chef::Provider::Cron::Solaris,
268
- :group => Chef::Provider::Group::Usermod
279
+ :group => Chef::Provider::Group::Usermod,
280
+ :user => Chef::Provider::User::Solaris
269
281
  }
270
282
  },
271
283
  :smartos => {
@@ -273,7 +285,8 @@ class Chef
273
285
  :service => Chef::Provider::Service::Solaris,
274
286
  :package => Chef::Provider::Package::SmartOS,
275
287
  :cron => Chef::Provider::Cron::Solaris,
276
- :group => Chef::Provider::Group::Usermod
288
+ :group => Chef::Provider::Group::Usermod,
289
+ :user => Chef::Provider::User::Solaris
277
290
  }
278
291
  },
279
292
  :netbsd => {
@@ -0,0 +1,90 @@
1
+ #
2
+ # Author:: Stephen Nelson-Smith (<sns@opscode.com>)
3
+ # Author:: Jon Ramsey (<jonathon.ramsey@gmail.com>)
4
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ class Chef
20
+ class Provider
21
+ class User
22
+ class Solaris < Chef::Provider::User::Useradd
23
+ UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
24
+
25
+ attr_writer :password_file
26
+
27
+ def initialize(new_resource, run_context)
28
+ @password_file = "/etc/shadow"
29
+ super
30
+ end
31
+
32
+ def create_user
33
+ super
34
+ manage_password
35
+ end
36
+
37
+ def manage_user
38
+ manage_password
39
+ super
40
+ end
41
+
42
+ private
43
+
44
+ def manage_password
45
+ if @current_resource.password != @new_resource.password && @new_resource.password
46
+ Chef::Log.debug("#{@new_resource} setting password to #{@new_resource.password}")
47
+ write_shadow_file
48
+ end
49
+ end
50
+
51
+ def write_shadow_file
52
+ buffer = Tempfile.new("shadow", "/etc")
53
+ ::File.open(@password_file) do |shadow_file|
54
+ shadow_file.each do |entry|
55
+ user = entry.split(":").first
56
+ if user == @new_resource.username
57
+ buffer.write(updated_password(entry))
58
+ else
59
+ buffer.write(entry)
60
+ end
61
+ end
62
+ end
63
+ buffer.close
64
+
65
+ # FIXME: mostly duplicates code with file provider deploying a file
66
+ mode = ::File.stat(@password_file).mode & 07777
67
+ uid = ::File.stat(@password_file).uid
68
+ gid = ::File.stat(@password_file).gid
69
+
70
+ FileUtils.chown uid, gid, buffer.path
71
+ FileUtils.chmod mode, buffer.path
72
+
73
+ FileUtils.mv buffer.path, @password_file
74
+ end
75
+
76
+ def updated_password(entry)
77
+ fields = entry.split(":")
78
+ fields[1] = @new_resource.password
79
+ fields[2] = days_since_epoch
80
+ fields.join(":")
81
+ end
82
+
83
+ def days_since_epoch
84
+ (Time.now.to_i / 86400).floor
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
@@ -6,9 +6,9 @@
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
8
8
  # You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,7 +21,7 @@ require 'chef/provider/user'
21
21
 
22
22
  class Chef
23
23
  class Provider
24
- class User
24
+ class User
25
25
  class Useradd < Chef::Provider::User
26
26
  UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]]
27
27
 
@@ -32,21 +32,23 @@ class Chef
32
32
  end
33
33
  run_command(:command => command)
34
34
  end
35
-
35
+
36
36
  def manage_user
37
- command = compile_command("usermod") do |u|
38
- u << universal_options
37
+ if universal_options != ""
38
+ command = compile_command("usermod") do |u|
39
+ u << universal_options
40
+ end
41
+ run_command(:command => command)
39
42
  end
40
- run_command(:command => command)
41
43
  end
42
-
44
+
43
45
  def remove_user
44
46
  command = "userdel"
45
47
  command << " -r" if managing_home_dir?
46
48
  command << " #{@new_resource.username}"
47
49
  run_command(:command => command)
48
50
  end
49
-
51
+
50
52
  def check_lock
51
53
  status = popen4("passwd -S #{@new_resource.username}") do |pid, stdin, stdout, stderr|
52
54
  status_line = stdout.gets.split(' ')
@@ -80,11 +82,11 @@ class Chef
80
82
 
81
83
  @locked
82
84
  end
83
-
85
+
84
86
  def lock_user
85
87
  run_command(:command => "usermod -L #{@new_resource.username}")
86
88
  end
87
-
89
+
88
90
  def unlock_user
89
91
  run_command(:command => "usermod -U #{@new_resource.username}")
90
92
  end
@@ -94,29 +96,36 @@ class Chef
94
96
  base_command << " #{@new_resource.username}"
95
97
  base_command
96
98
  end
97
-
99
+
98
100
  def universal_options
99
- opts = ''
100
-
101
- UNIVERSAL_OPTIONS.each do |field, option|
102
- if @current_resource.send(field) != @new_resource.send(field)
103
- if @new_resource.send(field)
104
- Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field)}")
105
- opts << " #{option} '#{@new_resource.send(field)}'"
101
+ @universal_options ||=
102
+ begin
103
+ opts = ''
104
+ # magic allows UNIVERSAL_OPTIONS to be overridden in a subclass
105
+ self.class::UNIVERSAL_OPTIONS.each do |field, option|
106
+ update_options(field, option, opts)
106
107
  end
108
+ if updating_home?
109
+ if managing_home_dir?
110
+ Chef::Log.debug("#{@new_resource} managing the users home directory")
111
+ opts << " -m -d '#{@new_resource.home}'"
112
+ else
113
+ Chef::Log.debug("#{@new_resource} setting home to #{@new_resource.home}")
114
+ opts << " -d '#{@new_resource.home}'"
115
+ end
116
+ end
117
+ opts << " -o" if @new_resource.non_unique || @new_resource.supports[:non_unique]
118
+ opts
107
119
  end
108
- end
109
- if updating_home?
110
- if managing_home_dir?
111
- Chef::Log.debug("#{@new_resource} managing the users home directory")
112
- opts << " -m -d '#{@new_resource.home}'"
113
- else
114
- Chef::Log.debug("#{@new_resource} setting home to #{@new_resource.home}")
115
- opts << " -d '#{@new_resource.home}'"
120
+ end
121
+
122
+ def update_options(field, option, opts)
123
+ if @current_resource.send(field) != @new_resource.send(field)
124
+ if @new_resource.send(field)
125
+ Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field)}")
126
+ opts << " #{option} '#{@new_resource.send(field)}'"
116
127
  end
117
128
  end
118
- opts << " -o" if @new_resource.non_unique || @new_resource.supports[:non_unique]
119
- opts
120
129
  end
121
130
 
122
131
  def useradd_options
@@ -82,6 +82,7 @@ require 'chef/provider/user/dscl'
82
82
  require 'chef/provider/user/pw'
83
83
  require 'chef/provider/user/useradd'
84
84
  require 'chef/provider/user/windows'
85
+ require 'chef/provider/user/solaris'
85
86
 
86
87
  require 'chef/provider/group/aix'
87
88
  require 'chef/provider/group/dscl'
@@ -146,6 +146,15 @@ F
146
146
  include Chef::Mixin::ConvertToClassName
147
147
  include Chef::Mixin::Deprecation
148
148
 
149
+ if Module.method(:const_defined?).arity == 1
150
+ def self.strict_const_defined?(const)
151
+ const_defined?(const)
152
+ end
153
+ else
154
+ def self.strict_const_defined?(const)
155
+ const_defined?(const, false)
156
+ end
157
+ end
149
158
 
150
159
  # Set or return the list of "state attributes" implemented by the Resource
151
160
  # subclass. State attributes are attributes that describe the desired state
@@ -731,7 +740,7 @@ F
731
740
 
732
741
  # Add log entry if we override an existing light-weight resource.
733
742
  class_name = convert_to_class_name(rname)
734
- if Chef::Resource.const_defined?(class_name, false)
743
+ if Chef::Resource.strict_const_defined?(class_name)
735
744
  Chef::Log.info("#{class_name} light-weight resource already initialized -- overriding!")
736
745
  old_class = Chef::Resource.send(:remove_const, class_name)
737
746
  Chef::Resource.resource_classes.delete(old_class)
@@ -202,8 +202,8 @@ module Shef
202
202
  Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, Chef::REST.new(Chef::Config[:server_url])) }
203
203
  cookbook_hash = @client.sync_cookbooks
204
204
  cookbook_collection = Chef::CookbookCollection.new(cookbook_hash)
205
- @run_context = Chef::RunContext.new(node, cookbook_collection, @events)
206
- @run_context.load(Chef::RunList::RunListExpansionFromAPI.new("_default", []))
205
+ @run_context = Chef::RunContext.new(node, cookbook_collection, @events)
206
+ @run_context.load(@node.run_list.expand(@node.chef_environment))
207
207
  @run_status.run_context = run_context
208
208
  end
209
209
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  class Chef
19
19
  CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
20
- VERSION = '10.24.4'
20
+ VERSION = '10.26.0.beta.0'
21
21
  end
22
22
 
23
23
  # NOTE: the Chef::Version class is defined in version_class.rb
@@ -19,7 +19,7 @@ require 'spec_helper'
19
19
 
20
20
  describe Chef::Application::Client, "reconfigure" do
21
21
  before do
22
- @original_config = Chef::Config.configuration
22
+ @original_config = Chef::Config.configuration.dup
23
23
 
24
24
  @app = Chef::Application::Client.new
25
25
  @app.stub!(:configure_opt_parser).and_return(true)
@@ -134,3 +134,38 @@ describe Chef::Application::Client, "setup_application" do
134
134
  Chef::Config[:solo] = false
135
135
  end
136
136
  end
137
+
138
+ describe Chef::Application::Client, "run_application", :unix_only do
139
+ before do
140
+ Chef::Config[:daemonize] = false
141
+ Chef::Config[:interval] = 10
142
+
143
+ @pipe = IO.pipe
144
+ @app = Chef::Application::Client.new
145
+ @app.stub(:run_chef_client) do
146
+ @pipe[1].puts 'started'
147
+ sleep 1
148
+ @pipe[1].puts 'finished'
149
+ end
150
+ end
151
+
152
+ it "should exit gracefully when sent SIGTERM" do
153
+ pid = fork do
154
+ begin
155
+ @app.run_application
156
+ rescue SystemExit # expected
157
+ rescue Exception => e
158
+ $stderr.puts e
159
+ $stderr.puts e.backtrace
160
+ ensure
161
+ exit!
162
+ end
163
+ end
164
+ IO.select([@pipe[0]], nil, nil, 10).should_not be_nil
165
+ @pipe[0].gets.should == "started\n"
166
+ Process.kill("TERM", pid)
167
+ Process.wait
168
+ IO.select([@pipe[0]], nil, nil, 0).should_not be_nil
169
+ @pipe[0].gets.should == "finished\n"
170
+ end
171
+ end
@@ -41,7 +41,8 @@ describe Chef::Knife::CookbookUpload do
41
41
 
42
42
  describe 'run' do
43
43
  before(:each) do
44
- @knife.stub!(:upload).and_return(true)
44
+ @cookbook_uploader = stub(:upload_cookbooks => nil)
45
+ Chef::CookbookUploader.stub(:new => @cookbook_uploader)
45
46
  Chef::CookbookVersion.stub(:list_all_versions).and_return({})
46
47
  end
47
48
 
@@ -125,8 +126,11 @@ describe Chef::Knife::CookbookUpload do
125
126
 
126
127
  describe 'when a frozen cookbook exists on the server' do
127
128
  it 'should fail to replace it' do
128
- @knife.stub!(:upload).and_raise(Chef::Exceptions::CookbookFrozen)
129
- @knife.ui.should_receive(:error).with(/Failed to upload 1 cookbook/)
129
+ exception = Chef::Exceptions::CookbookFrozen.new
130
+ @cookbook_uploader.should_receive(:upload_cookbooks).
131
+ and_raise(exception)
132
+ @knife.ui.stub(:error)
133
+ @knife.ui.should_receive(:error).with(exception)
130
134
  lambda { @knife.run }.should raise_error(SystemExit)
131
135
  end
132
136
 
@@ -140,5 +144,5 @@ describe Chef::Knife::CookbookUpload do
140
144
  lambda { @knife.run }.should raise_error(SystemExit)
141
145
  end
142
146
  end
143
- end
144
- end # run
147
+ end # run
148
+ end
@@ -35,7 +35,8 @@ describe "Chef::Platform supports" do
35
35
  :solaris,
36
36
  :mswin,
37
37
  :mingw32,
38
- :windows
38
+ :windows,
39
+ :gcel
39
40
  ].each do |platform|
40
41
  it "#{platform}" do
41
42
  Chef::Platform.platforms.should have_key(platform)
@@ -0,0 +1,414 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
5
+ #
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'spec_helper'
22
+
23
+ describe Chef::Provider::User::Solaris do
24
+ before(:each) do
25
+ @node = Chef::Node.new
26
+ @events = Chef::EventDispatch::Dispatcher.new
27
+ @run_context = Chef::RunContext.new(@node, {}, @events)
28
+
29
+ @new_resource = Chef::Resource::User.new("adam", @run_context)
30
+ @new_resource.comment "Adam Jacob"
31
+ @new_resource.uid 1000
32
+ @new_resource.gid 1000
33
+ @new_resource.home "/home/adam"
34
+ @new_resource.shell "/usr/bin/zsh"
35
+ @new_resource.password "abracadabra"
36
+ @new_resource.system false
37
+ @new_resource.manage_home false
38
+ @new_resource.non_unique false
39
+ @current_resource = Chef::Resource::User.new("adam", @run_context)
40
+ @current_resource.comment "Adam Jacob"
41
+ @current_resource.uid 1000
42
+ @current_resource.gid 1000
43
+ @current_resource.home "/home/adam"
44
+ @current_resource.shell "/usr/bin/zsh"
45
+ @current_resource.password "abracadabra"
46
+ @current_resource.system false
47
+ @current_resource.manage_home false
48
+ @current_resource.non_unique false
49
+ @current_resource.supports({:manage_home => false, :non_unique => false})
50
+ @provider = Chef::Provider::User::Solaris.new(@new_resource, @run_context)
51
+ @provider.current_resource = @current_resource
52
+ end
53
+
54
+ describe "when setting option" do
55
+ field_list = {
56
+ 'comment' => "-c",
57
+ 'gid' => "-g",
58
+ 'uid' => "-u",
59
+ 'shell' => "-s"
60
+ }
61
+
62
+ field_list.each do |attribute, option|
63
+ it "should check for differences in #{attribute} between the new and current resources" do
64
+ @current_resource.should_receive(attribute)
65
+ @new_resource.should_receive(attribute)
66
+ @provider.universal_options
67
+ end
68
+
69
+ it "should set the option for #{attribute} if the new resources #{attribute} is not nil" do
70
+ @new_resource.stub!(attribute).and_return("hola")
71
+ @provider.universal_options.should eql(" #{option} 'hola'")
72
+ end
73
+
74
+ it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management" do
75
+ @new_resource.stub!(:supports).and_return({:manage_home => false,
76
+ :non_unique => false})
77
+ @new_resource.stub!(attribute).and_return("hola")
78
+ @provider.universal_options.should eql(" #{option} 'hola'")
79
+ end
80
+
81
+ it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management (using real attributes)" do
82
+ @new_resource.stub!(:manage_home).and_return(false)
83
+ @new_resource.stub!(:non_unique).and_return(false)
84
+ @new_resource.stub!(attribute).and_return("hola")
85
+ @provider.universal_options.should eql(" #{option} 'hola'")
86
+ end
87
+ end
88
+
89
+ it "should combine all the possible options" do
90
+ match_string = ""
91
+ field_list.sort{ |a,b| a[0] <=> b[0] }.each do |attribute, option|
92
+ @new_resource.stub!(attribute).and_return("hola")
93
+ match_string << " #{option} 'hola'"
94
+ end
95
+ @provider.universal_options.should eql(match_string)
96
+ end
97
+
98
+ describe "when we want to set a password" do
99
+ before do
100
+ @new_resource.password "hocus-pocus"
101
+ end
102
+
103
+ it "should use its own shadow file writer to set the password" do
104
+ @provider.should_receive(:write_shadow_file)
105
+ @provider.stub!(:run_command).and_return(true)
106
+ @provider.manage_user
107
+ end
108
+
109
+ it "should write out a modified version of the password file" do
110
+ password_file = Tempfile.new("shadow")
111
+ password_file.puts "adam:existingpassword:15441::::::"
112
+ password_file.close
113
+ @provider.password_file = password_file.path
114
+ @provider.stub!(:run_command).and_return(true)
115
+ # may not be able to write to /etc for tests...
116
+ temp_file = Tempfile.new("shadow")
117
+ Tempfile.stub!(:new).with("shadow", "/etc").and_return(temp_file)
118
+ @new_resource.password "verysecurepassword"
119
+ @provider.manage_user
120
+ ::File.open(password_file.path, "r").read.should =~ /adam:verysecurepassword:/
121
+ password_file.unlink
122
+ end
123
+ end
124
+
125
+ describe "when we want to create a system user" do
126
+ before do
127
+ @new_resource.manage_home(true)
128
+ @new_resource.non_unique(false)
129
+ end
130
+
131
+ it "should set useradd -r" do
132
+ @new_resource.system(true)
133
+ @provider.useradd_options.should == " -r"
134
+ end
135
+ end
136
+
137
+ describe "when the resource has a different home directory and supports home directory management" do
138
+ before do
139
+ @new_resource.stub!(:home).and_return("/wowaweea")
140
+ @new_resource.stub!(:supports).and_return({:manage_home => true,
141
+ :non_unique => false})
142
+ end
143
+
144
+ it "should set -m -d /homedir" do
145
+ @provider.universal_options.should == " -m -d '/wowaweea'"
146
+ @provider.useradd_options.should == ""
147
+ end
148
+ end
149
+
150
+ describe "when the resource has a different home directory and supports home directory management (using real attributes)" do
151
+ before do
152
+ @new_resource.stub!(:home).and_return("/wowaweea")
153
+ @new_resource.stub!(:manage_home).and_return(true)
154
+ @new_resource.stub!(:non_unique).and_return(false)
155
+ end
156
+
157
+ it "should set -m -d /homedir" do
158
+ @provider.universal_options.should eql(" -m -d '/wowaweea'")
159
+ @provider.useradd_options.should == ""
160
+ end
161
+ end
162
+
163
+ describe "when the resource supports non_unique ids" do
164
+ before do
165
+ @new_resource.stub!(:supports).and_return({:manage_home => false,
166
+ :non_unique => true})
167
+ end
168
+
169
+ it "should set -m -o" do
170
+ @provider.universal_options.should eql(" -o")
171
+ end
172
+ end
173
+
174
+ describe "when the resource supports non_unique ids (using real attributes)" do
175
+ before do
176
+ @new_resource.stub!(:manage_home).and_return(false)
177
+ @new_resource.stub!(:non_unique).and_return(true)
178
+ end
179
+
180
+ it "should set -m -o" do
181
+ @provider.universal_options.should eql(" -o")
182
+ end
183
+ end
184
+ end
185
+
186
+ describe "when creating a user" do
187
+ before(:each) do
188
+ @current_resource = Chef::Resource::User.new(@new_resource.name, @run_context)
189
+ @current_resource.username(@new_resource.username)
190
+ @provider.current_resource = @current_resource
191
+ @provider.new_resource.manage_home true
192
+ @provider.new_resource.home "/Users/mud"
193
+ @provider.new_resource.gid '23'
194
+ end
195
+
196
+ it "runs useradd with the computed command options" do
197
+ command = "useradd -c 'Adam Jacob' -g '23' -s '/usr/bin/zsh' -u '1000' -m -d '/Users/mud' adam"
198
+ @provider.should_receive(:run_command).with({ :command => command }).and_return(true)
199
+ @provider.should_receive(:manage_password).and_return(nil)
200
+ @provider.create_user
201
+ end
202
+
203
+ describe "and home is not specified for new system user resource" do
204
+
205
+ before do
206
+ @provider.new_resource.system true
207
+ # there is no public API to set attribute's value to nil
208
+ @provider.new_resource.instance_variable_set("@home", nil)
209
+ end
210
+
211
+ it "should not include -m or -d in the command options" do
212
+ command = "useradd -c 'Adam Jacob' -g '23' -s '/usr/bin/zsh' -u '1000' -r adam"
213
+ @provider.should_receive(:run_command).with({ :command => command }).and_return(true)
214
+ @provider.should_receive(:manage_password).and_return(nil)
215
+ @provider.create_user
216
+ end
217
+
218
+ end
219
+
220
+ end
221
+
222
+ describe "when managing a user" do
223
+ before(:each) do
224
+ @provider.new_resource.manage_home true
225
+ @provider.new_resource.home "/Users/mud"
226
+ @provider.new_resource.gid '23'
227
+ end
228
+
229
+ # CHEF-3423, -m must come before the username
230
+ it "runs usermod with the computed command options" do
231
+ @provider.should_receive(:run_command).with({ :command => "usermod -g '23' -m -d '/Users/mud' adam" }).and_return(true)
232
+ @provider.manage_user
233
+ end
234
+
235
+ it "does not set the -r option to usermod" do
236
+ @new_resource.system(true)
237
+ @provider.should_receive(:run_command).with({ :command => "usermod -g '23' -m -d '/Users/mud' adam" }).and_return(true)
238
+ @provider.manage_user
239
+ end
240
+
241
+ it "CHEF-3429: does not set -m if we aren't changing the home directory" do
242
+ @provider.should_receive(:updating_home?).and_return(false)
243
+ @provider.should_receive(:run_command).with({ :command => "usermod -g '23' adam" }).and_return(true)
244
+ @provider.manage_user
245
+ end
246
+ end
247
+
248
+ describe "when removing a user" do
249
+
250
+ it "should run userdel with the new resources user name" do
251
+ @provider.should_receive(:run_command).with({ :command => "userdel #{@new_resource.username}" }).and_return(true)
252
+ @provider.remove_user
253
+ end
254
+
255
+ it "should run userdel with the new resources user name and -r if manage_home is true" do
256
+ @new_resource.stub!(:supports).and_return({ :manage_home => true,
257
+ :non_unique => false})
258
+ @provider.should_receive(:run_command).with({ :command => "userdel -r #{@new_resource.username}"}).and_return(true)
259
+ @provider.remove_user
260
+ end
261
+
262
+ it "should run userdel with the new resources user name if non_unique is true" do
263
+ @new_resource.stub!(:supports).and_return({ :manage_home => false,
264
+ :non_unique => true})
265
+ @provider.should_receive(:run_command).with({ :command => "userdel #{@new_resource.username}"}).and_return(true)
266
+ @provider.remove_user
267
+ end
268
+ end
269
+
270
+ describe "when checking the lock" do
271
+ before(:each) do
272
+ # @node = Chef::Node.new
273
+ # @new_resource = mock("Chef::Resource::User",
274
+ # :nil_object => true,
275
+ # :username => "adam"
276
+ # )
277
+ @status = mock("Status", :exitstatus => 0)
278
+ #@provider = Chef::Provider::User::Useradd.new(@node, @new_resource)
279
+ @provider.stub!(:popen4).and_return(@status)
280
+ @stdin = mock("STDIN", :nil_object => true)
281
+ @stdout = mock("STDOUT", :nil_object => true)
282
+ @stdout.stub!(:gets).and_return("root P 09/02/2008 0 99999 7 -1")
283
+ @stderr = mock("STDERR", :nil_object => true)
284
+ @pid = mock("PID", :nil_object => true)
285
+ end
286
+
287
+ it "should call passwd -S to check the lock status" do
288
+ @provider.should_receive(:popen4).with("passwd -S #{@new_resource.username}").and_return(@status)
289
+ @provider.check_lock
290
+ end
291
+
292
+ it "should get the first line of passwd -S STDOUT" do
293
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
294
+ @stdout.should_receive(:gets).and_return("root P 09/02/2008 0 99999 7 -1")
295
+ @provider.check_lock
296
+ end
297
+
298
+ it "should return false if status begins with P" do
299
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
300
+ @provider.check_lock.should eql(false)
301
+ end
302
+
303
+ it "should return false if status begins with N" do
304
+ @stdout.stub!(:gets).and_return("root N")
305
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
306
+ @provider.check_lock.should eql(false)
307
+ end
308
+
309
+ it "should return true if status begins with L" do
310
+ @stdout.stub!(:gets).and_return("root L")
311
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
312
+ @provider.check_lock.should eql(true)
313
+ end
314
+
315
+ it "should raise a Chef::Exceptions::User if passwd -S fails on anything other than redhat/centos" do
316
+ @node.automatic_attrs[:platform] = 'ubuntu'
317
+ @status.should_receive(:exitstatus).and_return(1)
318
+ lambda { @provider.check_lock }.should raise_error(Chef::Exceptions::User)
319
+ end
320
+
321
+ ['redhat', 'centos'].each do |os|
322
+ it "should not raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is version 0.73-1" do
323
+ @node.automatic_attrs[:platform] = os
324
+ @stdout.stub!(:gets).and_return("passwd-0.73-1\n")
325
+ @status.should_receive(:exitstatus).twice.and_return(1)
326
+ @provider.should_receive(:popen4).with("passwd -S #{@new_resource.username}")
327
+ @provider.should_receive(:popen4).with("rpm -q passwd").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
328
+ lambda { @provider.check_lock }.should_not raise_error(Chef::Exceptions::User)
329
+ end
330
+
331
+ it "should raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is not version 0.73-1" do
332
+ @node.automatic_attrs[:platform] = os
333
+ @stdout.stub!(:gets).and_return("passwd-0.73-2\n")
334
+ @status.should_receive(:exitstatus).twice.and_return(1)
335
+ @provider.should_receive(:popen4).with("passwd -S #{@new_resource.username}")
336
+ @provider.should_receive(:popen4).with("rpm -q passwd").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
337
+ lambda { @provider.check_lock }.should raise_error(Chef::Exceptions::User)
338
+ end
339
+
340
+ it "should raise a Chef::Exceptions::User if passwd -S exits with something other than 0 or 1 on #{os}" do
341
+ @node.automatic_attrs[:platform] = os
342
+ @status.should_receive(:exitstatus).twice.and_return(2)
343
+ lambda { @provider.check_lock }.should raise_error(Chef::Exceptions::User)
344
+ end
345
+ end
346
+ end
347
+
348
+ describe "when locking the user" do
349
+ it "should run usermod -L with the new resources username" do
350
+ @provider.should_receive(:run_command).with({ :command => "usermod -L #{@new_resource.username}"})
351
+ @provider.lock_user
352
+ end
353
+ end
354
+
355
+ describe "when unlocking the user" do
356
+ it "should run usermod -L with the new resources username" do
357
+ @provider.should_receive(:run_command).with({ :command => "usermod -U #{@new_resource.username}"})
358
+ @provider.unlock_user
359
+ end
360
+ end
361
+
362
+ describe "when checking if home needs updating" do
363
+ [
364
+ {
365
+ "action" => "should return false if home matches",
366
+ "current_resource_home" => [ "/home/laurent" ],
367
+ "new_resource_home" => [ "/home/laurent" ],
368
+ "expected_result" => false
369
+ },
370
+ {
371
+ "action" => "should return true if home doesn't match",
372
+ "current_resource_home" => [ "/home/laurent" ],
373
+ "new_resource_home" => [ "/something/else" ],
374
+ "expected_result" => true
375
+ },
376
+ {
377
+ "action" => "should return false if home only differs by trailing slash",
378
+ "current_resource_home" => [ "/home/laurent" ],
379
+ "new_resource_home" => [ "/home/laurent/", "/home/laurent" ],
380
+ "expected_result" => false
381
+ },
382
+ {
383
+ "action" => "should return false if home is an equivalent path",
384
+ "current_resource_home" => [ "/home/laurent" ],
385
+ "new_resource_home" => [ "/home/./laurent", "/home/laurent" ],
386
+ "expected_result" => false
387
+ },
388
+ ].each do |home_check|
389
+ it home_check["action"] do
390
+ @provider.current_resource.home home_check["current_resource_home"].first
391
+ @current_home_mock = mock("Pathname")
392
+ @provider.new_resource.home home_check["new_resource_home"].first
393
+ @new_home_mock = mock("Pathname")
394
+
395
+ Pathname.should_receive(:new).with(@current_resource.home).and_return(@current_home_mock)
396
+ @current_home_mock.should_receive(:cleanpath).and_return(home_check["current_resource_home"].last)
397
+ Pathname.should_receive(:new).with(@new_resource.home).and_return(@new_home_mock)
398
+ @new_home_mock.should_receive(:cleanpath).and_return(home_check["new_resource_home"].last)
399
+
400
+ @provider.updating_home?.should == home_check["expected_result"]
401
+ end
402
+ end
403
+ it "should return true if the current home does not exist but a home is specified by the new resource" do
404
+ @new_resource = Chef::Resource::User.new("adam", @run_context)
405
+ @current_resource = Chef::Resource::User.new("adam", @run_context)
406
+ @provider = Chef::Provider::User::Solaris.new(@new_resource, @run_context)
407
+ @provider.current_resource = @current_resource
408
+ @current_resource.home nil
409
+ @new_resource.home "/home/kitten"
410
+
411
+ @provider.updating_home?.should == true
412
+ end
413
+ end
414
+ end