chef 10.24.4 → 10.26.0.beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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