chef 0.9.18 → 0.10.0.beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +0 -3
- data/distro/arch/etc/rc.d/chef-server +0 -4
- data/distro/arch/etc/rc.d/chef-server-webui +0 -4
- data/distro/arch/etc/rc.d/chef-solr +0 -4
- data/distro/arch/etc/rc.d/chef-solr-indexer +0 -4
- data/lib/chef.rb +3 -3
- data/lib/chef/api_client.rb +1 -1
- data/lib/chef/application.rb +11 -1
- data/lib/chef/application/client.rb +18 -22
- data/lib/chef/application/knife.rb +28 -29
- data/lib/chef/application/solo.rb +14 -12
- data/lib/chef/client.rb +112 -54
- data/lib/chef/config.rb +4 -0
- data/lib/chef/cookbook/chefignore.rb +66 -0
- data/lib/chef/cookbook/cookbook_collection.rb +6 -5
- data/lib/chef/cookbook/cookbook_version_loader.rb +151 -0
- data/lib/chef/cookbook/file_system_file_vendor.rb +10 -8
- data/lib/chef/cookbook/metadata.rb +200 -108
- data/lib/chef/cookbook_loader.rb +39 -163
- data/lib/chef/cookbook_uploader.rb +100 -78
- data/lib/chef/cookbook_version.rb +92 -47
- data/lib/chef/cookbook_version_selector.rb +163 -0
- data/lib/chef/couchdb.rb +9 -1
- data/lib/chef/data_bag.rb +1 -1
- data/lib/chef/data_bag_item.rb +1 -1
- data/lib/chef/encrypted_data_bag_item.rb +126 -0
- data/lib/chef/environment.rb +386 -0
- data/lib/chef/exceptions.rb +82 -1
- data/lib/chef/index_queue/amqp_client.rb +15 -12
- data/lib/chef/index_queue/indexable.rb +38 -4
- data/lib/chef/json_compat.rb +3 -3
- data/lib/chef/knife.rb +97 -202
- data/lib/chef/knife/bootstrap.rb +27 -61
- data/lib/chef/knife/bootstrap/archlinux-gems.erb +4 -2
- data/lib/chef/knife/bootstrap/centos5-gems.erb +6 -15
- data/lib/chef/knife/bootstrap/fedora13-gems.erb +3 -4
- data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +2 -2
- data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +6 -5
- data/lib/chef/knife/client_bulk_delete.rb +6 -3
- data/lib/chef/knife/client_create.rb +13 -10
- data/lib/chef/knife/client_delete.rb +10 -7
- data/lib/chef/knife/client_edit.rb +9 -6
- data/lib/chef/knife/client_list.rb +8 -5
- data/lib/chef/knife/client_reregister.rb +9 -6
- data/lib/chef/knife/client_show.rb +9 -6
- data/lib/chef/knife/configure.rb +15 -19
- data/lib/chef/knife/configure_client.rb +4 -4
- data/lib/chef/knife/cookbook_bulk_delete.rb +11 -8
- data/lib/chef/knife/cookbook_create.rb +120 -55
- data/lib/chef/knife/cookbook_delete.rb +18 -12
- data/lib/chef/knife/cookbook_download.rb +10 -6
- data/lib/chef/knife/cookbook_list.rb +15 -6
- data/lib/chef/knife/cookbook_metadata.rb +41 -21
- data/lib/chef/knife/cookbook_metadata_from_file.rb +4 -0
- data/lib/chef/knife/cookbook_show.rb +16 -5
- data/lib/chef/knife/cookbook_site_download.rb +2 -2
- data/lib/chef/knife/cookbook_site_share.rb +18 -13
- data/lib/chef/knife/cookbook_site_unshare.rb +7 -4
- data/lib/chef/knife/cookbook_site_vendor.rb +21 -18
- data/lib/chef/knife/cookbook_test.rb +14 -14
- data/lib/chef/knife/cookbook_upload.rb +91 -40
- data/lib/chef/knife/data_bag_create.rb +41 -6
- data/lib/chef/knife/data_bag_delete.rb +5 -3
- data/lib/chef/knife/data_bag_edit.rb +55 -11
- data/lib/chef/knife/data_bag_from_file.rb +47 -7
- data/lib/chef/knife/data_bag_list.rb +4 -1
- data/lib/chef/knife/data_bag_show.rb +44 -4
- data/lib/chef/knife/environment_create.rb +53 -0
- data/lib/chef/knife/environment_delete.rb +45 -0
- data/lib/chef/knife/environment_edit.rb +45 -0
- data/lib/chef/knife/environment_from_file.rb +39 -0
- data/lib/chef/knife/environment_list.rb +42 -0
- data/lib/chef/knife/environment_show.rb +46 -0
- data/lib/chef/knife/exec.rb +1 -1
- data/lib/chef/knife/index_rebuild.rb +8 -9
- data/lib/chef/knife/node_bulk_delete.rb +9 -6
- data/lib/chef/knife/node_create.rb +9 -6
- data/lib/chef/knife/node_delete.rb +10 -7
- data/lib/chef/knife/node_edit.rb +129 -10
- data/lib/chef/knife/node_from_file.rb +10 -7
- data/lib/chef/knife/node_list.rb +11 -6
- data/lib/chef/knife/node_run_list_add.rb +10 -7
- data/lib/chef/knife/node_run_list_remove.rb +9 -6
- data/lib/chef/knife/node_show.rb +15 -7
- data/lib/chef/knife/recipe_list.rb +4 -3
- data/lib/chef/knife/role_bulk_delete.rb +9 -6
- data/lib/chef/knife/role_create.rb +9 -6
- data/lib/chef/knife/role_delete.rb +10 -7
- data/lib/chef/knife/role_edit.rb +11 -8
- data/lib/chef/knife/role_from_file.rb +10 -7
- data/lib/chef/knife/role_list.rb +8 -5
- data/lib/chef/knife/role_show.rb +11 -8
- data/lib/chef/knife/search.rb +33 -10
- data/lib/chef/knife/ssh.rb +33 -61
- data/lib/chef/knife/status.rb +7 -4
- data/lib/chef/knife/subcommand_loader.rb +101 -0
- data/lib/chef/knife/tag_create.rb +31 -0
- data/lib/chef/knife/tag_delete.rb +31 -0
- data/lib/chef/knife/tag_list.rb +29 -0
- data/lib/chef/knife/ui.rb +229 -0
- data/lib/chef/knife/windows_bootstrap.rb +8 -5
- data/lib/chef/log.rb +5 -59
- data/lib/chef/mash.rb +211 -0
- data/lib/chef/mixins.rb +1 -2
- data/lib/chef/nil_argument.rb +3 -0
- data/lib/chef/node.rb +96 -34
- data/lib/chef/platform.rb +27 -0
- data/lib/chef/provider/cookbook_file.rb +21 -20
- data/lib/chef/provider/deploy/revision.rb +3 -0
- data/lib/chef/provider/file.rb +20 -11
- data/lib/chef/provider/git.rb +26 -26
- data/lib/chef/provider/group/aix.rb +70 -0
- data/lib/chef/provider/group/groupadd.rb +7 -4
- data/lib/chef/provider/group/usermod.rb +1 -1
- data/lib/chef/provider/package.rb +28 -28
- data/lib/chef/provider/package/dpkg.rb +1 -1
- data/lib/chef/provider/package/portage.rb +50 -39
- data/lib/chef/provider/package/rubygems.rb +1 -1
- data/lib/chef/provider/package/zypper.rb +3 -20
- data/lib/chef/provider/remote_directory.rb +0 -2
- data/lib/chef/provider/remote_file.rb +2 -3
- data/lib/chef/provider/service/arch.rb +28 -35
- data/lib/chef/provider/service/simple.rb +1 -1
- data/lib/chef/provider/subversion.rb +22 -22
- data/lib/chef/providers.rb +1 -0
- data/lib/chef/recipe.rb +10 -12
- data/lib/chef/resource.rb +49 -42
- data/lib/chef/resource/gem_package.rb +7 -3
- data/lib/chef/resource/git.rb +5 -5
- data/lib/chef/resource/package.rb +7 -7
- data/lib/chef/resource/scm.rb +2 -1
- data/lib/chef/resource/solaris_package.rb +0 -1
- data/lib/chef/resource/yum_package.rb +0 -1
- data/lib/chef/rest.rb +7 -16
- data/lib/chef/rest/rest_request.rb +0 -16
- data/lib/chef/role.rb +67 -13
- data/lib/chef/run_context.rb +37 -21
- data/lib/chef/run_list.rb +30 -15
- data/lib/chef/run_list/run_list_expansion.rb +41 -20
- data/lib/chef/run_list/run_list_item.rb +20 -6
- data/lib/chef/run_list/versioned_recipe_list.rb +68 -0
- data/lib/chef/runner.rb +7 -15
- data/lib/chef/search/query.rb +12 -7
- data/lib/chef/shef.rb +6 -7
- data/lib/chef/shef/shef_session.rb +40 -35
- data/lib/chef/shell_out.rb +22 -201
- data/lib/chef/shell_out/unix.rb +224 -0
- data/lib/chef/shell_out/windows.rb +95 -0
- data/lib/chef/solr_query.rb +187 -0
- data/lib/chef/solr_query/lucene.treetop +145 -0
- data/lib/chef/solr_query/lucene_nodes.rb +285 -0
- data/lib/chef/solr_query/query_transform.rb +65 -0
- data/lib/chef/solr_query/solr_http_request.rb +118 -0
- data/lib/chef/version.rb +4 -2
- data/lib/chef/version_class.rb +70 -0
- data/lib/chef/version_constraint.rb +116 -0
- metadata +68 -37
- data/lib/chef/cookbook/metadata/version.rb +0 -87
- data/lib/chef/knife/bluebox_images_list.rb +0 -54
- data/lib/chef/knife/bluebox_server_create.rb +0 -157
- data/lib/chef/knife/bluebox_server_delete.rb +0 -63
- data/lib/chef/knife/bluebox_server_list.rb +0 -59
- data/lib/chef/knife/ec2_instance_data.rb +0 -46
- data/lib/chef/knife/ec2_server_create.rb +0 -218
- data/lib/chef/knife/ec2_server_delete.rb +0 -87
- data/lib/chef/knife/ec2_server_list.rb +0 -89
- data/lib/chef/knife/rackspace_server_create.rb +0 -184
- data/lib/chef/knife/rackspace_server_delete.rb +0 -57
- data/lib/chef/knife/rackspace_server_list.rb +0 -59
- data/lib/chef/knife/slicehost_images_list.rb +0 -53
- data/lib/chef/knife/slicehost_server_create.rb +0 -103
- data/lib/chef/knife/slicehost_server_delete.rb +0 -61
- data/lib/chef/knife/slicehost_server_list.rb +0 -64
- data/lib/chef/knife/terremark_server_create.rb +0 -152
- data/lib/chef/knife/terremark_server_delete.rb +0 -87
- data/lib/chef/knife/terremark_server_list.rb +0 -77
- data/lib/chef/mixin/find_preferred_file.rb +0 -92
data/lib/chef/exceptions.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Adam Jacob (<adam@opscode.com>)
|
|
3
|
-
#
|
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
|
4
|
+
# Copyright:: Copyright 2008-2010 Opscode, Inc.
|
|
4
5
|
# License:: Apache License, Version 2.0
|
|
5
6
|
#
|
|
6
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -49,6 +50,9 @@ class Chef
|
|
|
49
50
|
class RedirectLimitExceeded < RuntimeError; end
|
|
50
51
|
class AmbiguousRunlistSpecification < ArgumentError; end
|
|
51
52
|
class CookbookNotFound < RuntimeError; end
|
|
53
|
+
# Cookbook loader used to raise an argument error when cookbook not found.
|
|
54
|
+
# for back compat, need to raise an error that inherits from ArgumentError
|
|
55
|
+
class CookbookNotFoundInRepo < ArgumentError; end
|
|
52
56
|
class AttributeNotFound < RuntimeError; end
|
|
53
57
|
class InvalidCommandOption < RuntimeError; end
|
|
54
58
|
class CommandTimeout < RuntimeError; end
|
|
@@ -63,10 +67,87 @@ class Chef
|
|
|
63
67
|
class InvalidResourceSpecification < ArgumentError; end
|
|
64
68
|
class SolrConnectionError < RuntimeError; end
|
|
65
69
|
class IllegalChecksumRevert < RuntimeError; end
|
|
70
|
+
class CookbookVersionNameMismatch < ArgumentError; end
|
|
66
71
|
class MissingParentDirectory < RuntimeError; end
|
|
67
72
|
class UnresolvableGitReference < RuntimeError; end
|
|
68
73
|
class InvalidEnvironmentRunListSpecification < ArgumentError; end
|
|
69
74
|
class InvalidDataBagItemID < ArgumentError; end
|
|
70
75
|
class InvalidDataBagName < ArgumentError; end
|
|
76
|
+
class EnclosingDirectoryDoesNotExist < ArgumentError; end
|
|
77
|
+
|
|
78
|
+
class ObsoleteDependencySyntax < ArgumentError; end
|
|
79
|
+
|
|
80
|
+
# A different version of a cookbook was added to a
|
|
81
|
+
# VersionedRecipeList than the one already there.
|
|
82
|
+
class CookbookVersionConflict < ArgumentError ; end
|
|
83
|
+
|
|
84
|
+
# does not follow X.Y.Z format. ArgumentError?
|
|
85
|
+
class InvalidCookbookVersion < ArgumentError; end
|
|
86
|
+
|
|
87
|
+
# version constraint should be a string or array, or it doesn't
|
|
88
|
+
# match OP VERSION. ArgumentError?
|
|
89
|
+
class InvalidVersionConstraint < ArgumentError; end
|
|
90
|
+
|
|
91
|
+
class CookbookVersionSelection
|
|
92
|
+
|
|
93
|
+
# Compound exception: In run_list expansion and resolution,
|
|
94
|
+
# run_list items referred to cookbooks that don't exist and/or
|
|
95
|
+
# have no versions available.
|
|
96
|
+
class InvalidRunListItems < StandardError
|
|
97
|
+
attr_reader :non_existent_cookbooks
|
|
98
|
+
attr_reader :cookbooks_with_no_matching_versions
|
|
99
|
+
|
|
100
|
+
def initialize(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
|
|
101
|
+
super(message)
|
|
102
|
+
|
|
103
|
+
@non_existent_cookbooks = non_existent_cookbooks
|
|
104
|
+
@cookbooks_with_no_matching_versions = cookbooks_with_no_matching_versions
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def to_json(*a)
|
|
108
|
+
result = {
|
|
109
|
+
"message" => message,
|
|
110
|
+
"non_existent_cookbooks" => non_existent_cookbooks,
|
|
111
|
+
"cookbooks_with_no_versions" => cookbooks_with_no_matching_versions
|
|
112
|
+
}
|
|
113
|
+
result.to_json(*a)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# In run_list expansion and resolution, a constraint was
|
|
118
|
+
# unsatisfiable.
|
|
119
|
+
#
|
|
120
|
+
# This exception may not be the complete error report. If you
|
|
121
|
+
# resolve the misconfiguration represented by this exception and
|
|
122
|
+
# re-solve, you may get another exception
|
|
123
|
+
class UnsatisfiableRunListItem < StandardError
|
|
124
|
+
attr_reader :run_list_item
|
|
125
|
+
attr_reader :non_existent_cookbooks, :most_constrained_cookbooks
|
|
126
|
+
|
|
127
|
+
# most_constrained_cookbooks: if I were to remove constraints
|
|
128
|
+
# regarding these cookbooks, I would get a solution or move on
|
|
129
|
+
# to the next error (deeper in the graph). An item in this list
|
|
130
|
+
# may be unsatisfiable, but when resolved may also reveal
|
|
131
|
+
# further unsatisfiable constraints; this condition would not be
|
|
132
|
+
# reported.
|
|
133
|
+
def initialize(message, run_list_item, non_existent_cookbooks, most_constrained_cookbooks)
|
|
134
|
+
super(message)
|
|
135
|
+
|
|
136
|
+
@run_list_item = run_list_item
|
|
137
|
+
@non_existent_cookbooks = non_existent_cookbooks
|
|
138
|
+
@most_constrained_cookbooks = most_constrained_cookbooks
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def to_json(*a)
|
|
142
|
+
result = {
|
|
143
|
+
"message" => message,
|
|
144
|
+
"unsatisfiable_run_list_item" => run_list_item,
|
|
145
|
+
"non_existent_cookbooks" => non_existent_cookbooks,
|
|
146
|
+
"most_constrained_cookbooks" => most_constrained_cookbooks
|
|
147
|
+
}
|
|
148
|
+
result.to_json(*a)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
71
152
|
end
|
|
72
153
|
end
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
class Chef
|
|
20
20
|
module IndexQueue
|
|
21
21
|
class AmqpClient
|
|
22
|
+
VNODES = 1024
|
|
23
|
+
|
|
22
24
|
include Singleton
|
|
23
25
|
|
|
24
26
|
def initialize
|
|
@@ -29,11 +31,9 @@ class Chef
|
|
|
29
31
|
@amqp_client && amqp_client.connected? && amqp_client.stop
|
|
30
32
|
@amqp_client = nil
|
|
31
33
|
@exchange = nil
|
|
32
|
-
@queue = nil
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def stop
|
|
36
|
-
@queue && @queue.subscription && @queue.unsubscribe
|
|
37
37
|
@amqp_client && @amqp_client.stop
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -59,24 +59,17 @@ class Chef
|
|
|
59
59
|
@exchange ||= amqp_client.exchange("chef-indexer", :durable => true, :type => :fanout)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def queue
|
|
63
|
-
unless @queue
|
|
64
|
-
@queue = amqp_client.queue("chef-index-consumer-" + consumer_id, :durable => durable_queue?)
|
|
65
|
-
@queue.bind(exchange)
|
|
66
|
-
end
|
|
67
|
-
@queue
|
|
68
|
-
end
|
|
69
|
-
|
|
70
62
|
def disconnected!
|
|
71
63
|
Chef::Log.error("Disconnected from the AMQP Broker (RabbitMQ)")
|
|
72
64
|
@amqp_client = nil
|
|
73
65
|
reset!
|
|
74
66
|
end
|
|
75
67
|
|
|
76
|
-
def
|
|
68
|
+
def queue_for_object(obj_id)
|
|
77
69
|
retries = 0
|
|
70
|
+
vnode_tag = obj_id_to_int(obj_id) % VNODES
|
|
78
71
|
begin
|
|
79
|
-
|
|
72
|
+
yield amqp_client.queue("vnode-#{vnode_tag}", :passive => false, :durable => true, :exclusive => false, :auto_delete => false)
|
|
80
73
|
rescue Bunny::ServerDownError, Bunny::ConnectionError, Errno::ECONNRESET
|
|
81
74
|
disconnected!
|
|
82
75
|
if (retries += 1) < 2
|
|
@@ -90,6 +83,15 @@ class Chef
|
|
|
90
83
|
end
|
|
91
84
|
|
|
92
85
|
private
|
|
86
|
+
|
|
87
|
+
# Sometimes object ids are "proper" UUIDs, like "64bc00eb-120b-b6a2-ec0e-34fc90d151be"
|
|
88
|
+
# and sometimes they omit the dashes, like "64bc00eb120bb6a2ec0e34fc90d151be"
|
|
89
|
+
# UUIDTools uses different methods to parse the different styles.
|
|
90
|
+
def obj_id_to_int(obj_id)
|
|
91
|
+
UUIDTools::UUID.parse(obj_id).to_i
|
|
92
|
+
rescue ArgumentError
|
|
93
|
+
UUIDTools::UUID.parse_hexdigest(obj_id).to_i
|
|
94
|
+
end
|
|
93
95
|
|
|
94
96
|
def durable_queue?
|
|
95
97
|
!!Chef::Config[:amqp_consumer_id]
|
|
@@ -111,3 +113,4 @@ class Chef
|
|
|
111
113
|
end
|
|
112
114
|
end
|
|
113
115
|
end
|
|
116
|
+
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Daniel DeLeo (<dan@kallistec.com>)
|
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
|
3
4
|
# Copyright:: Copyright (c) 2009 Daniel DeLeo
|
|
5
|
+
# Copyright:: Copyright (c) 2010 Opscode, Inc.
|
|
4
6
|
# License:: Apache License, Version 2.0
|
|
5
7
|
#
|
|
6
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
# See the License for the specific language governing permissions and
|
|
16
18
|
# limitations under the License.
|
|
17
19
|
#
|
|
20
|
+
require 'chef/json_compat'
|
|
18
21
|
|
|
19
22
|
class Chef
|
|
20
23
|
module IndexQueue
|
|
@@ -56,20 +59,51 @@ class Chef
|
|
|
56
59
|
with_metadata["id"] ||= self.index_id
|
|
57
60
|
with_metadata["database"] ||= Chef::Config[:couchdb_database]
|
|
58
61
|
with_metadata["item"] ||= self.to_hash
|
|
62
|
+
with_metadata["enqueued_at"] ||= Time.now.utc.to_i
|
|
59
63
|
|
|
60
64
|
raise ArgumentError, "Type, Id, or Database missing in index operation: #{with_metadata.inspect}" if (with_metadata["id"].nil? or with_metadata["type"].nil?)
|
|
61
65
|
with_metadata
|
|
62
66
|
end
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
def add_to_index(metadata={})
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
Chef::Log.debug("pushing item to index queue for addition: #{self.with_indexer_metadata(metadata)}")
|
|
70
|
+
object_with_metadata = with_indexer_metadata(metadata)
|
|
71
|
+
obj_id = object_with_metadata["id"]
|
|
72
|
+
obj = {:action => :add, :payload => self.with_indexer_metadata(metadata)}
|
|
73
|
+
|
|
74
|
+
publish_object(obj_id, obj)
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
def delete_from_index(metadata={})
|
|
70
78
|
Chef::Log.debug("pushing item to index queue for deletion: #{self.with_indexer_metadata(metadata)}")
|
|
71
|
-
|
|
79
|
+
object_with_metadata = with_indexer_metadata(metadata)
|
|
80
|
+
obj_id = object_with_metadata["id"]
|
|
81
|
+
obj = {:action => :delete, :payload => self.with_indexer_metadata(metadata)}
|
|
82
|
+
|
|
83
|
+
publish_object(obj_id, obj)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Uses the publisher to update the object's queue. If
|
|
89
|
+
# Chef::Config[:persistent_queue] is true, the update is wrapped
|
|
90
|
+
# in a transaction.
|
|
91
|
+
def publish_object(object_id, object)
|
|
92
|
+
publisher = AmqpClient.instance
|
|
93
|
+
begin
|
|
94
|
+
publisher.amqp_client.tx_select if Chef::Config[:persistent_queue]
|
|
95
|
+
publisher.queue_for_object(object_id) do |queue|
|
|
96
|
+
queue.publish(Chef::JSONCompat.to_json(object), :persistent => Chef::Config[:persistent_queue])
|
|
97
|
+
end
|
|
98
|
+
publisher.amqp_client.tx_commit if Chef::Config[:persistent_queue]
|
|
99
|
+
rescue
|
|
100
|
+
publisher.amqp_client.tx_rollback if Chef::Config[:persistent_queue]
|
|
101
|
+
raise
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
true
|
|
72
105
|
end
|
|
106
|
+
|
|
73
107
|
end
|
|
74
108
|
end
|
|
75
109
|
end
|
data/lib/chef/json_compat.rb
CHANGED
|
@@ -24,8 +24,9 @@ class Chef
|
|
|
24
24
|
JSON_MAX_NESTING = 1000
|
|
25
25
|
|
|
26
26
|
class <<self
|
|
27
|
-
# See PL-538. Increase the max nesting for JSON, which defaults
|
|
28
|
-
# and isn't enough for some
|
|
27
|
+
# See CHEF-1292/PL-538. Increase the max nesting for JSON, which defaults
|
|
28
|
+
# to 19, and isn't enough for some (for example, a Node within a Node)
|
|
29
|
+
# structures.
|
|
29
30
|
def opts_add_max_nesting(opts)
|
|
30
31
|
if opts.nil? || !opts.has_key?(:max_nesting)
|
|
31
32
|
opts = opts.nil? ? Hash.new : opts.clone
|
|
@@ -40,7 +41,6 @@ class Chef
|
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
def to_json(obj, opts = nil)
|
|
43
|
-
#::JSON.generate(obj, opts_add_max_nesting(opts))
|
|
44
44
|
obj.to_json(opts_add_max_nesting(opts))
|
|
45
45
|
end
|
|
46
46
|
|
data/lib/chef/knife.rb
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
8
|
# you may not use this file except in compliance with the License.
|
|
9
9
|
# You may obtain a copy of the License at
|
|
10
|
-
#
|
|
10
|
+
#
|
|
11
11
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
12
|
+
#
|
|
13
13
|
# Unless required by applicable law or agreed to in writing, software
|
|
14
14
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
15
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
@@ -17,28 +17,52 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
#
|
|
19
19
|
|
|
20
|
+
require 'forwardable'
|
|
20
21
|
require 'chef/version'
|
|
21
22
|
require 'mixlib/cli'
|
|
22
23
|
require 'chef/mixin/convert_to_class_name'
|
|
23
|
-
require 'chef/
|
|
24
|
+
require 'chef/knife/subcommand_loader'
|
|
25
|
+
require 'chef/knife/ui'
|
|
26
|
+
|
|
24
27
|
require 'pp'
|
|
25
28
|
|
|
26
29
|
class Chef
|
|
27
30
|
class Knife
|
|
28
|
-
|
|
29
|
-
Chef::REST::RESTRequest.user_agent = "Chef Knife#{Chef::REST::RESTRequest::UA_COMMON}"
|
|
30
|
-
|
|
31
31
|
include Mixlib::CLI
|
|
32
32
|
extend Chef::Mixin::ConvertToClassName
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
extend Forwardable
|
|
34
|
+
|
|
35
|
+
# Backwards Compat:
|
|
36
|
+
# Ideally, we should not vomit all of these methods into this base class;
|
|
37
|
+
# instead, they should be accessed by hitting the ui object directly.
|
|
38
|
+
def_delegator :@ui, :stdout
|
|
39
|
+
def_delegator :@ui, :stderr
|
|
40
|
+
def_delegator :@ui, :stdin
|
|
41
|
+
def_delegator :@ui, :msg
|
|
42
|
+
def_delegator :@ui, :ask_question
|
|
43
|
+
def_delegator :@ui, :pretty_print
|
|
44
|
+
def_delegator :@ui, :output
|
|
45
|
+
def_delegator :@ui, :format_list_for_display
|
|
46
|
+
def_delegator :@ui, :format_for_display
|
|
47
|
+
def_delegator :@ui, :format_cookbook_list_for_display
|
|
48
|
+
def_delegator :@ui, :edit_data
|
|
49
|
+
def_delegator :@ui, :edit_object
|
|
50
|
+
def_delegator :@ui, :confirm
|
|
37
51
|
|
|
38
52
|
attr_accessor :name_args
|
|
53
|
+
attr_reader :ui
|
|
54
|
+
|
|
55
|
+
def self.ui
|
|
56
|
+
@ui ||= Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
|
|
57
|
+
end
|
|
39
58
|
|
|
40
59
|
def self.msg(msg="")
|
|
41
|
-
|
|
60
|
+
ui.msg(msg)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.reset_subcommands!
|
|
64
|
+
@@subcommands = {}
|
|
65
|
+
@subcommands_by_category = nil
|
|
42
66
|
end
|
|
43
67
|
|
|
44
68
|
def self.inherited(subclass)
|
|
@@ -73,6 +97,14 @@ class Chef
|
|
|
73
97
|
name.nil? || name.empty?
|
|
74
98
|
end
|
|
75
99
|
|
|
100
|
+
def self.subcommand_loader
|
|
101
|
+
@subcommand_loader ||= Knife::SubcommandLoader.new(chef_config_dir)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.load_commands
|
|
105
|
+
subcommand_loader.load_commands
|
|
106
|
+
end
|
|
107
|
+
|
|
76
108
|
def self.subcommands
|
|
77
109
|
@@subcommands ||= {}
|
|
78
110
|
end
|
|
@@ -87,17 +119,10 @@ class Chef
|
|
|
87
119
|
@subcommands_by_category
|
|
88
120
|
end
|
|
89
121
|
|
|
90
|
-
# Load all the sub-commands
|
|
91
|
-
def self.load_commands
|
|
92
|
-
DEFAULT_SUBCOMMAND_FILES.each { |subcommand| require subcommand }
|
|
93
|
-
subcommands
|
|
94
|
-
end
|
|
95
|
-
|
|
96
122
|
# Print the list of subcommands knife knows about. If +preferred_category+
|
|
97
123
|
# is given, only subcommands in that category are shown
|
|
98
124
|
def self.list_commands(preferred_category=nil)
|
|
99
125
|
load_commands
|
|
100
|
-
|
|
101
126
|
category_desc = preferred_category ? preferred_category + " " : ''
|
|
102
127
|
msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
|
|
103
128
|
|
|
@@ -125,7 +150,8 @@ class Chef
|
|
|
125
150
|
def self.run(args, options={})
|
|
126
151
|
load_commands
|
|
127
152
|
subcommand_class = subcommand_class_from(args)
|
|
128
|
-
subcommand_class.options.merge!(options)
|
|
153
|
+
subcommand_class.options = options.merge!(subcommand_class.options)
|
|
154
|
+
subcommand_class.load_deps
|
|
129
155
|
instance = subcommand_class.new(args)
|
|
130
156
|
instance.configure_chef
|
|
131
157
|
instance.run
|
|
@@ -155,16 +181,12 @@ class Chef
|
|
|
155
181
|
subcommand_class || subcommand_not_found!(args)
|
|
156
182
|
end
|
|
157
183
|
|
|
158
|
-
|
|
184
|
+
def self.deps(&block)
|
|
185
|
+
@dependency_loader = block
|
|
186
|
+
end
|
|
159
187
|
|
|
160
|
-
def
|
|
161
|
-
|
|
162
|
-
require dep
|
|
163
|
-
rescue LoadError
|
|
164
|
-
gem_name ||= dep.gsub('/', '-')
|
|
165
|
-
Chef::Log.fatal "#{gem_name} is not installed. run \"gem install #{gem_name}\" to install it."
|
|
166
|
-
exit 1
|
|
167
|
-
end
|
|
188
|
+
def self.load_deps
|
|
189
|
+
@dependency_loader && @dependency_loader.call
|
|
168
190
|
end
|
|
169
191
|
|
|
170
192
|
private
|
|
@@ -174,9 +196,9 @@ class Chef
|
|
|
174
196
|
# user could not be resolved to a subcommand.
|
|
175
197
|
def self.subcommand_not_found!(args)
|
|
176
198
|
unless want_help?(args)
|
|
177
|
-
|
|
199
|
+
ui.fatal("Cannot find sub command for: '#{args.join(' ')}'")
|
|
178
200
|
end
|
|
179
|
-
|
|
201
|
+
list_commands(guess_category(args))
|
|
180
202
|
exit 10
|
|
181
203
|
end
|
|
182
204
|
|
|
@@ -187,12 +209,32 @@ class Chef
|
|
|
187
209
|
(args.any? { |arg| arg =~ /^(:?(:?\-\-)?help|\-h)$/})
|
|
188
210
|
end
|
|
189
211
|
|
|
212
|
+
@@chef_config_dir = nil
|
|
213
|
+
|
|
214
|
+
# search upward from current_dir until .chef directory is found
|
|
215
|
+
def self.chef_config_dir
|
|
216
|
+
if @@chef_config_dir.nil? # share this with subclasses
|
|
217
|
+
@@chef_config_dir = false
|
|
218
|
+
full_path = Dir.pwd.split(File::SEPARATOR)
|
|
219
|
+
(full_path.length - 1).downto(0) do |i|
|
|
220
|
+
canidate_directory = File.join(full_path[0..i] + [".chef" ])
|
|
221
|
+
if File.exist?(canidate_directory) && File.directory?(canidate_directory)
|
|
222
|
+
@@chef_config_dir = canidate_directory
|
|
223
|
+
break
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
@@chef_config_dir
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
|
|
190
231
|
public
|
|
191
232
|
|
|
192
233
|
# Create a new instance of the current class configured for the given
|
|
193
234
|
# arguments and options
|
|
194
235
|
def initialize(argv=[])
|
|
195
236
|
super() # having to call super in initialize is the most annoying anti-pattern :(
|
|
237
|
+
@ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, config)
|
|
196
238
|
|
|
197
239
|
command_name_words = self.class.snake_case_name.split('_')
|
|
198
240
|
|
|
@@ -219,37 +261,11 @@ class Chef
|
|
|
219
261
|
exit(1)
|
|
220
262
|
end
|
|
221
263
|
|
|
222
|
-
def ask_question(question, opts={})
|
|
223
|
-
question = question + "[#{opts[:default]}] " if opts[:default]
|
|
224
|
-
|
|
225
|
-
if opts[:default] and config[:defaults]
|
|
226
|
-
|
|
227
|
-
opts[:default]
|
|
228
|
-
|
|
229
|
-
else
|
|
230
|
-
|
|
231
|
-
stdout.print question
|
|
232
|
-
a = stdin.readline.strip
|
|
233
|
-
|
|
234
|
-
if opts[:default]
|
|
235
|
-
a.empty? ? opts[:default] : a
|
|
236
|
-
else
|
|
237
|
-
a
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
end
|
|
243
|
-
|
|
244
264
|
def configure_chef
|
|
245
265
|
unless config[:config_file]
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if File.exists?(config_file_to_check)
|
|
250
|
-
config[:config_file] = config_file_to_check
|
|
251
|
-
break
|
|
252
|
-
end
|
|
266
|
+
if self.class.chef_config_dir
|
|
267
|
+
candidate_config = File.expand_path('knife.rb',self.class.chef_config_dir)
|
|
268
|
+
config[:config_file] = candidate_config if File.exist?(candidate_config)
|
|
253
269
|
end
|
|
254
270
|
# If we haven't set a config yet and $HOME is set, and the home
|
|
255
271
|
# knife.rb exists, use it:
|
|
@@ -266,13 +282,22 @@ class Chef
|
|
|
266
282
|
self.msg("No knife configuration file found")
|
|
267
283
|
end
|
|
268
284
|
|
|
269
|
-
Chef::Config[:log_level]
|
|
270
|
-
Chef::Config[:log_location]
|
|
271
|
-
Chef::Config[:node_name]
|
|
272
|
-
Chef::Config[:client_key]
|
|
273
|
-
Chef::Config[:chef_server_url]
|
|
285
|
+
Chef::Config[:log_level] = config[:log_level] if config[:log_level]
|
|
286
|
+
Chef::Config[:log_location] = config[:log_location] if config[:log_location]
|
|
287
|
+
Chef::Config[:node_name] = config[:node_name] if config[:node_name]
|
|
288
|
+
Chef::Config[:client_key] = config[:client_key] if config[:client_key]
|
|
289
|
+
Chef::Config[:chef_server_url] = config[:chef_server_url] if config[:chef_server_url]
|
|
290
|
+
Chef::Config[:environment] = config[:environment] if config[:environment]
|
|
291
|
+
|
|
292
|
+
# Expand a relative path from the config directory. Config from command
|
|
293
|
+
# line should already be expanded, and absolute paths will be unchanged.
|
|
294
|
+
if Chef::Config[:client_key] && config[:config_file]
|
|
295
|
+
Chef::Config[:client_key] = File.expand_path(Chef::Config[:client_key], File.dirname(config[:config_file]))
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
Mixlib::Log::Formatter.show_time = false
|
|
274
299
|
Chef::Log.init(Chef::Config[:log_location])
|
|
275
|
-
Chef::Log.level(Chef::Config[:log_level])
|
|
300
|
+
Chef::Log.level(Chef::Config[:log_level] || :error)
|
|
276
301
|
|
|
277
302
|
Chef::Log.debug("Using configuration from #{config[:config_file]}")
|
|
278
303
|
|
|
@@ -281,99 +306,6 @@ class Chef
|
|
|
281
306
|
end
|
|
282
307
|
end
|
|
283
308
|
|
|
284
|
-
def pretty_print(data)
|
|
285
|
-
puts data
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
def output(data)
|
|
289
|
-
case config[:format]
|
|
290
|
-
when "json", nil
|
|
291
|
-
stdout.puts Chef::JSONCompat.to_json_pretty(data)
|
|
292
|
-
when "yaml"
|
|
293
|
-
require 'yaml'
|
|
294
|
-
stdout.puts YAML::dump(data)
|
|
295
|
-
when "text"
|
|
296
|
-
# If you were looking for some attribute and there is only one match
|
|
297
|
-
# just dump the attribute value
|
|
298
|
-
if data.length == 1 and config[:attribute]
|
|
299
|
-
stdout.puts data.values[0]
|
|
300
|
-
else
|
|
301
|
-
PP.pp(data, stdout)
|
|
302
|
-
end
|
|
303
|
-
else
|
|
304
|
-
raise ArgumentError, "Unknown output format #{config[:format]}"
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def format_list_for_display(list)
|
|
309
|
-
config[:with_uri] ? list : list.keys.sort { |a,b| a <=> b }
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
def format_for_display(item)
|
|
313
|
-
data = item.kind_of?(Chef::DataBagItem) ? item.raw_data : item
|
|
314
|
-
|
|
315
|
-
if config[:attribute]
|
|
316
|
-
config[:attribute].split(".").each do |attr|
|
|
317
|
-
if data.respond_to?(:[])
|
|
318
|
-
data = data[attr]
|
|
319
|
-
elsif data.nil?
|
|
320
|
-
nil # don't get no method error on nil
|
|
321
|
-
else data.respond_to?(attr.to_sym)
|
|
322
|
-
data = data.send(attr.to_sym)
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
{ config[:attribute] => data.kind_of?(Chef::Node::Attribute) ? data.to_hash : data }
|
|
326
|
-
elsif config[:run_list]
|
|
327
|
-
data = data.run_list.run_list
|
|
328
|
-
{ "run_list" => data }
|
|
329
|
-
elsif config[:id_only]
|
|
330
|
-
data.respond_to?(:name) ? data.name : data["id"]
|
|
331
|
-
else
|
|
332
|
-
data
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def edit_data(data, parse_output=true)
|
|
337
|
-
output = Chef::JSONCompat.to_json_pretty(data)
|
|
338
|
-
|
|
339
|
-
if (!config[:no_editor])
|
|
340
|
-
filename = "knife-edit-"
|
|
341
|
-
0.upto(20) { filename += rand(9).to_s }
|
|
342
|
-
filename << ".js"
|
|
343
|
-
filename = File.join(Dir.tmpdir, filename)
|
|
344
|
-
tf = File.open(filename, "w")
|
|
345
|
-
tf.sync = true
|
|
346
|
-
tf.puts output
|
|
347
|
-
tf.close
|
|
348
|
-
raise "Please set EDITOR environment variable" unless system("#{config[:editor]} #{tf.path}")
|
|
349
|
-
tf = File.open(filename, "r")
|
|
350
|
-
output = tf.gets(nil)
|
|
351
|
-
tf.close
|
|
352
|
-
File.unlink(filename)
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
parse_output ? Chef::JSONCompat.from_json(output) : output
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
def confirm(question, append_instructions=true)
|
|
359
|
-
return true if config[:yes]
|
|
360
|
-
|
|
361
|
-
stdout.print question
|
|
362
|
-
stdout.print "? (Y/N) " if append_instructions
|
|
363
|
-
answer = stdin.readline
|
|
364
|
-
answer.chomp!
|
|
365
|
-
case answer
|
|
366
|
-
when "Y", "y"
|
|
367
|
-
true
|
|
368
|
-
when "N", "n"
|
|
369
|
-
self.msg("You said no, so I'm done here.")
|
|
370
|
-
exit 3
|
|
371
|
-
else
|
|
372
|
-
self.msg("I have no idea what to do with #{answer}")
|
|
373
|
-
self.msg("Just say Y or N, please.")
|
|
374
|
-
confirm(question)
|
|
375
|
-
end
|
|
376
|
-
end
|
|
377
309
|
|
|
378
310
|
def show_usage
|
|
379
311
|
stdout.puts("USAGE: " + self.opt_parser.to_s)
|
|
@@ -387,6 +319,8 @@ class Chef
|
|
|
387
319
|
relative_path = "nodes"
|
|
388
320
|
elsif klass == Chef::DataBagItem
|
|
389
321
|
relative_path = "data_bags/#{bag}"
|
|
322
|
+
elsif klass == Chef::Environment
|
|
323
|
+
relative_path = "environments"
|
|
390
324
|
end
|
|
391
325
|
|
|
392
326
|
relative_file = File.expand_path(File.join(Dir.pwd, relative_path, from_file))
|
|
@@ -394,10 +328,10 @@ class Chef
|
|
|
394
328
|
|
|
395
329
|
if file_exists_and_is_readable?(from_file)
|
|
396
330
|
filename = from_file
|
|
397
|
-
elsif file_exists_and_is_readable?(relative_file)
|
|
398
|
-
filename = relative_file
|
|
331
|
+
elsif file_exists_and_is_readable?(relative_file)
|
|
332
|
+
filename = relative_file
|
|
399
333
|
else
|
|
400
|
-
|
|
334
|
+
ui.fatal("Cannot find file #{from_file}")
|
|
401
335
|
exit 30
|
|
402
336
|
end
|
|
403
337
|
|
|
@@ -409,7 +343,7 @@ class Chef
|
|
|
409
343
|
r.from_file(filename)
|
|
410
344
|
r
|
|
411
345
|
else
|
|
412
|
-
|
|
346
|
+
ui.fatal("File must end in .js, .json, or .rb")
|
|
413
347
|
exit 30
|
|
414
348
|
end
|
|
415
349
|
end
|
|
@@ -418,33 +352,6 @@ class Chef
|
|
|
418
352
|
File.exists?(file) && File.readable?(file)
|
|
419
353
|
end
|
|
420
354
|
|
|
421
|
-
def edit_object(klass, name)
|
|
422
|
-
object = klass.load(name)
|
|
423
|
-
|
|
424
|
-
output = edit_data(object)
|
|
425
|
-
|
|
426
|
-
# Only make the save if the user changed the object.
|
|
427
|
-
#
|
|
428
|
-
# Output JSON for the original (object) and edited (output), then parse
|
|
429
|
-
# them without reconstituting the objects into real classes
|
|
430
|
-
# (create_additions=false). Then, compare the resulting simple objects,
|
|
431
|
-
# which will be Array/Hash/String/etc.
|
|
432
|
-
#
|
|
433
|
-
# We wouldn't have to do these shenanigans if all the editable objects
|
|
434
|
-
# implemented to_hash, or if to_json against a hash returned a string
|
|
435
|
-
# with stable key order.
|
|
436
|
-
object_parsed_again = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(object), :create_additions => false)
|
|
437
|
-
output_parsed_again = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(output), :create_additions => false)
|
|
438
|
-
if object_parsed_again != output_parsed_again
|
|
439
|
-
output.save
|
|
440
|
-
self.msg("Saved #{output}")
|
|
441
|
-
else
|
|
442
|
-
self.msg("Object unchanged, not saving")
|
|
443
|
-
end
|
|
444
|
-
|
|
445
|
-
output(format_for_display(object)) if config[:print_after]
|
|
446
|
-
end
|
|
447
|
-
|
|
448
355
|
def create_object(object, pretty_name=nil, &block)
|
|
449
356
|
output = edit_data(object)
|
|
450
357
|
|
|
@@ -457,7 +364,7 @@ class Chef
|
|
|
457
364
|
pretty_name ||= output
|
|
458
365
|
|
|
459
366
|
self.msg("Created (or updated) #{pretty_name}")
|
|
460
|
-
|
|
367
|
+
|
|
461
368
|
output(output) if config[:print_after]
|
|
462
369
|
end
|
|
463
370
|
|
|
@@ -505,18 +412,6 @@ class Chef
|
|
|
505
412
|
end
|
|
506
413
|
end
|
|
507
414
|
|
|
508
|
-
def msg(message)
|
|
509
|
-
stdout.puts message
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
def stdout
|
|
513
|
-
STDOUT
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
def stdin
|
|
517
|
-
STDIN
|
|
518
|
-
end
|
|
519
|
-
|
|
520
415
|
def rest
|
|
521
416
|
@rest ||= Chef::REST.new(Chef::Config[:chef_server_url])
|
|
522
417
|
end
|