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
@@ -20,7 +20,7 @@ require 'chef/client'
|
|
20
20
|
require 'chef/config'
|
21
21
|
require 'chef/daemon'
|
22
22
|
require 'chef/log'
|
23
|
-
require '
|
23
|
+
require 'chef/rest'
|
24
24
|
require 'open-uri'
|
25
25
|
require 'fileutils'
|
26
26
|
|
@@ -132,7 +132,13 @@ class Chef::Application::Solo < Chef::Application
|
|
132
132
|
|
133
133
|
if Chef::Config[:json_attribs]
|
134
134
|
begin
|
135
|
-
|
135
|
+
json_io = case Chef::Config[:json_attribs]
|
136
|
+
when /^(http|https):\/\//
|
137
|
+
@rest = Chef::REST.new(Chef::Config[:json_attribs], nil, nil)
|
138
|
+
@rest.get_rest(Chef::Config[:json_attribs], true).open
|
139
|
+
else
|
140
|
+
open(Chef::Config[:json_attribs])
|
141
|
+
end
|
136
142
|
rescue SocketError => error
|
137
143
|
Chef::Application.fatal!("I cannot connect to #{Chef::Config[:json_attribs]}", 2)
|
138
144
|
rescue Errno::ENOENT => error
|
@@ -145,6 +151,7 @@ class Chef::Application::Solo < Chef::Application
|
|
145
151
|
|
146
152
|
begin
|
147
153
|
@chef_solo_json = JSON.parse(json_io.read)
|
154
|
+
json_io.close unless json_io.closed?
|
148
155
|
rescue JSON::ParserError => error
|
149
156
|
Chef::Application.fatal!("Could not parse the provided JSON file (#{Chef::Config[:json_attribs]})!: " + error.message, 2)
|
150
157
|
end
|
data/lib/chef/cache.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Author:: Daniel DeLeo (<dan@kallistec.com>)
|
4
|
+
# Copyright:: Copyright (c) 2009 Opscode, Inc.
|
5
|
+
# Copyright:: Copyright (c) 2009 Daniel DeLeo
|
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 'chef/log'
|
22
|
+
require 'chef/config'
|
23
|
+
require 'chef/mixin/convert_to_class_name'
|
24
|
+
require 'singleton'
|
25
|
+
require 'moneta'
|
26
|
+
|
27
|
+
class Chef
|
28
|
+
class Cache
|
29
|
+
include Chef::Mixin::ConvertToClassName
|
30
|
+
include ::Singleton
|
31
|
+
|
32
|
+
attr_reader :moneta
|
33
|
+
|
34
|
+
def initialize(*args)
|
35
|
+
self.reset!(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def reset!(backend=nil, options=nil)
|
39
|
+
backend ||= Chef::Config[:cache_type]
|
40
|
+
options ||= Chef::Config[:cache_options]
|
41
|
+
|
42
|
+
begin
|
43
|
+
require "moneta/#{convert_to_snake_case(backend, 'Moneta')}"
|
44
|
+
rescue LoadError => e
|
45
|
+
Chef::Log.fatal("Could not load Moneta back end #{backend.inspect}")
|
46
|
+
raise e
|
47
|
+
end
|
48
|
+
|
49
|
+
@moneta = Moneta.const_get(backend).new(options)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module Moneta
|
56
|
+
module Defaults
|
57
|
+
def default
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Author:: Daniel DeLeo (<dan@kallistec.com>)
|
4
|
+
# Copyright:: Copyright (c) 2009 Opscode, Inc.
|
5
|
+
# Copyright:: Copyright (c) 2009 Daniel DeLeo
|
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 'chef/cache'
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Cache
|
25
|
+
class Checksum < Chef::Cache
|
26
|
+
|
27
|
+
def self.checksum_for_file(*args)
|
28
|
+
instance.checksum_for_file(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def checksum_for_file(file)
|
32
|
+
key, fstat = filename_to_key(file), File.stat(file)
|
33
|
+
lookup_checksum(key, fstat) || generate_checksum(key, file, fstat)
|
34
|
+
end
|
35
|
+
|
36
|
+
def lookup_checksum(key, fstat)
|
37
|
+
cached = @moneta.fetch(key)
|
38
|
+
if cached && file_unchanged?(cached, fstat)
|
39
|
+
cached["checksum"]
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_checksum(key, file, fstat)
|
46
|
+
checksum = checksum_file(file)
|
47
|
+
moneta.store(key, {"mtime" => fstat.mtime.to_f, "checksum" => checksum})
|
48
|
+
checksum
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def file_unchanged?(cached, fstat)
|
54
|
+
cached["mtime"].to_f == fstat.mtime.to_f
|
55
|
+
end
|
56
|
+
|
57
|
+
def checksum_file(file)
|
58
|
+
digest = Digest::SHA256.new
|
59
|
+
IO.foreach(file) {|line| digest.update(line) }
|
60
|
+
digest.hexdigest
|
61
|
+
end
|
62
|
+
|
63
|
+
def filename_to_key(file)
|
64
|
+
"chef-file-#{file.gsub(/(#{File::SEPARATOR}|\.)/, '-')}"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
@@ -0,0 +1,154 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Author:: Christopher Brown (<cb@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2009 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
|
+
|
20
|
+
require 'chef/log'
|
21
|
+
require 'chef/config'
|
22
|
+
require 'chef/api_client'
|
23
|
+
require 'openssl'
|
24
|
+
require 'fileutils'
|
25
|
+
|
26
|
+
class Chef
|
27
|
+
class Certificate
|
28
|
+
class << self
|
29
|
+
|
30
|
+
# Generates a new CA Certificate and Key, and writes them out to
|
31
|
+
# Chef::Config[:signing_ca_cert] and Chef::Config[:signing_ca_key].
|
32
|
+
def generate_signing_ca
|
33
|
+
ca_cert_file = Chef::Config[:signing_ca_cert]
|
34
|
+
ca_keypair_file = Chef::Config[:signing_ca_key]
|
35
|
+
|
36
|
+
unless File.exists?(ca_cert_file) && File.exists?(ca_keypair_file)
|
37
|
+
Chef::Log.info("Creating new signing certificate")
|
38
|
+
|
39
|
+
[ ca_cert_file, ca_keypair_file ].each do |f|
|
40
|
+
ca_basedir = File.dirname(f)
|
41
|
+
FileUtils.mkdir_p ca_basedir
|
42
|
+
end
|
43
|
+
|
44
|
+
keypair = OpenSSL::PKey::RSA.generate(1024)
|
45
|
+
|
46
|
+
ca_cert = OpenSSL::X509::Certificate.new
|
47
|
+
ca_cert.version = 3
|
48
|
+
ca_cert.serial = 1
|
49
|
+
info = [
|
50
|
+
["C", Chef::Config[:signing_ca_country]],
|
51
|
+
["ST", Chef::Config[:signing_ca_state]],
|
52
|
+
["L", Chef::Config[:signing_ca_location]],
|
53
|
+
["O", Chef::Config[:signing_ca_org]],
|
54
|
+
["OU", "Certificate Service"],
|
55
|
+
["CN", "#{Chef::Config[:signing_ca_domain]}/emailAddress=#{Chef::Config[:signing_ca_email]}"]
|
56
|
+
]
|
57
|
+
ca_cert.subject = ca_cert.issuer = OpenSSL::X509::Name.new(info)
|
58
|
+
ca_cert.not_before = Time.now
|
59
|
+
ca_cert.not_after = Time.now + 10 * 365 * 24 * 60 * 60 # 10 years
|
60
|
+
ca_cert.public_key = keypair.public_key
|
61
|
+
|
62
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
63
|
+
ef.subject_certificate = ca_cert
|
64
|
+
ef.issuer_certificate = ca_cert
|
65
|
+
ca_cert.extensions = [
|
66
|
+
ef.create_extension("basicConstraints", "CA:TRUE", true),
|
67
|
+
ef.create_extension("subjectKeyIdentifier", "hash"),
|
68
|
+
ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
|
69
|
+
]
|
70
|
+
ca_cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
|
71
|
+
ca_cert.sign keypair, OpenSSL::Digest::SHA1.new
|
72
|
+
|
73
|
+
File.open(ca_cert_file, "w") { |f| f.write ca_cert.to_pem }
|
74
|
+
File.open(ca_keypair_file, "w") { |f| f.write keypair.to_pem }
|
75
|
+
end
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Creates a new key pair, and signs them with the signing certificate
|
80
|
+
# and key generated from generate_signing_ca above.
|
81
|
+
#
|
82
|
+
# @param [String] The common name for the key pair.
|
83
|
+
# @param [Optional String] The subject alternative name.
|
84
|
+
# @return [Object, Object] The public and private key objects.
|
85
|
+
def gen_keypair(common_name, subject_alternative_name = nil)
|
86
|
+
|
87
|
+
Chef::Log.info("Creating new key pair for #{common_name}")
|
88
|
+
|
89
|
+
# generate client keypair
|
90
|
+
client_keypair = OpenSSL::PKey::RSA.generate(2048)
|
91
|
+
|
92
|
+
client_cert = OpenSSL::X509::Certificate.new
|
93
|
+
|
94
|
+
ca_cert = OpenSSL::X509::Certificate.new(File.read(Chef::Config[:signing_ca_cert]))
|
95
|
+
|
96
|
+
info = [
|
97
|
+
["C", Chef::Config[:signing_ca_country]],
|
98
|
+
["ST", Chef::Config[:signing_ca_state]],
|
99
|
+
["L", Chef::Config[:signing_ca_location]],
|
100
|
+
["O", Chef::Config[:signing_ca_org]],
|
101
|
+
["OU", "Certificate Service"],
|
102
|
+
["CN", common_name ]
|
103
|
+
]
|
104
|
+
|
105
|
+
client_cert.subject = OpenSSL::X509::Name.new(info)
|
106
|
+
client_cert.issuer = ca_cert.subject
|
107
|
+
client_cert.not_before = Time.now
|
108
|
+
client_cert.not_after = Time.now + 10 * 365 * 24 * 60 * 60 # 10 years
|
109
|
+
client_cert.public_key = client_keypair.public_key
|
110
|
+
client_cert.serial = 1
|
111
|
+
client_cert.version = 3
|
112
|
+
|
113
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
114
|
+
ef.subject_certificate = client_cert
|
115
|
+
ef.issuer_certificate = ca_cert
|
116
|
+
|
117
|
+
client_cert.extensions = [
|
118
|
+
ef.create_extension("basicConstraints", "CA:FALSE", true),
|
119
|
+
ef.create_extension("subjectKeyIdentifier", "hash")
|
120
|
+
]
|
121
|
+
client_cert.add_extension ef.create_extension("subjectAltName", subject_alternative_name) if subject_alternative_name
|
122
|
+
|
123
|
+
client_cert.sign(OpenSSL::PKey::RSA.new(File.read(Chef::Config[:signing_ca_key])), OpenSSL::Digest::SHA1.new)
|
124
|
+
|
125
|
+
return client_cert.public_key, client_keypair
|
126
|
+
end
|
127
|
+
|
128
|
+
def gen_validation_key(name=Chef::Config[:validation_client_name], key_file=Chef::Config[:validation_key])
|
129
|
+
# Create the validation key
|
130
|
+
create_key = false
|
131
|
+
begin
|
132
|
+
c = Chef::ApiClient.cdb_load(name)
|
133
|
+
rescue Chef::Exceptions::CouchDBNotFound
|
134
|
+
create_key = true
|
135
|
+
end
|
136
|
+
|
137
|
+
if create_key
|
138
|
+
Chef::Log.info("Creating validation key...")
|
139
|
+
api_client = Chef::ApiClient.new
|
140
|
+
api_client.name(name)
|
141
|
+
api_client.admin(true)
|
142
|
+
api_client.create_keys
|
143
|
+
api_client.cdb_save
|
144
|
+
key_dir = File.dirname(key_file)
|
145
|
+
FileUtils.mkdir_p(key_dir) unless File.directory?(key_dir)
|
146
|
+
File.open(key_file, File::WRONLY|File::EXCL|File::CREAT, 0600) do |f|
|
147
|
+
f.print(api_client.private_key)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/chef/client.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
3
|
# Author:: Christopher Walters (<cw@opscode.com>)
|
4
|
+
# Author:: Christopher Brown (<cb@opscode.com>)
|
4
5
|
# Copyright:: Copyright (c) 2008 Opscode, Inc.
|
5
6
|
# License:: Apache License, Version 2.0
|
6
7
|
#
|
@@ -36,20 +37,25 @@ class Chef
|
|
36
37
|
include Chef::Mixin::GenerateURL
|
37
38
|
include Chef::Mixin::Checksum
|
38
39
|
|
39
|
-
attr_accessor :node, :registration, :
|
40
|
+
attr_accessor :node, :registration, :json_attribs, :validation_token, :node_name, :ohai, :rest
|
40
41
|
|
41
42
|
# Creates a new Chef::Client.
|
42
43
|
def initialize()
|
43
44
|
@node = nil
|
44
|
-
@safe_name = nil
|
45
45
|
@validation_token = nil
|
46
46
|
@registration = nil
|
47
47
|
@json_attribs = nil
|
48
48
|
@node_name = nil
|
49
|
-
@node_exists = true
|
50
|
-
Ohai::Log.logger = Chef::Log.logger
|
49
|
+
@node_exists = true
|
51
50
|
@ohai = Ohai::System.new
|
52
|
-
|
51
|
+
Chef::Log.verbose = Chef::Config[:verbose_logging]
|
52
|
+
Mixlib::Authentication::Log.logger = Ohai::Log.logger = Chef::Log.logger
|
53
|
+
@ohai_has_run = false
|
54
|
+
@rest = if File.exists?(Chef::Config[:client_key])
|
55
|
+
Chef::REST.new(Chef::Config[:chef_server_url])
|
56
|
+
else
|
57
|
+
Chef::REST.new(Chef::Config[:chef_server_url], nil, nil)
|
58
|
+
end
|
53
59
|
end
|
54
60
|
|
55
61
|
# Do a full run for this Chef::Client. Calls:
|
@@ -76,15 +82,9 @@ class Chef
|
|
76
82
|
|
77
83
|
determine_node_name
|
78
84
|
register
|
79
|
-
authenticate
|
80
85
|
build_node(@node_name)
|
81
86
|
save_node
|
82
|
-
|
83
|
-
sync_provider_files
|
84
|
-
sync_resource_files
|
85
|
-
sync_attribute_files
|
86
|
-
sync_definitions
|
87
|
-
sync_recipes
|
87
|
+
sync_cookbooks
|
88
88
|
save_node
|
89
89
|
converge
|
90
90
|
save_node
|
@@ -115,18 +115,22 @@ class Chef
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def run_ohai
|
118
|
-
if
|
119
|
-
|
118
|
+
if ohai.keys
|
119
|
+
ohai.refresh_plugins
|
120
120
|
else
|
121
|
-
|
121
|
+
ohai.all_plugins
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
125
|
def determine_node_name
|
126
|
-
run_ohai
|
127
|
-
unless
|
128
|
-
|
129
|
-
|
126
|
+
run_ohai
|
127
|
+
unless node_name
|
128
|
+
if Chef::Config[:node_name]
|
129
|
+
@node_name = Chef::Config[:node_name]
|
130
|
+
else
|
131
|
+
@node_name ||= ohai[:fqdn] ? ohai[:fqdn] : ohai[:hostname]
|
132
|
+
Chef::Config[:node_name] = @node_name
|
133
|
+
end
|
130
134
|
end
|
131
135
|
@node_name
|
132
136
|
end
|
@@ -142,37 +146,24 @@ class Chef
|
|
142
146
|
def build_node(node_name=nil, solo=false)
|
143
147
|
node_name ||= determine_node_name
|
144
148
|
raise RuntimeError, "Unable to determine node name from ohai" unless node_name
|
145
|
-
Chef::Log.debug("Building node object for #{@
|
149
|
+
Chef::Log.debug("Building node object for #{@node_name}")
|
150
|
+
|
146
151
|
unless solo
|
147
|
-
begin
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
end
|
152
|
+
@node = begin
|
153
|
+
rest.get_rest("nodes/#{@node_name}")
|
154
|
+
rescue Net::HTTPServerException => e
|
155
|
+
raise unless e.message =~ /^404/
|
156
|
+
end
|
154
157
|
end
|
158
|
+
|
155
159
|
unless @node
|
156
160
|
@node_exists = false
|
157
161
|
@node ||= Chef::Node.new
|
158
162
|
@node.name(node_name)
|
159
163
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
if key == "recipes" || key == "run_list"
|
164
|
-
value.each do |recipe|
|
165
|
-
unless @node.recipes.detect { |r| r == recipe }
|
166
|
-
Chef::Log.debug("Adding recipe #{recipe}")
|
167
|
-
@node.recipes << recipe
|
168
|
-
end
|
169
|
-
end
|
170
|
-
else
|
171
|
-
Chef::Log.debug("JSON Attribute: #{key} - #{value.inspect}")
|
172
|
-
@node[key] = value
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
164
|
+
|
165
|
+
@node.consume_attributes(@json_attribs)
|
166
|
+
|
176
167
|
ohai.each do |field, value|
|
177
168
|
Chef::Log.debug("Ohai Attribute: #{field} - #{value.inspect}")
|
178
169
|
@node[field] = value
|
@@ -181,69 +172,21 @@ class Chef
|
|
181
172
|
Chef::Log.debug("Platform is #{platform} version #{version}")
|
182
173
|
@node[:platform] = platform
|
183
174
|
@node[:platform_version] = version
|
184
|
-
@node[:tags] = Array.new unless @node.attribute?(:tags)
|
185
175
|
@node
|
186
176
|
end
|
187
|
-
|
188
|
-
#
|
189
|
-
# data.
|
190
|
-
#
|
191
|
-
# If it has not, we register it by calling create_registration.
|
192
|
-
#
|
177
|
+
|
178
|
+
#
|
193
179
|
# === Returns
|
194
|
-
#
|
180
|
+
# rest<Chef::REST>:: returns Chef::REST connection object
|
195
181
|
def register
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
@secret = Chef::FileCache.load(File.join("registration", @safe_name))
|
202
|
-
end
|
203
|
-
rescue Net::HTTPServerException => e
|
204
|
-
case e.message
|
205
|
-
when /^404/
|
206
|
-
create_registration
|
207
|
-
else
|
208
|
-
raise
|
209
|
-
end
|
210
|
-
rescue Chef::Exceptions::FileNotFound
|
211
|
-
Chef::Application.fatal! "A remote registration already exists for #{@safe_name}, however the local shared secret does not exist." +
|
212
|
-
" To remedy this, you could delete the registration via webUI/REST, change the node_name option in config.rb" +
|
213
|
-
" (or use the -N/--node-name option to the CLI) or" +
|
214
|
-
" copy the old shared secret to #{File.join(Chef::Config[:file_cache_path], 'registration', @safe_name)}", 3
|
182
|
+
if File.exists?(Chef::Config[:client_key])
|
183
|
+
Chef::Log.debug("Client key #{Chef::Config[:client_key]} is present - skipping registration")
|
184
|
+
else
|
185
|
+
Chef::Log.info("Client key #{Chef::Config[:client_key]} is not present - registering")
|
186
|
+
Chef::REST.new(Chef::Config[:client_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key]).register(@node_name, Chef::Config[:client_key])
|
215
187
|
end
|
216
|
-
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
# Generates a random secret, stores it in the Chef::Filestore with the "registration" key,
|
221
|
-
# and posts our nodes registration information to the server.
|
222
|
-
#
|
223
|
-
# === Returns
|
224
|
-
# true:: Always returns true
|
225
|
-
def create_registration
|
226
|
-
@secret = random_password(500)
|
227
|
-
Chef::FileCache.store(File.join("registration", @safe_name), @secret)
|
228
|
-
@rest.post_rest("registrations", { :id => @safe_name, :password => @secret, :validation_token => @validation_token })
|
229
|
-
true
|
230
|
-
end
|
231
|
-
|
232
|
-
# Authenticates the node via OpenID.
|
233
|
-
#
|
234
|
-
# === Returns
|
235
|
-
# true:: Always returns true
|
236
|
-
def authenticate
|
237
|
-
determine_node_name unless @node_name
|
238
|
-
Chef::Log.debug("Authenticating #{@safe_name} via openid")
|
239
|
-
response = @rest.post_rest('openid/consumer/start', {
|
240
|
-
"openid_identifier" => "#{Chef::Config[:openid_url]}/openid/server/node/#{@safe_name}",
|
241
|
-
"submit" => "Verify"
|
242
|
-
})
|
243
|
-
@rest.post_rest(
|
244
|
-
"#{Chef::Config[:openid_url]}#{response["action"]}",
|
245
|
-
{ "password" => @secret }
|
246
|
-
)
|
188
|
+
# We now have the client key, and should use it from now on.
|
189
|
+
self.rest = Chef::REST.new(Chef::Config[:chef_server_url])
|
247
190
|
end
|
248
191
|
|
249
192
|
# Update the file caches for a given cache segment. Takes a segment name
|
@@ -253,54 +196,64 @@ class Chef
|
|
253
196
|
# === Parameters
|
254
197
|
# segment<String>:: The cache segment to update
|
255
198
|
# remote_list<Hash>:: A cookbooks/_attribute_files style remote file listing
|
256
|
-
def update_file_cache(
|
257
|
-
|
258
|
-
|
199
|
+
def update_file_cache(cookbook_name, parts)
|
200
|
+
Chef::Log.debug("Synchronizing cookbook #{cookbook_name}")
|
201
|
+
|
259
202
|
file_canonical = Hash.new
|
260
|
-
|
261
|
-
remote_list.each do |rf|
|
262
|
-
cache_file = File.join("cookbooks", rf['cookbook'], segment, rf['name'])
|
263
|
-
file_canonical[cache_file] = true
|
264
203
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
current_checksum = nil
|
269
|
-
if Chef::FileCache.has_key?(cache_file)
|
270
|
-
current_checksum = checksum(Chef::FileCache.load(cache_file, false))
|
271
|
-
end
|
204
|
+
[ "recipes", "attributes", "definitions", "libraries", "resources", "providers" ].each do |segment|
|
205
|
+
remote_list = parts.has_key?(segment) ? parts[segment] : []
|
272
206
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
207
|
+
# segement = cookbook segment
|
208
|
+
# remote_list = list of file hashes
|
209
|
+
#
|
210
|
+
# We need the list of known good attribute files, so we can delete any that are
|
211
|
+
# just laying about.
|
212
|
+
|
213
|
+
remote_list.each do |rf|
|
214
|
+
cache_file = File.join("cookbooks", cookbook_name, segment, rf['name'])
|
215
|
+
file_canonical[cache_file] = true
|
281
216
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
changed = false
|
289
|
-
Chef::Log.debug("Cache file #{cache_file} is unchanged")
|
290
|
-
else
|
291
|
-
raise e
|
292
|
-
end
|
217
|
+
# For back-compat between older clients and new chef servers
|
218
|
+
rf['checksum'] ||= nil
|
219
|
+
|
220
|
+
current_checksum = nil
|
221
|
+
if Chef::FileCache.has_key?(cache_file)
|
222
|
+
current_checksum = checksum(Chef::FileCache.load(cache_file, false))
|
293
223
|
end
|
294
224
|
|
295
|
-
if
|
296
|
-
|
297
|
-
|
225
|
+
if current_checksum != rf['checksum']
|
226
|
+
rf_url = generate_cookbook_url(
|
227
|
+
rf['name'],
|
228
|
+
cookbook_name,
|
229
|
+
segment,
|
230
|
+
@node,
|
231
|
+
current_checksum ? { 'checksum' => current_checksum } : nil
|
232
|
+
)
|
233
|
+
|
234
|
+
changed = true
|
235
|
+
begin
|
236
|
+
raw_file = rest.get_rest(rf_url, true)
|
237
|
+
rescue Net::HTTPRetriableError => e
|
238
|
+
if e.response.kind_of?(Net::HTTPNotModified)
|
239
|
+
changed = false
|
240
|
+
Chef::Log.debug("Cache file #{cache_file} is unchanged")
|
241
|
+
else
|
242
|
+
raise e
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
if changed
|
247
|
+
Chef::Log.info("Storing updated #{cache_file} in the cache.")
|
248
|
+
Chef::FileCache.move_to(raw_file.path, cache_file)
|
249
|
+
end
|
298
250
|
end
|
299
251
|
end
|
252
|
+
|
300
253
|
end
|
301
|
-
|
254
|
+
|
302
255
|
Chef::FileCache.list.each do |cache_file|
|
303
|
-
if cache_file
|
256
|
+
if cache_file =~ /^cookbooks\/#{cookbook_name}\/(recipes|attributes|definitions|libraries|resources|providers)\//
|
304
257
|
unless file_canonical[cache_file]
|
305
258
|
Chef::Log.info("Removing #{cache_file} from the cache; it is no longer on the server.")
|
306
259
|
Chef::FileCache.delete(cache_file)
|
@@ -309,69 +262,26 @@ class Chef
|
|
309
262
|
end
|
310
263
|
|
311
264
|
end
|
312
|
-
|
313
|
-
# Gets all the attribute files included in all the cookbooks available on the server,
|
314
|
-
# and executes them.
|
315
|
-
#
|
316
|
-
# === Returns
|
317
|
-
# true:: Always returns true
|
318
|
-
def sync_attribute_files
|
319
|
-
Chef::Log.debug("Synchronizing attributes")
|
320
|
-
update_file_cache("attributes", @rest.get_rest("cookbooks/_attribute_files?node=#{@node.name}"))
|
321
|
-
true
|
322
|
-
end
|
323
|
-
|
324
|
-
# Gets all the library files included in all the cookbooks available on the server,
|
325
|
-
# and loads them.
|
326
|
-
#
|
327
|
-
# === Returns
|
328
|
-
# true:: Always returns true
|
329
|
-
def sync_library_files
|
330
|
-
Chef::Log.debug("Synchronizing libraries")
|
331
|
-
update_file_cache("libraries", @rest.get_rest("cookbooks/_library_files?node=#{@node.name}"))
|
332
|
-
true
|
333
|
-
end
|
334
|
-
|
335
|
-
# Gets all the provider files included in all the cookbooks available on the server,
|
336
|
-
# and loads them.
|
337
|
-
#
|
338
|
-
# === Returns
|
339
|
-
# true:: Always returns true
|
340
|
-
def sync_provider_files
|
341
|
-
Chef::Log.debug("Synchronizing providers")
|
342
|
-
update_file_cache("providers", @rest.get_rest("cookbooks/_provider_files?node=#{@node.name}"))
|
343
|
-
true
|
344
|
-
end
|
345
|
-
|
346
|
-
# Gets all the resource files included in all the cookbooks available on the server,
|
347
|
-
# and loads them.
|
348
|
-
#
|
349
|
-
# === Returns
|
350
|
-
# true:: Always returns true
|
351
|
-
def sync_resource_files
|
352
|
-
Chef::Log.debug("Synchronizing resources")
|
353
|
-
update_file_cache("resources", @rest.get_rest("cookbooks/_resource_files?node=#{@node.name}"))
|
354
|
-
true
|
355
|
-
end
|
356
265
|
|
357
|
-
#
|
358
|
-
# and loads them.
|
359
|
-
#
|
360
|
-
# === Returns
|
361
|
-
# true:: Always returns true
|
362
|
-
def sync_definitions
|
363
|
-
Chef::Log.debug("Synchronizing definitions")
|
364
|
-
update_file_cache("definitions", @rest.get_rest("cookbooks/_definition_files?node=#{@node.name}"))
|
365
|
-
end
|
366
|
-
|
367
|
-
# Gets all the recipe files included in all the cookbooks available on the server,
|
368
|
-
# and loads them.
|
266
|
+
# Synchronizes all the cookbooks from the chef-server.
|
369
267
|
#
|
370
268
|
# === Returns
|
371
269
|
# true:: Always returns true
|
372
|
-
def
|
373
|
-
Chef::Log.debug("Synchronizing
|
374
|
-
|
270
|
+
def sync_cookbooks
|
271
|
+
Chef::Log.debug("Synchronizing cookbooks")
|
272
|
+
cookbook_hash = rest.get_rest("nodes/#{@node_name}/cookbooks")
|
273
|
+
Chef::Log.debug("Cookbooks to load: #{cookbook_hash.inspect}")
|
274
|
+
Chef::FileCache.list.each do |cache_file|
|
275
|
+
if cache_file =~ /^cookbooks\/(.+?)\//
|
276
|
+
unless cookbook_hash.has_key?($1)
|
277
|
+
Chef::Log.info("Removing #{cache_file} from the cache; it's cookbook is no longer needed on this client.")
|
278
|
+
Chef::FileCache.delete(cache_file)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
cookbook_hash.each do |cookbook_name, parts|
|
283
|
+
update_file_cache(cookbook_name, parts)
|
284
|
+
end
|
375
285
|
end
|
376
286
|
|
377
287
|
# Updates the current node configuration on the server.
|
@@ -379,15 +289,18 @@ class Chef
|
|
379
289
|
# === Returns
|
380
290
|
# true:: Always returns true
|
381
291
|
def save_node
|
382
|
-
Chef::Log.debug("Saving the current state of node #{@
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
292
|
+
Chef::Log.debug("Saving the current state of node #{@node_name}")
|
293
|
+
@node = begin
|
294
|
+
if @node_exists
|
295
|
+
rest.put_rest("nodes/#{@node_name}", @node)
|
296
|
+
else
|
297
|
+
result = rest.post_rest("nodes", @node)
|
298
|
+
@node_exists = true
|
299
|
+
rest.get_rest(result['uri'])
|
300
|
+
end
|
301
|
+
rescue
|
302
|
+
nil
|
303
|
+
end
|
391
304
|
end
|
392
305
|
|
393
306
|
# Compiles the full list of recipes for the server, and passes it to an instance of
|
@@ -396,26 +309,16 @@ class Chef
|
|
396
309
|
# === Returns
|
397
310
|
# true:: Always returns true
|
398
311
|
def converge(solo=false)
|
399
|
-
Chef::Log.debug("Compiling recipes for node #{@
|
312
|
+
Chef::Log.debug("Compiling recipes for node #{@node_name}")
|
400
313
|
unless solo
|
401
314
|
Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
|
402
315
|
end
|
403
316
|
compile = Chef::Compile.new(@node)
|
404
317
|
|
405
|
-
Chef::Log.debug("Converging node #{@
|
406
|
-
|
407
|
-
cr.converge
|
318
|
+
Chef::Log.debug("Converging node #{@node_name}")
|
319
|
+
Chef::Runner.new(@node, compile.collection, compile.definitions, compile.cookbook_loader).converge
|
408
320
|
true
|
409
321
|
end
|
410
|
-
|
411
|
-
protected
|
412
|
-
# Generates a random password of "len" length.
|
413
|
-
def random_password(len)
|
414
|
-
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
415
|
-
newpass = ""
|
416
|
-
1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
|
417
|
-
newpass
|
418
|
-
end
|
419
322
|
|
420
323
|
end
|
421
324
|
end
|