chef 0.7.16 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chef might be problematic. Click here for more details.

Files changed (180) hide show
  1. data/README.rdoc +11 -10
  2. data/bin/chef-client +2 -2
  3. data/bin/chef-solo +1 -1
  4. data/bin/knife +27 -0
  5. data/bin/shef +49 -0
  6. data/distro/README +2 -0
  7. data/distro/{debian → common}/man/man1/chef-indexer.1 +0 -0
  8. data/distro/{debian → common}/man/man1/chef-server.1 +0 -0
  9. data/distro/{debian → common}/man/man8/chef-client.8 +0 -0
  10. data/distro/{debian → common}/man/man8/chef-solo.8 +0 -0
  11. data/distro/common/man/man8/knife.8 +375 -0
  12. data/distro/redhat/etc/init.d/chef-client +8 -4
  13. data/distro/redhat/etc/init.d/chef-server +16 -15
  14. data/distro/redhat/etc/init.d/chef-server-webui +78 -0
  15. data/distro/redhat/etc/init.d/chef-solr +76 -0
  16. data/distro/redhat/etc/init.d/chef-solr-indexer +75 -0
  17. data/distro/redhat/etc/sysconfig/chef-client +10 -0
  18. data/distro/redhat/etc/sysconfig/chef-server +10 -0
  19. data/distro/redhat/etc/sysconfig/chef-server-webui +10 -0
  20. data/distro/redhat/etc/sysconfig/chef-solr +9 -0
  21. data/distro/redhat/etc/sysconfig/chef-solr-indexer +7 -0
  22. data/distro/suse/etc/init.d/chef-client +121 -0
  23. data/lib/chef.rb +1 -1
  24. data/lib/chef/api_client.rb +263 -0
  25. data/lib/chef/application.rb +1 -1
  26. data/lib/chef/application/client.rb +21 -3
  27. data/lib/chef/application/knife.rb +144 -0
  28. data/lib/chef/application/server.rb +2 -1
  29. data/lib/chef/application/solo.rb +9 -2
  30. data/lib/chef/cache.rb +61 -0
  31. data/lib/chef/cache/checksum.rb +70 -0
  32. data/lib/chef/certificate.rb +154 -0
  33. data/lib/chef/client.rb +123 -220
  34. data/lib/chef/compile.rb +9 -21
  35. data/lib/chef/config.rb +67 -10
  36. data/lib/chef/cookbook.rb +49 -22
  37. data/lib/chef/cookbook/metadata.rb +85 -5
  38. data/lib/chef/cookbook_loader.rb +4 -4
  39. data/lib/chef/couchdb.rb +99 -30
  40. data/lib/chef/daemon.rb +1 -1
  41. data/lib/chef/data_bag.rb +215 -0
  42. data/lib/chef/data_bag_item.rb +219 -0
  43. data/lib/chef/exceptions.rb +3 -0
  44. data/lib/chef/index_queue.rb +29 -0
  45. data/lib/chef/index_queue/amqp_client.rb +106 -0
  46. data/lib/chef/index_queue/consumer.rb +76 -0
  47. data/lib/chef/index_queue/indexable.rb +74 -0
  48. data/lib/chef/knife.rb +309 -0
  49. data/lib/chef/knife/client_bulk_delete.rb +40 -0
  50. data/lib/chef/knife/client_create.rb +62 -0
  51. data/lib/chef/knife/client_delete.rb +37 -0
  52. data/lib/chef/knife/client_edit.rb +37 -0
  53. data/lib/chef/knife/client_list.rb +40 -0
  54. data/lib/chef/knife/client_reregister.rb +48 -0
  55. data/lib/chef/knife/client_show.rb +42 -0
  56. data/lib/chef/knife/configure.rb +123 -0
  57. data/lib/chef/knife/cookbook_bulk_delete.rb +46 -0
  58. data/lib/chef/knife/cookbook_delete.rb +41 -0
  59. data/lib/chef/knife/cookbook_download.rb +57 -0
  60. data/lib/chef/knife/cookbook_list.rb +41 -0
  61. data/lib/chef/knife/cookbook_metadata.rb +87 -0
  62. data/lib/chef/knife/cookbook_show.rb +75 -0
  63. data/lib/chef/knife/cookbook_upload.rb +179 -0
  64. data/lib/chef/knife/data_bag_create.rb +43 -0
  65. data/lib/chef/knife/data_bag_delete.rb +43 -0
  66. data/lib/chef/knife/data_bag_edit.rb +49 -0
  67. data/lib/chef/knife/data_bag_list.rb +42 -0
  68. data/lib/chef/knife/data_bag_show.rb +40 -0
  69. data/lib/chef/knife/ec2_instance_data.rb +46 -0
  70. data/lib/chef/knife/index_rebuild.rb +51 -0
  71. data/lib/chef/knife/node_bulk_delete.rb +43 -0
  72. data/lib/chef/knife/node_create.rb +39 -0
  73. data/lib/chef/knife/node_delete.rb +36 -0
  74. data/lib/chef/knife/node_edit.rb +36 -0
  75. data/lib/chef/knife/node_from_file.rb +42 -0
  76. data/lib/chef/knife/node_list.rb +41 -0
  77. data/lib/chef/knife/node_run_list_add.rb +64 -0
  78. data/lib/chef/knife/node_run_list_remove.rb +45 -0
  79. data/lib/chef/knife/node_show.rb +46 -0
  80. data/lib/chef/knife/role_bulk_delete.rb +44 -0
  81. data/lib/chef/knife/role_create.rb +44 -0
  82. data/lib/chef/knife/role_delete.rb +36 -0
  83. data/lib/chef/knife/role_edit.rb +37 -0
  84. data/lib/chef/knife/role_from_file.rb +46 -0
  85. data/lib/chef/knife/role_list.rb +40 -0
  86. data/lib/chef/knife/role_show.rb +43 -0
  87. data/lib/chef/knife/search.rb +94 -0
  88. data/lib/chef/knife/ssh.rb +170 -0
  89. data/lib/chef/log.rb +30 -8
  90. data/lib/chef/mixin/checksum.rb +2 -7
  91. data/lib/chef/mixin/command.rb +32 -13
  92. data/lib/chef/mixin/convert_to_class_name.rb +15 -0
  93. data/lib/chef/mixin/deep_merge.rb +199 -11
  94. data/lib/chef/mixin/generate_url.rb +18 -9
  95. data/lib/chef/mixin/language.rb +29 -1
  96. data/lib/chef/mixin/language_include_attribute.rb +56 -0
  97. data/lib/chef/mixin/language_include_recipe.rb +53 -0
  98. data/lib/chef/mixin/params_validate.rb +25 -12
  99. data/lib/chef/mixin/recipe_definition_dsl_core.rb +2 -0
  100. data/lib/chef/mixin/template.rb +11 -1
  101. data/lib/chef/mixin/xml_escape.rb +87 -0
  102. data/lib/chef/node.rb +144 -122
  103. data/lib/chef/openid_registration.rb +12 -5
  104. data/lib/chef/platform.rb +89 -47
  105. data/lib/chef/provider/breakpoint.rb +36 -0
  106. data/lib/chef/provider/cron.rb +5 -6
  107. data/lib/chef/provider/deploy.rb +43 -10
  108. data/lib/chef/provider/deploy/revision.rb +2 -3
  109. data/lib/chef/provider/erl_call.rb +72 -0
  110. data/lib/chef/provider/file.rb +8 -4
  111. data/lib/chef/provider/git.rb +10 -5
  112. data/lib/chef/provider/group/dscl.rb +128 -0
  113. data/lib/chef/provider/http_request.rb +6 -2
  114. data/lib/chef/provider/ifconfig.rb +1 -0
  115. data/lib/chef/provider/link.rb +1 -1
  116. data/lib/chef/provider/log.rb +53 -0
  117. data/lib/chef/provider/mdadm.rb +88 -0
  118. data/lib/chef/provider/mount/mount.rb +1 -1
  119. data/lib/chef/provider/package.rb +1 -1
  120. data/lib/chef/provider/package/easy_install.rb +106 -0
  121. data/lib/chef/provider/package/pacman.rb +101 -0
  122. data/lib/chef/provider/package/portage.rb +1 -1
  123. data/lib/chef/provider/package/rpm.rb +10 -8
  124. data/lib/chef/provider/package/yum-dump.py +22 -3
  125. data/lib/chef/provider/package/yum.rb +32 -8
  126. data/lib/chef/provider/package/zypper.rb +132 -0
  127. data/lib/chef/provider/remote_directory.rb +58 -49
  128. data/lib/chef/provider/remote_file.rb +1 -1
  129. data/lib/chef/provider/route.rb +136 -80
  130. data/lib/chef/provider/ruby_block.rb +18 -1
  131. data/lib/chef/provider/service/arch.rb +109 -0
  132. data/lib/chef/provider/service/freebsd.rb +0 -1
  133. data/lib/chef/provider/service/simple.rb +2 -3
  134. data/lib/chef/provider/service/upstart.rb +191 -0
  135. data/lib/chef/provider/subversion.rb +12 -4
  136. data/lib/chef/provider/template.rb +85 -53
  137. data/lib/chef/provider/user.rb +1 -1
  138. data/lib/chef/provider/user/dscl.rb +277 -0
  139. data/lib/chef/provider/user/useradd.rb +1 -0
  140. data/lib/chef/recipe.rb +2 -41
  141. data/lib/chef/resource.rb +9 -3
  142. data/lib/chef/resource/breakpoint.rb +35 -0
  143. data/lib/chef/resource/deploy.rb +16 -2
  144. data/lib/chef/resource/easy_install_package.rb +41 -0
  145. data/lib/chef/resource/erl_call.rb +83 -0
  146. data/lib/chef/resource/freebsd_package.rb +35 -0
  147. data/lib/chef/resource/log.rb +62 -0
  148. data/lib/chef/resource/mdadm.rb +82 -0
  149. data/lib/chef/resource/pacman_package.rb +33 -0
  150. data/lib/chef/resource/ruby_block.rb +21 -2
  151. data/lib/chef/resource/scm.rb +8 -0
  152. data/lib/chef/resource/subversion.rb +1 -0
  153. data/lib/chef/resource/user.rb +5 -2
  154. data/lib/chef/resource/yum_package.rb +36 -0
  155. data/lib/chef/resource_collection.rb +17 -9
  156. data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
  157. data/lib/chef/rest.rb +166 -81
  158. data/lib/chef/role.rb +114 -38
  159. data/lib/chef/run_list.rb +15 -6
  160. data/lib/chef/runner.rb +13 -11
  161. data/lib/chef/search/query.rb +60 -0
  162. data/lib/chef/shef.rb +220 -0
  163. data/lib/chef/shef/ext.rb +297 -0
  164. data/lib/chef/shef/shef_session.rb +175 -0
  165. data/lib/chef/streaming_cookbook_uploader.rb +187 -0
  166. data/lib/chef/tasks/chef_repo.rake +53 -155
  167. data/lib/chef/util/file_edit.rb +94 -96
  168. data/lib/chef/webui_user.rb +233 -0
  169. metadata +219 -63
  170. data/distro/debian/etc/init.d/chef-indexer +0 -175
  171. data/distro/redhat/etc/chef/client.rb +0 -16
  172. data/distro/redhat/etc/chef/indexer.rb +0 -10
  173. data/distro/redhat/etc/chef/server.rb +0 -22
  174. data/distro/redhat/etc/init.d/chef-indexer +0 -76
  175. data/lib/chef/application/indexer.rb +0 -141
  176. data/lib/chef/queue.rb +0 -145
  177. data/lib/chef/search.rb +0 -88
  178. data/lib/chef/search/result.rb +0 -64
  179. data/lib/chef/search_index.rb +0 -77
  180. data/lib/chef/util/fileedit.rb +0 -121
@@ -70,7 +70,6 @@ class Chef
70
70
  end
71
71
 
72
72
  status = popen4(@node[:command][:ps]) do |pid, stdin, stdout, stderr|
73
- stdin.close rescue nil
74
73
  r = Regexp.new(@new_resource.pattern)
75
74
  Chef::Log.debug("#{@new_resource}: attempting to match #{@new_resource.pattern} (#{r}) against process table")
76
75
  stdout.each_line do |line|
@@ -50,14 +50,13 @@ class Chef
50
50
  nil
51
51
  end
52
52
  elsif
53
- Chef::Log.warn "#{@new_resource}: falling back to process table inspection"
53
+ Chef::Log.debug "#{@new_resource}: falling back to process table inspection"
54
54
  if @node[:command][:ps].nil? or @node[:command][:ps].empty?
55
55
  raise Chef::Exceptions::Service, "#{@new_resource}: could not determine how to inspect the process table, please set this nodes 'command.ps' attribute"
56
56
  end
57
57
  status = popen4(@node[:command][:ps]) do |pid, stdin, stdout, stderr|
58
- stdin.close rescue nil
59
58
  r = Regexp.new(@new_resource.pattern)
60
- Chef::Log.warn "#{@new_resource}: attempting to match '#{@new_resource.pattern}' (#{r}) against process list"
59
+ Chef::Log.debug "#{@new_resource}: attempting to match '#{@new_resource.pattern}' (#{r}) against process list"
61
60
  stdout.each_line do |line|
62
61
  if r.match(line)
63
62
  @current_resource.running true
@@ -0,0 +1,191 @@
1
+ #
2
+ # Author:: Bryan McLellan <btm@loftninjas.org>
3
+ # Copyright:: Copyright (c) 2010 Bryan McLellan
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/provider/service'
20
+ require 'chef/provider/service/simple'
21
+ require 'chef/mixin/command'
22
+ require 'chef/util/file_edit'
23
+
24
+ class Chef
25
+ class Provider
26
+ class Service
27
+ class Upstart < Chef::Provider::Service::Simple
28
+
29
+ # Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in.
30
+ # In chef, when we ask a service to start, we expect it to have started before performing the next step
31
+ # since we have top down dependencies. Which is to say we may follow witha resource next that requires
32
+ # that service to be running. According to [2] we can trust that sending a 'goal' such as start will not
33
+ # return until that 'goal' is reached, or some error has occured.
34
+ #
35
+ # [1] http://upstart.ubuntu.com/wiki/JobStates
36
+ # [2] http://www.netsplit.com/2008/04/27/upstart-05-events/
37
+
38
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
39
+ super(node, new_resource, collection, definitions, cookbook_loader)
40
+
41
+ platform, version = Chef::Platform.find_platform_and_version(node)
42
+ case platform
43
+ when "ubuntu"
44
+ case version
45
+ when /8.04/,/8.10/,/9.04/
46
+ @upstart_job_dir = "/etc/event.d"
47
+ @upstart_conf_suffix = ""
48
+ else
49
+ @upstart_job_dir = "/etc/init"
50
+ @upstart_conf_suffix = ".conf"
51
+ end
52
+ else
53
+ @upstart_job_dir = "/etc/init"
54
+ @upstart_conf_suffix = ".conf"
55
+ end
56
+ end
57
+
58
+ def load_current_resource
59
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
60
+ @current_resource.service_name(@new_resource.service_name)
61
+
62
+ # Get running/stopped state
63
+ # We do not support searching for a service via ps when using upstart since status is a native
64
+ # upstart function. We will however support status_command in case someone wants to do something special.
65
+ if @new_resource.status_command
66
+ Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
67
+
68
+ begin
69
+ if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
70
+ @current_resource.running true
71
+ end
72
+ rescue Chef::Exceptions::Exec
73
+ @current_resource.running false
74
+ nil
75
+ end
76
+ else
77
+ begin
78
+ if upstart_state == "running"
79
+ @current_resource.running true
80
+ else
81
+ @current_resource.running false
82
+ end
83
+ rescue Chef::Exceptions::Exec
84
+ @current_resource.running false
85
+ nil
86
+ end
87
+ end
88
+
89
+ # Get enabled/disabled state by reading job configuration file
90
+ if ::File.exists?("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
91
+ Chef::Log.debug("#{@new_resource}: found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
92
+ ::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}",'r') do |file|
93
+ while line = file.gets
94
+ case line
95
+ when /^start on/
96
+ Chef::Log.debug("#{@new_resource}: enabled: #{line.chomp}")
97
+ @current_resource.enabled true
98
+ break
99
+ when /^#start on/
100
+ Chef::Log.debug("#{@new_resource}: disabled: #{line.chomp}")
101
+ @current_resource.enabled false
102
+ break
103
+ end
104
+ end
105
+ end
106
+ else
107
+ Chef::Log.debug("#{@new_resource}: did not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
108
+ @current_resource.enabled false
109
+ end
110
+
111
+ @current_resource
112
+ end
113
+
114
+ def start_service
115
+ # Calling start on a service that is already started will return 1
116
+ # Our 'goal' when we call start is to ensure the service is started
117
+ if @current_resource.running
118
+ Chef::Log.debug("#{@new_resource}: Already running, not starting")
119
+ else
120
+ if @new_resource.start_command
121
+ super
122
+ else
123
+ run_command_with_systems_locale(:command => "/sbin/start #{@new_resource.service_name}")
124
+ end
125
+ end
126
+ end
127
+
128
+ def stop_service
129
+ # Calling stop on a service that is already stopped will return 1
130
+ # Our 'goal' when we call stop is to ensure the service is stopped
131
+ unless @current_resource.running
132
+ Chef::Log.debug("#{@new_resource}: Not running, not stopping")
133
+ else
134
+ if @new_resource.stop_command
135
+ super
136
+ else
137
+ run_command_with_systems_locale(:command => "/sbin/stop #{@new_resource.service_name}")
138
+ end
139
+ end
140
+ end
141
+
142
+ def restart_service
143
+ if @new_resource.restart_command
144
+ super
145
+ else
146
+ run_command_with_systems_locale(:command => "/sbin/restart #{@new_resource.service_name}")
147
+ end
148
+ end
149
+
150
+ def reload_service
151
+ if @new_resource.reload_command
152
+ super
153
+ else
154
+ # upstart >= 0.6.3-4 supports reload (HUP)
155
+ run_command_with_systems_locale(:command => "/sbin/reload #{@new_resource.service_name}")
156
+ end
157
+ end
158
+
159
+ # https://bugs.launchpad.net/upstart/+bug/94065
160
+
161
+ def enable_service
162
+ Chef::Log.warn("#{@new_resource}: upstart lacks inherent support for enabling services, editing job config file")
163
+ conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
164
+ conf.search_file_replace(/^#start on/, "start on")
165
+ conf.write_file
166
+ end
167
+
168
+ def disable_service
169
+ Chef::Log.warn("#{@new_resource}: upstart lacks inherent support for disabling services, editing job config file")
170
+ conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
171
+ conf.search_file_replace(/^start on/, "#start on")
172
+ conf.write_file
173
+ end
174
+
175
+ def upstart_state
176
+ command = "/sbin/status #{@new_resource.service_name}"
177
+ status = popen4(command) do |pid, stdin, stdout, stderr|
178
+ stdout.each do |line|
179
+ # rsyslog stop/waiting
180
+ # service goal/state
181
+ line =~ /\w+ (\w+)\/(\w+)/
182
+ data = Regexp.last_match
183
+ return data[2]
184
+ end
185
+ end
186
+ end
187
+
188
+ end
189
+ end
190
+ end
191
+ end
@@ -45,6 +45,11 @@ class Chef
45
45
  @new_resource.updated = true
46
46
  end
47
47
 
48
+ def action_force_export
49
+ run_command(run_options(:command => export_command(:force => true)))
50
+ @new_resource.updated = true
51
+ end
52
+
48
53
  def action_sync
49
54
  if !::File.exist?(@new_resource.destination + "/.svn") || ::Dir.entries(@new_resource.destination) == ['.','..']
50
55
  action_checkout
@@ -65,10 +70,12 @@ class Chef
65
70
  "-r#{revision_int}", @new_resource.repository, @new_resource.destination
66
71
  end
67
72
 
68
- def export_command
73
+ def export_command(opts={})
69
74
  Chef::Log.info "exporting #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}"
70
- scm :export, @new_resource.svn_arguments, verbose, authentication,
71
- "-r#{revision_int}", @new_resource.repository, @new_resource.destination
75
+ args = opts[:force] ? ["--force"] : []
76
+ args << @new_resource.svn_arguments << verbose << authentication <<
77
+ "-r#{revision_int}" << @new_resource.repository << @new_resource.destination
78
+ scm :export, *args
72
79
  end
73
80
 
74
81
  # If the specified revision isn't an integer ("HEAD" for example), look
@@ -102,6 +109,7 @@ class Chef
102
109
 
103
110
  def run_options(run_opts={})
104
111
  run_opts[:user] = @new_resource.user if @new_resource.user
112
+ run_opts[:group] = @new_resource.group if @new_resource.group
105
113
  run_opts
106
114
  end
107
115
 
@@ -122,7 +130,7 @@ class Chef
122
130
  # YAML doesn't appreciate input like "svn: '/tmp/deploydir' is not a working copy\n"
123
131
  return nil
124
132
  end
125
- raise "tried to run `#{command}' and got unexpected result #{result.inspect}" unless repo_attrs.kind_of?(Hash)
133
+ raise "Could not parse `svn info` data: #{svn_info}" unless repo_attrs.kind_of?(Hash)
126
134
  rev = (repo_attrs['Last Changed Rev'] || repo_attrs['Revision']).to_s
127
135
  Chef::Log.debug "Resolved revision #{@new_resource.revision} to #{rev}"
128
136
  rev
@@ -27,6 +27,7 @@ require 'tempfile'
27
27
 
28
28
  class Chef
29
29
  class Provider
30
+
30
31
  class Template < Chef::Provider::File
31
32
 
32
33
  include Chef::Mixin::Checksum
@@ -34,74 +35,25 @@ class Chef
34
35
  include Chef::Mixin::FindPreferredFile
35
36
 
36
37
  def action_create
37
- Chef::Log.debug(@node.run_state.inspect)
38
38
  raw_template_file = nil
39
39
 
40
- cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
41
-
42
40
  Chef::Log.debug("looking for template #{@new_resource.source} in cookbook #{cookbook_name.inspect}")
43
41
 
44
42
  cache_file_name = "cookbooks/#{cookbook_name}/templates/default/#{@new_resource.source}"
45
43
  template_cache_name = "#{cookbook_name}_#{@new_resource.source}"
46
44
 
47
45
  if Chef::Config[:solo]
48
- filename = find_preferred_file(
49
- cookbook_name,
50
- :template,
51
- @new_resource.source,
52
- @node[:fqdn],
53
- @node[:platform],
54
- @node[:platform_version]
55
- )
56
- Chef::Log.debug("Using local file for template:#{filename}")
57
- cache_file_name = Pathname.new(filename).relative_path_from(Pathname.new(Chef::Config[:file_cache_path])).to_s
58
- elsif @node.run_state[:template_cache].has_key?(template_cache_name)
59
- Chef::Log.debug("I have already fetched the template for #{@new_resource} once this run, not checking again.")
60
- template_updated = false
46
+ cache_file_name = solo_cache_file_name
61
47
  else
62
- r = Chef::REST.new(Chef::Config[:template_url])
63
-
64
- current_checksum = nil
65
-
66
- if Chef::FileCache.has_key?(cache_file_name)
67
- current_checksum = self.checksum(Chef::FileCache.load(cache_file_name, false))
68
- else
69
- Chef::Log.debug("Template #{@new_resource} is not in the template cache")
70
- end
71
-
72
- template_url = generate_url(
73
- @new_resource.source,
74
- "templates",
75
- {
76
- :checksum => current_checksum
77
- }
78
- )
79
-
80
- template_updated = true
81
- begin
82
- raw_template_file = r.get_rest(template_url, true)
83
- rescue Net::HTTPRetriableError => e
84
- if e.response.kind_of?(Net::HTTPNotModified)
85
- template_updated = false
86
- Chef::Log.debug("Cached template for #{@new_resource} is unchanged")
87
- else
88
- raise e
89
- end
90
- end
91
-
92
- # We have checked the cache for this template this run
93
- @node.run_state[:template_cache][template_cache_name] = true
48
+ raw_template_file = fetch_template_via_rest(cache_file_name, template_cache_name)
94
49
  end
95
50
 
96
- if template_updated
51
+ if template_updated?
97
52
  Chef::Log.debug("Updating template for #{@new_resource} in the cache")
98
53
  Chef::FileCache.move_to(raw_template_file.path, cache_file_name)
99
54
  end
100
55
 
101
- context = {}
102
- context.merge!(@new_resource.variables)
103
- context[:node] = @node
104
- template_file = render_template(Chef::FileCache.load(cache_file_name), context)
56
+ template_file = render_with_context(cache_file_name)
105
57
 
106
58
  update = false
107
59
 
@@ -138,6 +90,86 @@ class Chef
138
90
  end
139
91
  end
140
92
 
93
+ private
94
+
95
+ def template_updated
96
+ @template_updated = true
97
+ end
98
+
99
+ def template_not_updated
100
+ @template_updated = false
101
+ end
102
+
103
+ def template_updated?
104
+ @template_updated
105
+ end
106
+
107
+ def cookbook_name
108
+ @cookbook_name = (@new_resource.cookbook || @new_resource.cookbook_name)
109
+ end
110
+
111
+ def render_with_context(cache_file_name)
112
+ context = {}
113
+ context.merge!(@new_resource.variables)
114
+ context[:node] = @node
115
+ render_template(Chef::FileCache.load(cache_file_name), context)
116
+ end
117
+
118
+ def solo_cache_file_name
119
+ filename = find_preferred_file(
120
+ cookbook_name,
121
+ :template,
122
+ @new_resource.source,
123
+ @node[:fqdn],
124
+ @node[:platform],
125
+ @node[:platform_version]
126
+ )
127
+ Chef::Log.debug("Using local file for template:#{filename}")
128
+ Pathname.new(filename).relative_path_from(Pathname.new(Chef::Config[:file_cache_path])).to_s
129
+ end
130
+
131
+ def fetch_template_via_rest(cache_file_name, template_cache_name)
132
+ if @node.run_state[:template_cache].has_key?(template_cache_name)
133
+ Chef::Log.debug("I have already fetched the template for #{@new_resource} once this run, not checking again.")
134
+ template_not_updated
135
+ return false
136
+ end
137
+
138
+ r = Chef::REST.new(Chef::Config[:template_url])
139
+
140
+ current_checksum = nil
141
+
142
+ if Chef::FileCache.has_key?(cache_file_name)
143
+ current_checksum = self.checksum(Chef::FileCache.load(cache_file_name, false))
144
+ else
145
+ Chef::Log.debug("Template #{@new_resource} is not in the template cache")
146
+ end
147
+
148
+ template_url = generate_url(
149
+ @new_resource.source,
150
+ "templates",
151
+ {
152
+ :checksum => current_checksum
153
+ }
154
+ )
155
+
156
+ begin
157
+ raw_template_file = r.get_rest(template_url, true)
158
+ template_updated
159
+ rescue Net::HTTPRetriableError => e
160
+ if e.response.kind_of?(Net::HTTPNotModified)
161
+ Chef::Log.debug("Cached template for #{@new_resource} is unchanged")
162
+ else
163
+ raise e
164
+ end
165
+ end
166
+
167
+ # We have checked the cache for this template this run
168
+ @node.run_state[:template_cache][template_cache_name] = true
169
+
170
+ raw_template_file
171
+ end
172
+
141
173
  end
142
174
  end
143
175
  end
@@ -37,7 +37,7 @@ class Chef
37
37
 
38
38
  def convert_group_name
39
39
  if @new_resource.gid.is_a? String
40
- @new_resource.gid Etc.getgrnam(@new_resource.gid).gid
40
+ @new_resource.gid(Etc.getgrnam(@new_resource.gid).gid)
41
41
  end
42
42
  rescue ArgumentError => e
43
43
  raise Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{@new_resource.gid}"
@@ -0,0 +1,277 @@
1
+ #
2
+ # Author:: Dreamcat4 (<dreamcat4@gmail.com>)
3
+ # Copyright:: Copyright (c) 2009 OpsCode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/provider/user'
20
+ require 'openssl'
21
+
22
+ class Chef
23
+ class Provider
24
+ class User
25
+ class Dscl < Chef::Provider::User
26
+
27
+ def dscl(*args)
28
+ host = "."
29
+ stdout_result = ""; stderr_result = ""; cmd = "dscl #{host} -#{args.join(' ')}"
30
+ status = popen4(cmd) do |pid, stdin, stdout, stderr|
31
+ stdout.each { |line| stdout_result << line }
32
+ stderr.each { |line| stderr_result << line }
33
+ end
34
+ return [cmd, status, stdout_result, stderr_result]
35
+ end
36
+
37
+ def safe_dscl(*args)
38
+ result = dscl(*args)
39
+ return "" if ( args.first =~ /^delete/ ) && ( result[1].exitstatus != 0 )
40
+ raise(Chef::Exceptions::User,"dscl error: #{result.inspect}") unless result[1].exitstatus == 0
41
+ raise(Chef::Exceptions::User,"dscl error: #{result.inspect}") if result[2] =~ /No such key: /
42
+ return result[2]
43
+ end
44
+
45
+ # This is handled in providers/group.rb by Etc.getgrnam()
46
+ # def user_exists?(user)
47
+ # users = safe_dscl("list /Users")
48
+ # !! ( users =~ Regexp.new("\n#{user}\n") )
49
+ # end
50
+
51
+ # get a free UID greater than 200
52
+ def get_free_uid(search_limit=1000)
53
+ uid = nil; next_uid_guess = 200
54
+ users_uids = safe_dscl("list /Users uid")
55
+ while(next_uid_guess < search_limit + 200)
56
+ if users_uids =~ Regexp.new("#{next_uid_guess}\n")
57
+ next_uid_guess += 1
58
+ else
59
+ uid = next_uid_guess
60
+ break
61
+ end
62
+ end
63
+ return uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
64
+ end
65
+
66
+ def uid_used?(uid)
67
+ return false unless uid
68
+ users_uids = safe_dscl("list /Users uid")
69
+ !! ( users_uids =~ Regexp.new("#{uid}\n") )
70
+ end
71
+
72
+ def set_uid
73
+ @new_resource.uid(get_free_uid) if [nil,""].include? @new_resource.uid
74
+ raise(Chef::Exceptions::User,"uid is already in use") if uid_used?(@new_resource.uid)
75
+ safe_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
76
+ end
77
+
78
+ def modify_home
79
+ return safe_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory") if (@new_resource.home.nil? || @new_resource.home.empty?)
80
+ if @new_resource.supports[:manage_home]
81
+ unless @new_resource.home =~ /^\//
82
+ raise(Chef::Exceptions::User,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'")
83
+ end
84
+
85
+ ch_eq_nh = ( @current_resource.home == @new_resource.home )
86
+ cur_home_exists = ::File.exists?("#{@current_resource.home}")
87
+ new_home_exists = ::File.exists?("#{@new_resource.home}")
88
+ ditto = false
89
+ move = false
90
+
91
+ if ch_eq_nh
92
+ if !new_home_exists
93
+ ditto = true
94
+ end
95
+ else
96
+ if !cur_home_exists
97
+ if !new_home_exists
98
+ ditto = true
99
+ end
100
+ elsif cur_home_exists
101
+ move = true
102
+ end
103
+ end
104
+
105
+ if ditto
106
+ skel = "/System/Library/User Template/English.lproj"
107
+ raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
108
+ run_command(:command => "ditto '#{skel}' '#{@new_resource.home}'")
109
+ ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
110
+ end
111
+
112
+ if move
113
+ src = @current_resource.home
114
+ FileUtils.mkdir_p(@new_resource.home)
115
+ files = ::Dir.glob("#{src}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
116
+ ::FileUtils.mv(files,@new_resource.home, :force => true)
117
+ ::FileUtils.rmdir(src)
118
+ ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
119
+ end
120
+ end
121
+ safe_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
122
+ end
123
+
124
+ def osx_shadow_hash?(string)
125
+ return !! ( string =~ /^[[:xdigit:]]{1240}$/ )
126
+ end
127
+
128
+ def osx_salted_sha1?(string)
129
+ return !! ( string =~ /^[[:xdigit:]]{48}$/ )
130
+ end
131
+
132
+ def guid
133
+ safe_dscl("read /Users/#{@new_resource.username} GeneratedUID").gsub(/GeneratedUID: /,"").gsub!(/\n/,"")
134
+ end
135
+
136
+ def shadow_hash_set?
137
+ if safe_dscl("read /Users/#{@new_resource.username}") =~ /AuthenticationAuthority: /
138
+ auth_auth = safe_dscl("read /Users/#{@new_resource.username} AuthenticationAuthority")
139
+ return !! ( auth_auth =~ /ShadowHash/ )
140
+ end
141
+ return false
142
+ end
143
+
144
+ def modify_password
145
+ if @new_resource.password
146
+ shadow_hash = nil
147
+
148
+ Chef::Log.debug("#{new_resource}: updating password")
149
+ if osx_shadow_hash?(@new_resource.password)
150
+ shadow_hash = @new_resource.password.upcase
151
+ else
152
+ salted_sha1 = nil
153
+ if osx_salted_sha1?(@new_resource.password)
154
+ salted_sha1 = @new_resource.password.upcase
155
+ else
156
+ hex_salt = ""; chars = ("0".."9").to_a + ("a".."f").to_a
157
+ 1.upto(8) { |i| hex_salt << chars[::Kernel.rand(chars.size-1)] }
158
+ salt = [hex_salt].pack("H*")
159
+ sha1 = ::OpenSSL::Digest::SHA1.hexdigest(salt+@new_resource.password)
160
+ salted_sha1 = (hex_salt+sha1).upcase
161
+ end
162
+ shadow_hash = String.new("00000000"*155)
163
+ shadow_hash[168] = salted_sha1
164
+ end
165
+
166
+ ::File.open("/var/db/shadow/hash/#{guid}",'w',0600) do |output|
167
+ output.puts shadow_hash
168
+ end
169
+
170
+ unless shadow_hash_set?
171
+ safe_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';ShadowHash;'")
172
+ end
173
+ end
174
+ end
175
+
176
+ def load_current_resource
177
+ super
178
+ raise Chef::Exceptions::User, "Could not find binary /usr/bin/dscl for #{@new_resource}" unless ::File.exists?("/usr/bin/dscl")
179
+ end
180
+
181
+ def create_user
182
+ manage_user(false)
183
+ end
184
+
185
+ def manage_user(manage = true)
186
+ fields = []
187
+ if manage
188
+ [:username,:comment,:uid,:gid,:home,:shell,:password].each do |field|
189
+ if @current_resource.send(field) != @new_resource.send(field)
190
+ fields << field if @new_resource.send(field)
191
+ end
192
+ end
193
+ if @new_resource.send(:supports)[:manage_home]
194
+ fields << :home if @new_resource.send(:home)
195
+ end
196
+ fields << :shell if fields.include?(:password)
197
+ else
198
+ # create
199
+ fields = [:username,:comment,:uid,:gid,:home,:shell,:password]
200
+ end
201
+ fields.uniq!
202
+ fields.each do |field|
203
+ case field
204
+ when :username
205
+ safe_dscl("create /Users/#{@new_resource.username}")
206
+
207
+ when :comment
208
+ safe_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
209
+
210
+ when :uid
211
+ set_uid
212
+
213
+ when :gid
214
+ safe_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
215
+
216
+ when :home
217
+ modify_home
218
+
219
+ when :shell
220
+ if @new_resource.password || ::File.exists?("#{@new_resource.shell}")
221
+ safe_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
222
+ else
223
+ safe_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
224
+ end
225
+
226
+ when :password
227
+ modify_password
228
+ end
229
+ end
230
+ end
231
+
232
+ def remove_user
233
+ if @new_resource.supports[:manage_home]
234
+ # remove home directory
235
+ if safe_dscl("read /Users/#{@new_resource.username}") =~ /NFSHomeDirectory/
236
+ nfs_home = safe_dscl("read /Users/#{@new_resource.username} NFSHomeDirectory")
237
+ nfs_home.gsub!(/NFSHomeDirectory: /,"").gsub!(/\n$/,"")
238
+ FileUtils.rm_rf(nfs_home)
239
+ end
240
+ end
241
+ # remove the user from its groups
242
+ groups = []
243
+ Etc.group do |group|
244
+ groups << group.name if group.mem.include?(@new_resource.username)
245
+ end
246
+ groups.each do |group_name|
247
+ safe_dscl("delete /Groups/#{group_name} GroupMembership '#{@new_resource.username}'")
248
+ end
249
+ # remove user account
250
+ safe_dscl("delete /Users/#{@new_resource.username}")
251
+ end
252
+
253
+ def locked?
254
+ if safe_dscl("read /Users/#{@new_resource.username}") =~ /AuthenticationAuthority: /
255
+ auth_auth = safe_dscl("read /Users/#{@new_resource.username} AuthenticationAuthority")
256
+ return !! ( auth_auth =~ /DisabledUser/ )
257
+ end
258
+ return false
259
+ end
260
+
261
+ def check_lock
262
+ return @locked = locked?
263
+ end
264
+
265
+ def lock_user
266
+ safe_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
267
+ end
268
+
269
+ def unlock_user
270
+ auth_auth = safe_dscl("read /Users/#{@new_resource.username} AuthenticationAuthority")
271
+ auth_auth.gsub!(/AuthenticationAuthority: /,"").gsub!(/DisabledUser/,"").gsub!(/[; ]*$/,"")
272
+ safe_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_auth}'")
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end