cheffish 0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +4 -0
- data/Rakefile +23 -0
- data/lib/chef/provider/chef_client.rb +44 -0
- data/lib/chef/provider/chef_data_bag.rb +50 -0
- data/lib/chef/provider/chef_data_bag_item.rb +273 -0
- data/lib/chef/provider/chef_environment.rb +78 -0
- data/lib/chef/provider/chef_node.rb +82 -0
- data/lib/chef/provider/chef_role.rb +79 -0
- data/lib/chef/provider/chef_user.rb +48 -0
- data/lib/chef/provider/private_key.rb +160 -0
- data/lib/chef/provider/public_key.rb +83 -0
- data/lib/chef/resource/chef_client.rb +44 -0
- data/lib/chef/resource/chef_data_bag.rb +18 -0
- data/lib/chef/resource/chef_data_bag_item.rb +114 -0
- data/lib/chef/resource/chef_environment.rb +71 -0
- data/lib/chef/resource/chef_node.rb +18 -0
- data/lib/chef/resource/chef_role.rb +104 -0
- data/lib/chef/resource/chef_user.rb +51 -0
- data/lib/chef/resource/in_parallel.rb +6 -0
- data/lib/chef/resource/private_key.rb +39 -0
- data/lib/chef/resource/public_key.rb +16 -0
- data/lib/cheffish.rb +245 -0
- data/lib/cheffish/actor_provider_base.rb +120 -0
- data/lib/cheffish/chef_provider_base.rb +222 -0
- data/lib/cheffish/cheffish_server_api.rb +21 -0
- data/lib/cheffish/inline_resource.rb +88 -0
- data/lib/cheffish/key_formatter.rb +93 -0
- data/lib/cheffish/recipe_dsl.rb +98 -0
- data/lib/cheffish/version.rb +4 -0
- data/spec/integration/chef_client_spec.rb +48 -0
- data/spec/integration/chef_node_spec.rb +75 -0
- data/spec/integration/chef_user_spec.rb +48 -0
- data/spec/integration/private_key_spec.rb +356 -0
- data/spec/integration/recipe_dsl_spec.rb +29 -0
- data/spec/support/key_support.rb +29 -0
- data/spec/support/spec_support.rb +148 -0
- metadata +124 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'chef/config'
|
2
|
+
require 'chef/run_list'
|
3
|
+
require 'cheffish/cheffish_server_api'
|
4
|
+
|
5
|
+
module Cheffish
|
6
|
+
class ChefProviderBase < Chef::Provider::LWRPBase
|
7
|
+
def rest
|
8
|
+
@rest ||= CheffishServerAPI.new(new_resource.chef_server)
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_resource_exists?
|
12
|
+
Array(current_resource.action) != [ :delete ]
|
13
|
+
end
|
14
|
+
|
15
|
+
def not_found_resource
|
16
|
+
resource = resource_class.new(new_resource.name)
|
17
|
+
resource.action :delete
|
18
|
+
resource
|
19
|
+
end
|
20
|
+
|
21
|
+
def normalize_for_put(json)
|
22
|
+
data_handler.normalize_for_put(json, fake_entry)
|
23
|
+
end
|
24
|
+
|
25
|
+
def normalize_for_post(json)
|
26
|
+
data_handler.normalize_for_post(json, fake_entry)
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_json
|
30
|
+
@new_json ||= begin
|
31
|
+
if new_resource.complete
|
32
|
+
result = normalize(resource_to_json(new_resource))
|
33
|
+
else
|
34
|
+
# If resource is incomplete, use current json to fill any holes
|
35
|
+
result = current_json.merge(resource_to_json(new_resource))
|
36
|
+
end
|
37
|
+
augment_new_json(result)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Meant to be overridden
|
42
|
+
def augment_new_json(json)
|
43
|
+
json
|
44
|
+
end
|
45
|
+
|
46
|
+
def current_json
|
47
|
+
@current_json ||= begin
|
48
|
+
result = normalize(resource_to_json(current_resource))
|
49
|
+
result = augment_current_json(result)
|
50
|
+
result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Meant to be overridden
|
55
|
+
def augment_current_json(json)
|
56
|
+
json
|
57
|
+
end
|
58
|
+
|
59
|
+
def resource_to_json(resource)
|
60
|
+
json = resource.raw_json || {}
|
61
|
+
keys.each do |json_key, resource_key|
|
62
|
+
value = resource.send(resource_key)
|
63
|
+
json[json_key] = value if value
|
64
|
+
end
|
65
|
+
json
|
66
|
+
end
|
67
|
+
|
68
|
+
def json_to_resource(json)
|
69
|
+
resource = resource_class.new(new_resource.name)
|
70
|
+
keys.each do |json_key, resource_key|
|
71
|
+
resource.send(resource_key, json[json_key])
|
72
|
+
end
|
73
|
+
resource
|
74
|
+
end
|
75
|
+
|
76
|
+
def normalize(json)
|
77
|
+
data_handler.normalize(json, fake_entry)
|
78
|
+
end
|
79
|
+
|
80
|
+
def json_differences(old_json, new_json, print_values=true, name = '', result = nil)
|
81
|
+
result ||= []
|
82
|
+
json_differences_internal(old_json, new_json, print_values, name, result)
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
def json_differences_internal(old_json, new_json, print_values, name, result)
|
87
|
+
if old_json.kind_of?(Hash) && new_json.kind_of?(Hash)
|
88
|
+
removed_keys = old_json.keys.inject({}) { |hash, key| hash[key] = true; hash }
|
89
|
+
new_json.each_pair do |new_key, new_value|
|
90
|
+
if old_json.has_key?(new_key)
|
91
|
+
removed_keys.delete(new_key)
|
92
|
+
if new_value != old_json[new_key]
|
93
|
+
json_differences_internal(old_json[new_key], new_value, print_values, name == '' ? new_key : "#{name}.#{new_key}", result)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
if print_values
|
97
|
+
result << "add #{name == '' ? new_key : "#{name}.#{new_key}"} = #{new_value.inspect}"
|
98
|
+
else
|
99
|
+
result << "add #{name == '' ? new_key : "#{name}.#{new_key}"}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
removed_keys.keys.each do |removed_key|
|
104
|
+
result << "remove #{name == '' ? removed_key : "#{name}.#{removed_key}"}"
|
105
|
+
end
|
106
|
+
else
|
107
|
+
old_json = old_json.to_s if old_json.kind_of?(Symbol)
|
108
|
+
new_json = new_json.to_s if new_json.kind_of?(Symbol)
|
109
|
+
if old_json != new_json
|
110
|
+
if print_values
|
111
|
+
result << "update #{name} from #{old_json.inspect} to #{new_json.inspect}"
|
112
|
+
else
|
113
|
+
result << "update #{name}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def apply_modifiers(modifiers, json)
|
120
|
+
return json if !modifiers || modifiers.size == 0
|
121
|
+
|
122
|
+
# If the attributes have nothing, set them to {} so we have something to add to
|
123
|
+
if json
|
124
|
+
json = Marshal.load(Marshal.dump(json)) # Deep copy
|
125
|
+
else
|
126
|
+
json = {}
|
127
|
+
end
|
128
|
+
|
129
|
+
modifiers.each do |path, value|
|
130
|
+
path = [path] if !path.kind_of?(Array)
|
131
|
+
path = path.map { |path_part| path_part.to_s }
|
132
|
+
parent = path[0..-2].inject(json) { |hash, path_part| hash ? hash[path_part] : nil }
|
133
|
+
existing_value = parent ? parent[path[-1]] : nil
|
134
|
+
|
135
|
+
if value.is_a?(Proc)
|
136
|
+
value = value.call(existing_value)
|
137
|
+
end
|
138
|
+
if value == :delete
|
139
|
+
parent.delete(path[-1]) if parent
|
140
|
+
# TODO clean up parent chain if hash is completely emptied
|
141
|
+
else
|
142
|
+
if !parent
|
143
|
+
# Create parent if necessary
|
144
|
+
parent = path[0..-2].inject(json) do |hash, path_part|
|
145
|
+
hash[path_part] = {} if !hash[path_part]
|
146
|
+
hash[path_part]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
parent[path[-1]] = value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
json
|
153
|
+
end
|
154
|
+
|
155
|
+
def apply_run_list_modifiers(add_to_run_list, delete_from_run_list, run_list)
|
156
|
+
return run_list if (!add_to_run_list || add_to_run_list.size == 0) && (!delete_from_run_list || !delete_from_run_list.size)
|
157
|
+
delete_from_run_list ||= []
|
158
|
+
add_to_run_list ||= []
|
159
|
+
|
160
|
+
run_list = Chef::RunList.new(*run_list)
|
161
|
+
|
162
|
+
result = []
|
163
|
+
add_to_run_list_index = 0
|
164
|
+
run_list_index = 0
|
165
|
+
while run_list_index < run_list.run_list_items.size do
|
166
|
+
# See if the desired run list has this item
|
167
|
+
found_desired = add_to_run_list.index { |item| same_run_list_item(item, run_list[run_list_index]) }
|
168
|
+
if found_desired
|
169
|
+
# If so, copy all items up to that desired run list (to preserve order).
|
170
|
+
# If a run list item is out of order (run_list = X, B, Y, A, Z and desired = A, B)
|
171
|
+
# then this will give us X, A, B. When A is found later, nothing will be copied
|
172
|
+
# because found_desired will be less than add_to_run_list_index. The result will
|
173
|
+
# be X, A, B, Y, Z.
|
174
|
+
if found_desired >= add_to_run_list_index
|
175
|
+
result += add_to_run_list[add_to_run_list_index..found_desired].map { |item| item.to_s }
|
176
|
+
add_to_run_list_index = found_desired+1
|
177
|
+
end
|
178
|
+
else
|
179
|
+
# If not, just copy it in
|
180
|
+
unless delete_from_run_list.index { |item| same_run_list_item(item, run_list[run_list_index]) }
|
181
|
+
result << run_list[run_list_index].to_s
|
182
|
+
end
|
183
|
+
end
|
184
|
+
run_list_index += 1
|
185
|
+
end
|
186
|
+
|
187
|
+
# Copy any remaining desired items at the end
|
188
|
+
result += add_to_run_list[add_to_run_list_index..-1].map { |item| item.to_s }
|
189
|
+
result
|
190
|
+
end
|
191
|
+
|
192
|
+
def same_run_list_item(a, b)
|
193
|
+
a_name = a.name
|
194
|
+
b_name = b.name
|
195
|
+
# Handle "a::default" being the same as "a"
|
196
|
+
if a.type == :recipe && a_name =~ /(.+)::default$/
|
197
|
+
a_name = $1
|
198
|
+
elsif b.type == :recipe && b_name =~ /(.+)::default$/
|
199
|
+
b_name = $1
|
200
|
+
end
|
201
|
+
|
202
|
+
a_name == b_name && a.type == b.type # We want to replace things with same name and different version
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
# Needed to be able to use DataHandler classes
|
208
|
+
def fake_entry
|
209
|
+
FakeEntry.new("#{new_resource.send(keys.values.first)}.json")
|
210
|
+
end
|
211
|
+
|
212
|
+
class FakeEntry
|
213
|
+
def initialize(name, parent = nil)
|
214
|
+
@name = name
|
215
|
+
@parent = parent
|
216
|
+
end
|
217
|
+
|
218
|
+
attr_reader :name
|
219
|
+
attr_reader :parent
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'chef/http'
|
2
|
+
require 'chef/http/authenticator'
|
3
|
+
require 'chef/http/cookie_manager'
|
4
|
+
require 'chef/http/decompressor'
|
5
|
+
require 'chef/http/json_input'
|
6
|
+
require 'chef/http/json_output'
|
7
|
+
|
8
|
+
module Cheffish
|
9
|
+
# Just like ServerAPI, except it does not default the server URL or options
|
10
|
+
class CheffishServerAPI < Chef::HTTP
|
11
|
+
def initialize(chef_server)
|
12
|
+
super(chef_server[:chef_server_url], chef_server[:options] || {})
|
13
|
+
end
|
14
|
+
|
15
|
+
use Chef::HTTP::JSONInput
|
16
|
+
use Chef::HTTP::JSONOutput
|
17
|
+
use Chef::HTTP::CookieManager
|
18
|
+
use Chef::HTTP::Decompressor
|
19
|
+
use Chef::HTTP::Authenticator
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Cheffish
|
2
|
+
class InlineResource
|
3
|
+
def initialize(provider)
|
4
|
+
@provider = provider
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :provider
|
8
|
+
|
9
|
+
def run_context
|
10
|
+
provider.run_context
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_symbol, *args, &block)
|
14
|
+
# Stolen ruthlessly from Chef's chef/dsl/recipe.rb
|
15
|
+
|
16
|
+
# Checks the new platform => short_name => resource mapping initially
|
17
|
+
# then fall back to the older approach (Chef::Resource.const_get) for
|
18
|
+
# backward compatibility
|
19
|
+
resource_class = Chef::Resource.resource_for_node(method_symbol, provider.run_context.node)
|
20
|
+
|
21
|
+
super unless resource_class
|
22
|
+
raise ArgumentError, "You must supply a name when declaring a #{method_symbol} resource" unless args.size > 0
|
23
|
+
|
24
|
+
# If we have a resource like this one, we want to steal its state
|
25
|
+
args << run_context
|
26
|
+
resource = resource_class.new(*args)
|
27
|
+
resource.source_line = caller[0]
|
28
|
+
resource.load_prior_resource
|
29
|
+
resource.cookbook_name = provider.cookbook_name
|
30
|
+
resource.recipe_name = @recipe_name
|
31
|
+
resource.params = @params
|
32
|
+
# Determine whether this resource is being created in the context of an enclosing Provider
|
33
|
+
resource.enclosing_provider = provider.is_a?(Chef::Provider) ? provider : nil
|
34
|
+
# Evaluate resource attribute DSL
|
35
|
+
resource.instance_eval(&block) if block
|
36
|
+
|
37
|
+
# Run optional resource hook
|
38
|
+
resource.after_created
|
39
|
+
|
40
|
+
# Do NOT put this in the resource collection.
|
41
|
+
#run_context.resource_collection.insert(resource)
|
42
|
+
|
43
|
+
# Instead, run the action directly.
|
44
|
+
Array(resource.action).each do |action|
|
45
|
+
resource.updated_by_last_action(false)
|
46
|
+
run_provider_action(resource.provider_for_action(action))
|
47
|
+
provider.new_resource.updated_by_last_action(true) if resource.updated_by_last_action?
|
48
|
+
end
|
49
|
+
resource
|
50
|
+
end
|
51
|
+
|
52
|
+
# Do Chef::Provider.run_action, but without events
|
53
|
+
def run_provider_action(inline_provider)
|
54
|
+
if !inline_provider.whyrun_supported?
|
55
|
+
raise "#{inline_provider} is not why-run-safe. Only why-run-safe resources are supported in inline_resource."
|
56
|
+
end
|
57
|
+
|
58
|
+
# Blatantly ripped off from chef/provider run_action
|
59
|
+
|
60
|
+
# TODO: it would be preferable to get the action to be executed in the
|
61
|
+
# constructor...
|
62
|
+
|
63
|
+
# user-defined LWRPs may include unsafe load_current_resource methods that cannot be run in whyrun mode
|
64
|
+
inline_provider.load_current_resource
|
65
|
+
inline_provider.define_resource_requirements
|
66
|
+
inline_provider.process_resource_requirements
|
67
|
+
|
68
|
+
# user-defined providers including LWRPs may
|
69
|
+
# not include whyrun support - if they don't support it
|
70
|
+
# we can't execute any actions while we're running in
|
71
|
+
# whyrun mode. Instead we 'fake' whyrun by documenting that
|
72
|
+
# we can't execute the action.
|
73
|
+
# in non-whyrun mode, this will still cause the action to be
|
74
|
+
# executed normally.
|
75
|
+
if inline_provider.whyrun_supported? && !inline_provider.requirements.action_blocked?(@action)
|
76
|
+
inline_provider.send("action_#{inline_provider.action}")
|
77
|
+
elsif !inline_provider.whyrun_mode?
|
78
|
+
inline_provider.send("action_#{inline_provider.action}")
|
79
|
+
end
|
80
|
+
|
81
|
+
if inline_provider.resource_updated?
|
82
|
+
inline_provider.new_resource.updated_by_last_action(true)
|
83
|
+
end
|
84
|
+
|
85
|
+
inline_provider.cleanup_after_converge
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'etc'
|
4
|
+
require 'socket'
|
5
|
+
require 'digest/md5'
|
6
|
+
|
7
|
+
module Cheffish
|
8
|
+
class KeyFormatter
|
9
|
+
# Returns nil or key, format
|
10
|
+
def self.decode(str, pass_phrase=nil, filename='')
|
11
|
+
key_format = {}
|
12
|
+
key_format[:format] = format_of(str)
|
13
|
+
|
14
|
+
case key_format[:format]
|
15
|
+
when :openssh
|
16
|
+
key = decode_openssh_key(str, filename)
|
17
|
+
else
|
18
|
+
begin
|
19
|
+
key = OpenSSL::PKey.read(str) { pass_phrase }
|
20
|
+
rescue
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
key_format[:type] = type_of(key)
|
26
|
+
key_format[:size] = size_of(key)
|
27
|
+
key_format[:pass_phrase] = pass_phrase if pass_phrase
|
28
|
+
# TODO cipher, exponent
|
29
|
+
|
30
|
+
[key, key_format]
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.encode(key, key_format)
|
34
|
+
format = key_format[:format] || :pem
|
35
|
+
case format
|
36
|
+
when :openssh
|
37
|
+
encode_openssh_key(key)
|
38
|
+
when :pem
|
39
|
+
if key_format[:pass_phrase]
|
40
|
+
cipher = key_format[:cipher] || 'DES-EDE3-CBC'
|
41
|
+
key.to_pem(OpenSSL::Cipher.new(cipher), key_format[:pass_phrase])
|
42
|
+
else
|
43
|
+
key.to_pem
|
44
|
+
end
|
45
|
+
when :der
|
46
|
+
key.to_der
|
47
|
+
when :fingerprint
|
48
|
+
hexes = Digest::MD5.hexdigest(key.to_der)
|
49
|
+
# Put : between every pair of hexes
|
50
|
+
hexes.scan(/../).join(':')
|
51
|
+
else
|
52
|
+
raise "Unrecognized key format #{format}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def self.encode_openssh_key(key)
|
59
|
+
# TODO there really isn't a method somewhere in net/ssh or openssl that does this??
|
60
|
+
type = key.ssh_type
|
61
|
+
data = [ key.to_blob ].pack('m0')
|
62
|
+
"#{type} #{data} #{Etc.getlogin}@#{Socket.gethostname}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.decode_openssh_key(str, filename='')
|
66
|
+
Net::SSH::KeyFactory.load_data_public_key(str, filename)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.format_of(key_contents)
|
70
|
+
if key_contents.start_with?('-----BEGIN ')
|
71
|
+
:pem
|
72
|
+
elsif key_contents.start_with?('ssh-rsa ') || key_contents.start_with?('ssh-dss ')
|
73
|
+
:openssh
|
74
|
+
else
|
75
|
+
:der
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.type_of(key)
|
80
|
+
case key.class
|
81
|
+
when OpenSSL::PKey::RSA
|
82
|
+
:rsa
|
83
|
+
when OpenSSL::PKey::DSA
|
84
|
+
:dsa
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.size_of(key)
|
89
|
+
# TODO DSA -- this is RSA only
|
90
|
+
key.n.num_bytes * 8
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'cheffish'
|
2
|
+
|
3
|
+
require 'chef_zero/server'
|
4
|
+
require 'chef/chef_fs/chef_fs_data_store'
|
5
|
+
require 'chef/chef_fs/config'
|
6
|
+
|
7
|
+
class Chef
|
8
|
+
class Recipe
|
9
|
+
def with_chef_data_bag(name)
|
10
|
+
old_enclosing_data_bag = Cheffish.enclosing_data_bag
|
11
|
+
Cheffish.enclosing_data_bag = name
|
12
|
+
if block_given?
|
13
|
+
begin
|
14
|
+
yield
|
15
|
+
ensure
|
16
|
+
Cheffish.enclosing_data_bag = old_enclosing_data_bag
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_chef_environment(name)
|
22
|
+
old_enclosing_environment = Cheffish.enclosing_environment
|
23
|
+
Cheffish.enclosing_environment = name
|
24
|
+
if block_given?
|
25
|
+
begin
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
Cheffish.enclosing_environment = old_enclosing_environment
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def with_chef_data_bag_item_encryption(encryption_options)
|
34
|
+
old_enclosing_data_bag_item_encryption = Cheffish.enclosing_data_bag_item_encryption
|
35
|
+
Cheffish.enclosing_data_bag_item_encryption = encryption_options
|
36
|
+
if block_given?
|
37
|
+
begin
|
38
|
+
yield
|
39
|
+
ensure
|
40
|
+
Cheffish.enclosing_data_bag_item_encryption = old_enclosing_data_bag_item_encryption
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def with_chef_server(server_url, options = {})
|
46
|
+
old_enclosing_chef_server = Cheffish.enclosing_chef_server
|
47
|
+
Cheffish.enclosing_chef_server = { :chef_server_url => server_url, :options => options }
|
48
|
+
if block_given?
|
49
|
+
begin
|
50
|
+
yield
|
51
|
+
ensure
|
52
|
+
Cheffish.enclosing_chef_server = old_enclosing_chef_server
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_chef_local_server(options, &block)
|
58
|
+
options[:host] ||= '127.0.0.1'
|
59
|
+
options[:log_level] ||= Chef::Log.level
|
60
|
+
options[:port] ||= 8900
|
61
|
+
|
62
|
+
# Create the data store chef-zero will use
|
63
|
+
options[:data_store] ||= begin
|
64
|
+
if !options[:chef_repo_path]
|
65
|
+
raise "chef_repo_path must be specified to with_chef_local_server"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ensure all paths are given
|
69
|
+
%w(acl client cookbook container data_bag environment group node role).each do |type|
|
70
|
+
options["#{type}_path".to_sym] ||= begin
|
71
|
+
if options[:chef_repo_path].kind_of?(String)
|
72
|
+
Chef::Config.path_join(options[:chef_repo_path], "#{type}s")
|
73
|
+
else
|
74
|
+
options[:chef_repo_path].map { |path| Chef::Config.path_join(path, "#{type}s")}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
# Work around issue in earlier versions of ChefFS where it expects strings for these
|
78
|
+
# instead of symbols
|
79
|
+
options["#{type}_path"] = options["#{type}_path".to_sym]
|
80
|
+
end
|
81
|
+
|
82
|
+
chef_fs = Chef::ChefFS::Config.new(options).local_fs
|
83
|
+
chef_fs.write_pretty_json = true
|
84
|
+
Chef::ChefFS::ChefFSDataStore.new(chef_fs)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Start the chef-zero server
|
88
|
+
Chef::Log.info("Starting chef-zero on port #{options[:port]} with repository at #{options[:data_store].chef_fs.fs_description}")
|
89
|
+
chef_zero_server = ChefZero::Server.new(options)
|
90
|
+
chef_zero_server.start_background
|
91
|
+
|
92
|
+
@@local_servers ||= []
|
93
|
+
@@local_servers << chef_zero_server
|
94
|
+
|
95
|
+
with_chef_server(chef_zero_server.url, &block)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|