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.
- data/README.rdoc +11 -10
- data/bin/chef-client +2 -2
- data/bin/chef-solo +1 -1
- data/bin/knife +27 -0
- data/bin/shef +49 -0
- data/distro/README +2 -0
- data/distro/{debian → common}/man/man1/chef-indexer.1 +0 -0
- data/distro/{debian → common}/man/man1/chef-server.1 +0 -0
- data/distro/{debian → common}/man/man8/chef-client.8 +0 -0
- data/distro/{debian → common}/man/man8/chef-solo.8 +0 -0
- data/distro/common/man/man8/knife.8 +375 -0
- data/distro/redhat/etc/init.d/chef-client +8 -4
- data/distro/redhat/etc/init.d/chef-server +16 -15
- data/distro/redhat/etc/init.d/chef-server-webui +78 -0
- data/distro/redhat/etc/init.d/chef-solr +76 -0
- data/distro/redhat/etc/init.d/chef-solr-indexer +75 -0
- data/distro/redhat/etc/sysconfig/chef-client +10 -0
- data/distro/redhat/etc/sysconfig/chef-server +10 -0
- data/distro/redhat/etc/sysconfig/chef-server-webui +10 -0
- data/distro/redhat/etc/sysconfig/chef-solr +9 -0
- data/distro/redhat/etc/sysconfig/chef-solr-indexer +7 -0
- data/distro/suse/etc/init.d/chef-client +121 -0
- data/lib/chef.rb +1 -1
- data/lib/chef/api_client.rb +263 -0
- data/lib/chef/application.rb +1 -1
- data/lib/chef/application/client.rb +21 -3
- data/lib/chef/application/knife.rb +144 -0
- data/lib/chef/application/server.rb +2 -1
- data/lib/chef/application/solo.rb +9 -2
- data/lib/chef/cache.rb +61 -0
- data/lib/chef/cache/checksum.rb +70 -0
- data/lib/chef/certificate.rb +154 -0
- data/lib/chef/client.rb +123 -220
- data/lib/chef/compile.rb +9 -21
- data/lib/chef/config.rb +67 -10
- data/lib/chef/cookbook.rb +49 -22
- data/lib/chef/cookbook/metadata.rb +85 -5
- data/lib/chef/cookbook_loader.rb +4 -4
- data/lib/chef/couchdb.rb +99 -30
- data/lib/chef/daemon.rb +1 -1
- data/lib/chef/data_bag.rb +215 -0
- data/lib/chef/data_bag_item.rb +219 -0
- data/lib/chef/exceptions.rb +3 -0
- data/lib/chef/index_queue.rb +29 -0
- data/lib/chef/index_queue/amqp_client.rb +106 -0
- data/lib/chef/index_queue/consumer.rb +76 -0
- data/lib/chef/index_queue/indexable.rb +74 -0
- data/lib/chef/knife.rb +309 -0
- data/lib/chef/knife/client_bulk_delete.rb +40 -0
- data/lib/chef/knife/client_create.rb +62 -0
- data/lib/chef/knife/client_delete.rb +37 -0
- data/lib/chef/knife/client_edit.rb +37 -0
- data/lib/chef/knife/client_list.rb +40 -0
- data/lib/chef/knife/client_reregister.rb +48 -0
- data/lib/chef/knife/client_show.rb +42 -0
- data/lib/chef/knife/configure.rb +123 -0
- data/lib/chef/knife/cookbook_bulk_delete.rb +46 -0
- data/lib/chef/knife/cookbook_delete.rb +41 -0
- data/lib/chef/knife/cookbook_download.rb +57 -0
- data/lib/chef/knife/cookbook_list.rb +41 -0
- data/lib/chef/knife/cookbook_metadata.rb +87 -0
- data/lib/chef/knife/cookbook_show.rb +75 -0
- data/lib/chef/knife/cookbook_upload.rb +179 -0
- data/lib/chef/knife/data_bag_create.rb +43 -0
- data/lib/chef/knife/data_bag_delete.rb +43 -0
- data/lib/chef/knife/data_bag_edit.rb +49 -0
- data/lib/chef/knife/data_bag_list.rb +42 -0
- data/lib/chef/knife/data_bag_show.rb +40 -0
- data/lib/chef/knife/ec2_instance_data.rb +46 -0
- data/lib/chef/knife/index_rebuild.rb +51 -0
- data/lib/chef/knife/node_bulk_delete.rb +43 -0
- data/lib/chef/knife/node_create.rb +39 -0
- data/lib/chef/knife/node_delete.rb +36 -0
- data/lib/chef/knife/node_edit.rb +36 -0
- data/lib/chef/knife/node_from_file.rb +42 -0
- data/lib/chef/knife/node_list.rb +41 -0
- data/lib/chef/knife/node_run_list_add.rb +64 -0
- data/lib/chef/knife/node_run_list_remove.rb +45 -0
- data/lib/chef/knife/node_show.rb +46 -0
- data/lib/chef/knife/role_bulk_delete.rb +44 -0
- data/lib/chef/knife/role_create.rb +44 -0
- data/lib/chef/knife/role_delete.rb +36 -0
- data/lib/chef/knife/role_edit.rb +37 -0
- data/lib/chef/knife/role_from_file.rb +46 -0
- data/lib/chef/knife/role_list.rb +40 -0
- data/lib/chef/knife/role_show.rb +43 -0
- data/lib/chef/knife/search.rb +94 -0
- data/lib/chef/knife/ssh.rb +170 -0
- data/lib/chef/log.rb +30 -8
- data/lib/chef/mixin/checksum.rb +2 -7
- data/lib/chef/mixin/command.rb +32 -13
- data/lib/chef/mixin/convert_to_class_name.rb +15 -0
- data/lib/chef/mixin/deep_merge.rb +199 -11
- data/lib/chef/mixin/generate_url.rb +18 -9
- data/lib/chef/mixin/language.rb +29 -1
- data/lib/chef/mixin/language_include_attribute.rb +56 -0
- data/lib/chef/mixin/language_include_recipe.rb +53 -0
- data/lib/chef/mixin/params_validate.rb +25 -12
- data/lib/chef/mixin/recipe_definition_dsl_core.rb +2 -0
- data/lib/chef/mixin/template.rb +11 -1
- data/lib/chef/mixin/xml_escape.rb +87 -0
- data/lib/chef/node.rb +144 -122
- data/lib/chef/openid_registration.rb +12 -5
- data/lib/chef/platform.rb +89 -47
- data/lib/chef/provider/breakpoint.rb +36 -0
- data/lib/chef/provider/cron.rb +5 -6
- data/lib/chef/provider/deploy.rb +43 -10
- data/lib/chef/provider/deploy/revision.rb +2 -3
- data/lib/chef/provider/erl_call.rb +72 -0
- data/lib/chef/provider/file.rb +8 -4
- data/lib/chef/provider/git.rb +10 -5
- data/lib/chef/provider/group/dscl.rb +128 -0
- data/lib/chef/provider/http_request.rb +6 -2
- data/lib/chef/provider/ifconfig.rb +1 -0
- data/lib/chef/provider/link.rb +1 -1
- data/lib/chef/provider/log.rb +53 -0
- data/lib/chef/provider/mdadm.rb +88 -0
- data/lib/chef/provider/mount/mount.rb +1 -1
- data/lib/chef/provider/package.rb +1 -1
- data/lib/chef/provider/package/easy_install.rb +106 -0
- data/lib/chef/provider/package/pacman.rb +101 -0
- data/lib/chef/provider/package/portage.rb +1 -1
- data/lib/chef/provider/package/rpm.rb +10 -8
- data/lib/chef/provider/package/yum-dump.py +22 -3
- data/lib/chef/provider/package/yum.rb +32 -8
- data/lib/chef/provider/package/zypper.rb +132 -0
- data/lib/chef/provider/remote_directory.rb +58 -49
- data/lib/chef/provider/remote_file.rb +1 -1
- data/lib/chef/provider/route.rb +136 -80
- data/lib/chef/provider/ruby_block.rb +18 -1
- data/lib/chef/provider/service/arch.rb +109 -0
- data/lib/chef/provider/service/freebsd.rb +0 -1
- data/lib/chef/provider/service/simple.rb +2 -3
- data/lib/chef/provider/service/upstart.rb +191 -0
- data/lib/chef/provider/subversion.rb +12 -4
- data/lib/chef/provider/template.rb +85 -53
- data/lib/chef/provider/user.rb +1 -1
- data/lib/chef/provider/user/dscl.rb +277 -0
- data/lib/chef/provider/user/useradd.rb +1 -0
- data/lib/chef/recipe.rb +2 -41
- data/lib/chef/resource.rb +9 -3
- data/lib/chef/resource/breakpoint.rb +35 -0
- data/lib/chef/resource/deploy.rb +16 -2
- data/lib/chef/resource/easy_install_package.rb +41 -0
- data/lib/chef/resource/erl_call.rb +83 -0
- data/lib/chef/resource/freebsd_package.rb +35 -0
- data/lib/chef/resource/log.rb +62 -0
- data/lib/chef/resource/mdadm.rb +82 -0
- data/lib/chef/resource/pacman_package.rb +33 -0
- data/lib/chef/resource/ruby_block.rb +21 -2
- data/lib/chef/resource/scm.rb +8 -0
- data/lib/chef/resource/subversion.rb +1 -0
- data/lib/chef/resource/user.rb +5 -2
- data/lib/chef/resource/yum_package.rb +36 -0
- data/lib/chef/resource_collection.rb +17 -9
- data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
- data/lib/chef/rest.rb +166 -81
- data/lib/chef/role.rb +114 -38
- data/lib/chef/run_list.rb +15 -6
- data/lib/chef/runner.rb +13 -11
- data/lib/chef/search/query.rb +60 -0
- data/lib/chef/shef.rb +220 -0
- data/lib/chef/shef/ext.rb +297 -0
- data/lib/chef/shef/shef_session.rb +175 -0
- data/lib/chef/streaming_cookbook_uploader.rb +187 -0
- data/lib/chef/tasks/chef_repo.rake +53 -155
- data/lib/chef/util/file_edit.rb +94 -96
- data/lib/chef/webui_user.rb +233 -0
- metadata +219 -63
- data/distro/debian/etc/init.d/chef-indexer +0 -175
- data/distro/redhat/etc/chef/client.rb +0 -16
- data/distro/redhat/etc/chef/indexer.rb +0 -10
- data/distro/redhat/etc/chef/server.rb +0 -22
- data/distro/redhat/etc/init.d/chef-indexer +0 -76
- data/lib/chef/application/indexer.rb +0 -141
- data/lib/chef/queue.rb +0 -145
- data/lib/chef/search.rb +0 -88
- data/lib/chef/search/result.rb +0 -64
- data/lib/chef/search_index.rb +0 -77
- 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.
|
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.
|
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
|
-
|
71
|
-
|
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 "
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/chef/provider/user.rb
CHANGED
@@ -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
|
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
|