chef-zero 15.0.17 → 15.0.21
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 +4 -4
- data/Gemfile +36 -31
- data/LICENSE +201 -201
- data/Rakefile +73 -68
- data/bin/chef-zero +111 -111
- data/chef-zero.gemspec +34 -33
- data/lib/chef_zero/chef_data/acl_path.rb +140 -140
- data/lib/chef_zero/chef_data/cookbook_data.rb +237 -237
- data/lib/chef_zero/chef_data/data_normalizer.rb +276 -276
- data/lib/chef_zero/chef_data/default_creator.rb +476 -476
- data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -29
- data/lib/chef_zero/data_store/data_error.rb +32 -32
- data/lib/chef_zero/data_store/data_not_found_error.rb +29 -29
- data/lib/chef_zero/data_store/default_facade.rb +143 -147
- data/lib/chef_zero/data_store/interface_v1.rb +67 -67
- data/lib/chef_zero/data_store/interface_v2.rb +18 -18
- data/lib/chef_zero/data_store/memory_store.rb +33 -33
- data/lib/chef_zero/data_store/memory_store_v2.rb +159 -159
- data/lib/chef_zero/data_store/raw_file_store.rb +143 -143
- data/lib/chef_zero/data_store/v1_to_v2_adapter.rb +150 -150
- data/lib/chef_zero/data_store/v2_to_v1_adapter.rb +105 -105
- data/lib/chef_zero/dist.rb +9 -9
- data/lib/chef_zero/endpoints/acl_endpoint.rb +39 -39
- data/lib/chef_zero/endpoints/acls_endpoint.rb +41 -41
- data/lib/chef_zero/endpoints/actor_default_key_endpoint.rb +78 -78
- data/lib/chef_zero/endpoints/actor_endpoint.rb +184 -184
- data/lib/chef_zero/endpoints/actor_key_endpoint.rb +62 -62
- data/lib/chef_zero/endpoints/actor_keys_endpoint.rb +129 -129
- data/lib/chef_zero/endpoints/actors_endpoint.rb +104 -104
- data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +32 -32
- data/lib/chef_zero/endpoints/container_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/containers_endpoint.rb +25 -25
- data/lib/chef_zero/endpoints/controls_endpoint.rb +16 -16
- data/lib/chef_zero/endpoints/cookbook_artifact_endpoint.rb +24 -24
- data/lib/chef_zero/endpoints/cookbook_artifact_identifier_endpoint.rb +68 -68
- data/lib/chef_zero/endpoints/cookbook_artifacts_endpoint.rb +34 -34
- data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -39
- data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +136 -136
- data/lib/chef_zero/endpoints/cookbooks_base.rb +80 -80
- data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +19 -19
- data/lib/chef_zero/endpoints/data_bag_endpoint.rb +45 -45
- data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -25
- data/lib/chef_zero/endpoints/data_bags_endpoint.rb +23 -23
- data/lib/chef_zero/endpoints/dummy_endpoint.rb +29 -29
- data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -24
- data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +126 -126
- data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -33
- data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -23
- data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/environment_role_endpoint.rb +36 -36
- data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/group_endpoint.rb +20 -20
- data/lib/chef_zero/endpoints/groups_endpoint.rb +13 -13
- data/lib/chef_zero/endpoints/license_endpoint.rb +25 -25
- data/lib/chef_zero/endpoints/node_endpoint.rb +34 -34
- data/lib/chef_zero/endpoints/node_identifiers_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/nodes_endpoint.rb +34 -34
- data/lib/chef_zero/endpoints/not_found_endpoint.rb +11 -11
- data/lib/chef_zero/endpoints/organization_association_request_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/organization_association_requests_endpoint.rb +30 -30
- data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -26
- data/lib/chef_zero/endpoints/organization_endpoint.rb +47 -47
- data/lib/chef_zero/endpoints/organization_user_base.rb +15 -15
- data/lib/chef_zero/endpoints/organization_user_default_key_endpoint.rb +16 -16
- data/lib/chef_zero/endpoints/organization_user_endpoint.rb +26 -26
- data/lib/chef_zero/endpoints/organization_user_key_endpoint.rb +17 -17
- data/lib/chef_zero/endpoints/organization_user_keys_endpoint.rb +17 -17
- data/lib/chef_zero/endpoints/organization_users_endpoint.rb +43 -43
- data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -20
- data/lib/chef_zero/endpoints/organizations_endpoint.rb +61 -61
- data/lib/chef_zero/endpoints/policies_endpoint.rb +26 -26
- data/lib/chef_zero/endpoints/policy_endpoint.rb +24 -24
- data/lib/chef_zero/endpoints/policy_group_endpoint.rb +46 -46
- data/lib/chef_zero/endpoints/policy_group_policy_endpoint.rb +83 -83
- data/lib/chef_zero/endpoints/policy_groups_endpoint.rb +38 -38
- data/lib/chef_zero/endpoints/policy_revision_endpoint.rb +66 -66
- data/lib/chef_zero/endpoints/policy_revisions_endpoint.rb +15 -15
- data/lib/chef_zero/endpoints/principal_endpoint.rb +55 -55
- data/lib/chef_zero/endpoints/rest_list_endpoint.rb +42 -42
- data/lib/chef_zero/endpoints/rest_object_endpoint.rb +78 -78
- data/lib/chef_zero/endpoints/role_endpoint.rb +16 -16
- data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -14
- data/lib/chef_zero/endpoints/sandbox_endpoint.rb +27 -27
- data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +51 -51
- data/lib/chef_zero/endpoints/search_endpoint.rb +208 -208
- data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -18
- data/lib/chef_zero/endpoints/server_api_version_endpoint.rb +14 -14
- data/lib/chef_zero/endpoints/system_recovery_endpoint.rb +30 -30
- data/lib/chef_zero/endpoints/universe_endpoint.rb +15 -15
- data/lib/chef_zero/endpoints/user_association_request_endpoint.rb +41 -41
- data/lib/chef_zero/endpoints/user_association_requests_count_endpoint.rb +19 -19
- data/lib/chef_zero/endpoints/user_association_requests_endpoint.rb +19 -19
- data/lib/chef_zero/endpoints/user_organizations_endpoint.rb +22 -22
- data/lib/chef_zero/endpoints/version_endpoint.rb +13 -13
- data/lib/chef_zero/log.rb +7 -7
- data/lib/chef_zero/rest_base.rb +332 -332
- data/lib/chef_zero/rest_error_response.rb +11 -11
- data/lib/chef_zero/rest_request.rb +84 -88
- data/lib/chef_zero/rest_router.rb +72 -72
- data/lib/chef_zero/rspec.rb +355 -355
- data/lib/chef_zero/server.rb +730 -730
- data/lib/chef_zero/socketless_server_map.rb +92 -93
- data/lib/chef_zero/solr/query/binary_operator.rb +52 -52
- data/lib/chef_zero/solr/query/phrase.rb +23 -23
- data/lib/chef_zero/solr/query/range_query.rb +46 -46
- data/lib/chef_zero/solr/query/regexpable_query.rb +30 -30
- data/lib/chef_zero/solr/query/subquery.rb +37 -37
- data/lib/chef_zero/solr/query/term.rb +45 -45
- data/lib/chef_zero/solr/query/unary_operator.rb +41 -41
- data/lib/chef_zero/solr/solr_doc.rb +53 -53
- data/lib/chef_zero/solr/solr_parser.rb +208 -208
- data/lib/chef_zero/version.rb +3 -3
- data/lib/chef_zero.rb +10 -10
- data/spec/run_oc_pedant.rb +226 -226
- data/spec/search_spec.rb +36 -36
- data/spec/server_spec.rb +96 -96
- data/spec/socketless_server_map_spec.rb +74 -74
- data/spec/support/oc_pedant.rb +149 -149
- data/spec/support/secrets.json +6 -6
- data/spec/support/stickywicket.pem +27 -27
- metadata +35 -18
data/lib/chef_zero/server.rb
CHANGED
@@ -1,730 +1,730 @@
|
|
1
|
-
#
|
2
|
-
# Author:: John Keiser (<jkeiser@opscode.com>)
|
3
|
-
# Copyright:: Copyright (c) 2012-2017, Chef Software Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# https://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require "openssl" unless defined?(OpenSSL)
|
20
|
-
require "open-uri" unless defined?(OpenURI)
|
21
|
-
require "rubygems" unless defined?(Gem)
|
22
|
-
require "timeout" unless defined?(Timeout)
|
23
|
-
require "stringio" unless defined?(StringIO)
|
24
|
-
|
25
|
-
require "rack" unless defined?(Rack)
|
26
|
-
require "rackup" unless defined?(Rackup)
|
27
|
-
require "webrick" unless defined?(WEBrick)
|
28
|
-
require "webrick/https"
|
29
|
-
|
30
|
-
require_relative "../chef_zero"
|
31
|
-
require_relative "socketless_server_map"
|
32
|
-
require_relative "chef_data/cookbook_data"
|
33
|
-
require_relative "chef_data/acl_path"
|
34
|
-
require_relative "rest_router"
|
35
|
-
require_relative "data_store/memory_store_v2"
|
36
|
-
require_relative "data_store/v1_to_v2_adapter"
|
37
|
-
require_relative "data_store/default_facade"
|
38
|
-
require_relative "version"
|
39
|
-
require "chef_zero/dist
|
40
|
-
|
41
|
-
require_relative "endpoints/rest_list_endpoint"
|
42
|
-
require_relative "endpoints/authenticate_user_endpoint"
|
43
|
-
require_relative "endpoints/acls_endpoint"
|
44
|
-
require_relative "endpoints/acl_endpoint"
|
45
|
-
require_relative "endpoints/actor_endpoint"
|
46
|
-
require_relative "endpoints/actors_endpoint"
|
47
|
-
require_relative "endpoints/actor_key_endpoint"
|
48
|
-
require_relative "endpoints/organization_user_key_endpoint"
|
49
|
-
require_relative "endpoints/organization_user_default_key_endpoint"
|
50
|
-
require_relative "endpoints/organization_user_keys_endpoint"
|
51
|
-
require_relative "endpoints/actor_default_key_endpoint"
|
52
|
-
require_relative "endpoints/actor_keys_endpoint"
|
53
|
-
require_relative "endpoints/cookbooks_endpoint"
|
54
|
-
require_relative "endpoints/cookbook_endpoint"
|
55
|
-
require_relative "endpoints/cookbook_version_endpoint"
|
56
|
-
require_relative "endpoints/cookbook_artifacts_endpoint"
|
57
|
-
require_relative "endpoints/cookbook_artifact_endpoint"
|
58
|
-
require_relative "endpoints/cookbook_artifact_identifier_endpoint"
|
59
|
-
require_relative "endpoints/containers_endpoint"
|
60
|
-
require_relative "endpoints/container_endpoint"
|
61
|
-
require_relative "endpoints/controls_endpoint"
|
62
|
-
require_relative "endpoints/dummy_endpoint"
|
63
|
-
require_relative "endpoints/data_bags_endpoint"
|
64
|
-
require_relative "endpoints/data_bag_endpoint"
|
65
|
-
require_relative "endpoints/data_bag_item_endpoint"
|
66
|
-
require_relative "endpoints/groups_endpoint"
|
67
|
-
require_relative "endpoints/group_endpoint"
|
68
|
-
require_relative "endpoints/environment_endpoint"
|
69
|
-
require_relative "endpoints/environment_cookbooks_endpoint"
|
70
|
-
require_relative "endpoints/environment_cookbook_endpoint"
|
71
|
-
require_relative "endpoints/environment_cookbook_versions_endpoint"
|
72
|
-
require_relative "endpoints/environment_nodes_endpoint"
|
73
|
-
require_relative "endpoints/environment_recipes_endpoint"
|
74
|
-
require_relative "endpoints/environment_role_endpoint"
|
75
|
-
require_relative "endpoints/license_endpoint"
|
76
|
-
require_relative "endpoints/node_endpoint"
|
77
|
-
require_relative "endpoints/nodes_endpoint"
|
78
|
-
require_relative "endpoints/node_identifiers_endpoint"
|
79
|
-
require_relative "endpoints/organizations_endpoint"
|
80
|
-
require_relative "endpoints/organization_endpoint"
|
81
|
-
require_relative "endpoints/organization_association_requests_endpoint"
|
82
|
-
require_relative "endpoints/organization_association_request_endpoint"
|
83
|
-
require_relative "endpoints/organization_authenticate_user_endpoint"
|
84
|
-
require_relative "endpoints/organization_users_endpoint"
|
85
|
-
require_relative "endpoints/organization_user_endpoint"
|
86
|
-
require_relative "endpoints/organization_validator_key_endpoint"
|
87
|
-
require_relative "endpoints/policies_endpoint"
|
88
|
-
require_relative "endpoints/policy_endpoint"
|
89
|
-
require_relative "endpoints/policy_revisions_endpoint"
|
90
|
-
require_relative "endpoints/policy_revision_endpoint"
|
91
|
-
require_relative "endpoints/policy_groups_endpoint"
|
92
|
-
require_relative "endpoints/policy_group_endpoint"
|
93
|
-
require_relative "endpoints/policy_group_policy_endpoint"
|
94
|
-
require_relative "endpoints/principal_endpoint"
|
95
|
-
require_relative "endpoints/role_endpoint"
|
96
|
-
require_relative "endpoints/role_environments_endpoint"
|
97
|
-
require_relative "endpoints/sandboxes_endpoint"
|
98
|
-
require_relative "endpoints/sandbox_endpoint"
|
99
|
-
require_relative "endpoints/searches_endpoint"
|
100
|
-
require_relative "endpoints/search_endpoint"
|
101
|
-
require_relative "endpoints/system_recovery_endpoint"
|
102
|
-
require_relative "endpoints/user_association_requests_endpoint"
|
103
|
-
require_relative "endpoints/user_association_requests_count_endpoint"
|
104
|
-
require_relative "endpoints/user_association_request_endpoint"
|
105
|
-
require_relative "endpoints/user_organizations_endpoint"
|
106
|
-
require_relative "endpoints/file_store_file_endpoint"
|
107
|
-
require_relative "endpoints/not_found_endpoint"
|
108
|
-
require_relative "endpoints/version_endpoint"
|
109
|
-
require_relative "endpoints/server_api_version_endpoint"
|
110
|
-
require_relative "endpoints/universe_endpoint"
|
111
|
-
|
112
|
-
module ChefZero
|
113
|
-
|
114
|
-
class Server
|
115
|
-
|
116
|
-
DEFAULT_OPTIONS = {
|
117
|
-
host: ["127.0.0.1"],
|
118
|
-
port: 8889,
|
119
|
-
log_level: :warn,
|
120
|
-
generate_real_keys: true,
|
121
|
-
single_org: "chef",
|
122
|
-
ssl: false,
|
123
|
-
}.freeze
|
124
|
-
|
125
|
-
GLOBAL_ENDPOINTS = [
|
126
|
-
"/license",
|
127
|
-
"/version",
|
128
|
-
"/server_api_version",
|
129
|
-
].freeze
|
130
|
-
|
131
|
-
def initialize(options = {})
|
132
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
133
|
-
if @options[:single_org] && !@options.key?(:osc_compat)
|
134
|
-
@options[:osc_compat] = true
|
135
|
-
end
|
136
|
-
@options.freeze
|
137
|
-
ChefZero::Log.level = @options[:log_level].to_sym
|
138
|
-
@app = nil
|
139
|
-
end
|
140
|
-
|
141
|
-
# @return [Hash]
|
142
|
-
attr_reader :options
|
143
|
-
|
144
|
-
# @return [Integer]
|
145
|
-
def port
|
146
|
-
if @port
|
147
|
-
@port
|
148
|
-
# If options[:port] is not an Array or an Enumerable, it is just an Integer.
|
149
|
-
elsif !options[:port].respond_to?(:each)
|
150
|
-
options[:port]
|
151
|
-
else
|
152
|
-
raise "port cannot be determined until server is started"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# @return [WEBrick::HTTPServer]
|
157
|
-
attr_reader :server
|
158
|
-
|
159
|
-
include ChefZero::Endpoints
|
160
|
-
|
161
|
-
#
|
162
|
-
# The URL for this Chef Zero server. If the given host is an IPV6 address,
|
163
|
-
# it is escaped in brackets according to RFC-2732.
|
164
|
-
#
|
165
|
-
# @see http://www.ietf.org/rfc/rfc2732.txt RFC-2732
|
166
|
-
#
|
167
|
-
# @return [String]
|
168
|
-
#
|
169
|
-
def url
|
170
|
-
sch = @options[:ssl] ? "https" : "http"
|
171
|
-
hosts = Array(@options[:host])
|
172
|
-
@url ||= if hosts.first.include?(":")
|
173
|
-
URI("#{sch}://[#{hosts.first}]:#{port}").to_s
|
174
|
-
else
|
175
|
-
URI("#{sch}://#{hosts.first}:#{port}").to_s
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def local_mode_url
|
180
|
-
raise "Port not yet set, cannot generate URL" unless port.is_a?(Integer)
|
181
|
-
|
182
|
-
"chefzero://localhost:#{port}"
|
183
|
-
end
|
184
|
-
|
185
|
-
#
|
186
|
-
# The data store for this server (default is in-memory).
|
187
|
-
#
|
188
|
-
# @return [ChefZero::DataStore]
|
189
|
-
#
|
190
|
-
def data_store
|
191
|
-
@data_store ||= begin
|
192
|
-
result = @options[:data_store] || DataStore::DefaultFacade.new(DataStore::MemoryStoreV2.new, options[:single_org], options[:osc_compat])
|
193
|
-
if options[:single_org]
|
194
|
-
|
195
|
-
if !result.respond_to?(:interface_version) || result.interface_version == 1
|
196
|
-
result = ChefZero::DataStore::V1ToV2Adapter.new(result, options[:single_org])
|
197
|
-
result = ChefZero::DataStore::DefaultFacade.new(result, options[:single_org], options[:osc_compat])
|
198
|
-
end
|
199
|
-
|
200
|
-
else
|
201
|
-
if !result.respond_to?(:interface_version) || result.interface_version == 1
|
202
|
-
raise "Multi-org not supported by data store #{result}!"
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
result
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
#
|
211
|
-
# Boolean method to determine if real Public/Private keys should be
|
212
|
-
# generated.
|
213
|
-
#
|
214
|
-
# @return [Boolean]
|
215
|
-
# true if real keys should be created, false otherwise
|
216
|
-
#
|
217
|
-
def generate_real_keys?
|
218
|
-
!!@options[:generate_real_keys]
|
219
|
-
end
|
220
|
-
|
221
|
-
#
|
222
|
-
# Start a Chef Zero server in the current thread. You can stop this server
|
223
|
-
# by canceling the current thread.
|
224
|
-
#
|
225
|
-
# @param [Boolean|IO] publish
|
226
|
-
# publish the server information to the publish parameter or to STDOUT if it's "true"
|
227
|
-
#
|
228
|
-
# @return [nil]
|
229
|
-
# this method will block the main thread until interrupted
|
230
|
-
#
|
231
|
-
def start(publish = true)
|
232
|
-
publish = publish[:publish] if publish.is_a?(Hash) # Legacy API
|
233
|
-
|
234
|
-
if publish
|
235
|
-
output = publish.respond_to?(:puts) ? publish : STDOUT
|
236
|
-
output.puts <<-EOH.gsub(/^ {10}/, "")
|
237
|
-
>> Starting #{ChefZero::Dist::PRODUCT} (v#{ChefZero::VERSION})...
|
238
|
-
EOH
|
239
|
-
end
|
240
|
-
|
241
|
-
thread = start_background
|
242
|
-
|
243
|
-
if publish
|
244
|
-
output = publish.respond_to?(:puts) ? publish : STDOUT
|
245
|
-
output.puts <<-EOH.gsub(/^ {10}/, "")
|
246
|
-
>> WEBrick (v#{WEBrick::VERSION}) on Rack (v#{Rack.release}) is listening at #{url}
|
247
|
-
>> Press CTRL+C to stop
|
248
|
-
|
249
|
-
EOH
|
250
|
-
end
|
251
|
-
|
252
|
-
%w{INT TERM}.each do |signal|
|
253
|
-
Signal.trap(signal) do
|
254
|
-
puts "\n>> Stopping #{ChefZero::Dist::PRODUCT}..."
|
255
|
-
@server.shutdown
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
# Move the background process to the main thread
|
260
|
-
thread.join
|
261
|
-
end
|
262
|
-
|
263
|
-
#
|
264
|
-
# Start a Chef Zero server in a forked process. This method returns the PID
|
265
|
-
# to the forked process.
|
266
|
-
#
|
267
|
-
# @param [Fixnum] wait
|
268
|
-
# the number of seconds to wait for the server to start
|
269
|
-
#
|
270
|
-
# @return [Thread]
|
271
|
-
# the thread the background process is running in
|
272
|
-
#
|
273
|
-
def listen(hosts, port)
|
274
|
-
hosts.each do |host|
|
275
|
-
@server.listen(host, port)
|
276
|
-
end
|
277
|
-
true
|
278
|
-
rescue Errno::EADDRINUSE
|
279
|
-
ChefZero::Log.warn("Port #{port} not available")
|
280
|
-
@server.listeners.each(&:close)
|
281
|
-
@server.listeners.clear
|
282
|
-
false
|
283
|
-
end
|
284
|
-
|
285
|
-
def start_background(wait = 5)
|
286
|
-
@server = WEBrick::HTTPServer.new(
|
287
|
-
DoNotListen: true,
|
288
|
-
AccessLog: [],
|
289
|
-
Logger: WEBrick::Log.new(StringIO.new, 7),
|
290
|
-
RequestTimeout: 300,
|
291
|
-
SSLEnable: options[:ssl],
|
292
|
-
SSLOptions: ssl_opts,
|
293
|
-
SSLCertName: [ [ "CN", WEBrick::Utils.getservername ] ],
|
294
|
-
StartCallback: proc do
|
295
|
-
@running = true
|
296
|
-
end
|
297
|
-
)
|
298
|
-
ENV["HTTPS"] = "on" if options[:ssl]
|
299
|
-
@server.mount("/", Rackup::Handler::WEBrick, app)
|
300
|
-
|
301
|
-
# Pick a port
|
302
|
-
# If options[:port] can be an Enumerator, an Array, or an Integer,
|
303
|
-
# we need something that can respond to .each (Enum and Array can already).
|
304
|
-
Array(options[:port]).each do |port|
|
305
|
-
if listen(Array(options[:host]), port)
|
306
|
-
@port = port
|
307
|
-
break
|
308
|
-
end
|
309
|
-
end
|
310
|
-
unless @port
|
311
|
-
raise Errno::EADDRINUSE,
|
312
|
-
"No port in :port range #{options[:port]} is available"
|
313
|
-
end
|
314
|
-
|
315
|
-
# Start the server in the background
|
316
|
-
@thread = Thread.new do
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
end
|
325
|
-
|
326
|
-
# Do not return until the web server is genuinely started.
|
327
|
-
sleep(0.01) while !@running && @thread.alive?
|
328
|
-
|
329
|
-
SocketlessServerMap.instance.register_port(@port, self)
|
330
|
-
|
331
|
-
@thread
|
332
|
-
end
|
333
|
-
|
334
|
-
def start_socketless
|
335
|
-
@port = SocketlessServerMap.instance.register_no_listen_server(self)
|
336
|
-
end
|
337
|
-
|
338
|
-
def handle_socketless_request(request_env)
|
339
|
-
app.call(request_env)
|
340
|
-
end
|
341
|
-
|
342
|
-
#
|
343
|
-
# Boolean method to determine if the server is currently ready to accept
|
344
|
-
# requests. This method will attempt to make an HTTP request against the
|
345
|
-
# server. If this method returns true, you are safe to make a request.
|
346
|
-
#
|
347
|
-
# @return [Boolean]
|
348
|
-
# true if the server is accepting requests, false otherwise
|
349
|
-
#
|
350
|
-
def running?
|
351
|
-
!@server.nil? && @running && @server.status == :Running
|
352
|
-
end
|
353
|
-
|
354
|
-
#
|
355
|
-
# Gracefully stop the Chef Zero server.
|
356
|
-
#
|
357
|
-
# @param [Fixnum] wait
|
358
|
-
# the number of seconds to wait before raising force-terminating the
|
359
|
-
# server
|
360
|
-
#
|
361
|
-
def stop(wait = 5)
|
362
|
-
if @running
|
363
|
-
@server.shutdown if @server
|
364
|
-
@thread.join(wait) if @thread
|
365
|
-
end
|
366
|
-
rescue Timeout::Error
|
367
|
-
if @thread
|
368
|
-
ChefZero::Log.error("#{ChefZero::Dist::PRODUCT} did not stop within #{wait} seconds! Killing...")
|
369
|
-
@thread.kill
|
370
|
-
SocketlessServerMap.deregister(port)
|
371
|
-
end
|
372
|
-
ensure
|
373
|
-
@server = nil
|
374
|
-
@thread = nil
|
375
|
-
end
|
376
|
-
|
377
|
-
def gen_key_pair
|
378
|
-
if generate_real_keys?
|
379
|
-
private_key = OpenSSL::PKey::RSA.new(2048)
|
380
|
-
public_key = private_key.public_key.to_s
|
381
|
-
public_key.sub!(/^-----BEGIN RSA PUBLIC KEY-----/, "-----BEGIN PUBLIC KEY-----")
|
382
|
-
public_key.sub!(/-----END RSA PUBLIC KEY-----(\s+)$/, '-----END PUBLIC KEY-----\1')
|
383
|
-
[private_key.to_s, public_key]
|
384
|
-
else
|
385
|
-
[PRIVATE_KEY, PUBLIC_KEY]
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
def on_request(&block)
|
390
|
-
@on_request_proc = block
|
391
|
-
end
|
392
|
-
|
393
|
-
def on_response(&block)
|
394
|
-
@on_response_proc = block
|
395
|
-
end
|
396
|
-
|
397
|
-
# Load data in a nice, friendly form:
|
398
|
-
# {
|
399
|
-
# 'roles' => {
|
400
|
-
# 'desert' => '{ "description": "Hot and dry"' },
|
401
|
-
# 'rainforest' => { "description" => 'Wet and humid' }
|
402
|
-
# },
|
403
|
-
# 'cookbooks' => {
|
404
|
-
# 'apache2-1.0.1' => {
|
405
|
-
# 'templates' => { 'default' => { 'blah.txt' => 'hi' }}
|
406
|
-
# 'recipes' => { 'default.rb' => 'template "blah.txt"' }
|
407
|
-
# 'metadata.rb' => 'depends "mysql"'
|
408
|
-
# },
|
409
|
-
# 'apache2-1.2.0' => {
|
410
|
-
# 'templates' => { 'default' => { 'blah.txt' => 'lo' }}
|
411
|
-
# 'recipes' => { 'default.rb' => 'template "blah.txt"' }
|
412
|
-
# 'metadata.rb' => 'depends "mysql"'
|
413
|
-
# },
|
414
|
-
# 'mysql' => {
|
415
|
-
# 'recipes' => { 'default.rb' => 'file { contents "hi" }' },
|
416
|
-
# 'metadata.rb' => 'version "1.0.0"'
|
417
|
-
# }
|
418
|
-
# }
|
419
|
-
# }
|
420
|
-
def load_data(contents, org_name = nil)
|
421
|
-
org_name ||= options[:single_org]
|
422
|
-
if org_name.nil? && contents.keys != [ "users" ]
|
423
|
-
raise "Must pass an org name to load_data or run in single_org mode"
|
424
|
-
end
|
425
|
-
|
426
|
-
%w{clients containers environments groups nodes roles sandboxes}.each do |data_type|
|
427
|
-
if contents[data_type]
|
428
|
-
dejsonize_children(contents[data_type]).each_pair do |name, data|
|
429
|
-
data_store.set(["organizations", org_name, data_type, name], data, :create)
|
430
|
-
end
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
if contents["users"]
|
435
|
-
dejsonize_children(contents["users"]).each_pair do |name, data|
|
436
|
-
if options[:osc_compat]
|
437
|
-
data_store.set(["organizations", org_name, "users", name], data, :create)
|
438
|
-
else
|
439
|
-
# Create the user and put them in the org
|
440
|
-
data_store.set(["users", name], data, :create)
|
441
|
-
if org_name
|
442
|
-
data_store.set(["organizations", org_name, "users", name], "{}", :create)
|
443
|
-
end
|
444
|
-
end
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
if contents["members"]
|
449
|
-
contents["members"].each do |name|
|
450
|
-
data_store.set(["organizations", org_name, "users", name], "{}", :create)
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
if contents["invites"]
|
455
|
-
contents["invites"].each do |name|
|
456
|
-
data_store.set(["organizations", org_name, "association_requests", name], "{}", :create)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
if contents["acls"]
|
461
|
-
dejsonize_children(contents["acls"]).each do |path, acl|
|
462
|
-
path = [ "organizations", org_name ] + path.split("/")
|
463
|
-
path = ChefData::AclPath.get_acl_data_path(path)
|
464
|
-
ChefZero::RSpec.server.data_store.set(path, acl)
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
if contents["data"]
|
469
|
-
contents["data"].each_pair do |key, data_bag|
|
470
|
-
data_store.create_dir(["organizations", org_name, "data"], key, :recursive)
|
471
|
-
dejsonize_children(data_bag).each do |item_name, item|
|
472
|
-
data_store.set(["organizations", org_name, "data", key, item_name], item, :create)
|
473
|
-
end
|
474
|
-
end
|
475
|
-
end
|
476
|
-
|
477
|
-
if contents["policies"]
|
478
|
-
contents["policies"].each_pair do |policy_name, policy_struct|
|
479
|
-
# data_store.create_dir(['organizations', org_name, 'policies', policy_name], "revisions", :recursive)
|
480
|
-
dejsonize_children(policy_struct).each do |revision, policy_data|
|
481
|
-
data_store.set(["organizations", org_name, "policies", policy_name,
|
482
|
-
"revisions", revision], policy_data, :create, :create_dir)
|
483
|
-
end
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
if contents["policy_groups"]
|
488
|
-
contents["policy_groups"].each_pair do |group_name, group|
|
489
|
-
group["policies"].each do |policy_name, policy_revision|
|
490
|
-
data_store.set(["organizations", org_name, "policy_groups", group_name, "policies", policy_name], FFI_Yajl::Encoder.encode(policy_revision["revision_id"], pretty: true), :create, :create_dir)
|
491
|
-
end
|
492
|
-
end
|
493
|
-
end
|
494
|
-
|
495
|
-
%w{cookbooks cookbook_artifacts}.each do |cookbook_type|
|
496
|
-
if contents[cookbook_type]
|
497
|
-
contents[cookbook_type].each_pair do |name_version, cookbook|
|
498
|
-
if cookbook_type == "cookbook_artifacts"
|
499
|
-
name, _, identifier = name_version.rpartition("-")
|
500
|
-
cookbook_data = ChefData::CookbookData.to_hash(cookbook, name, identifier)
|
501
|
-
elsif name_version =~ /(.+)-(\d+\.\d+\.\d+)$/
|
502
|
-
cookbook_data = ChefData::CookbookData.to_hash(cookbook, $1, $2)
|
503
|
-
else
|
504
|
-
cookbook_data = ChefData::CookbookData.to_hash(cookbook, name_version)
|
505
|
-
end
|
506
|
-
raise "No version specified" unless cookbook_data[:version]
|
507
|
-
|
508
|
-
data_store.create_dir(["organizations", org_name, cookbook_type], cookbook_data[:cookbook_name], :recursive)
|
509
|
-
data_store.set(["organizations", org_name, cookbook_type, cookbook_data[:cookbook_name], cookbook_data[:version]], FFI_Yajl::Encoder.encode(cookbook_data, pretty: true), :create)
|
510
|
-
cookbook_data.values.each do |files|
|
511
|
-
next unless files.is_a? Array
|
512
|
-
|
513
|
-
files.each do |file|
|
514
|
-
data_store.set(["organizations", org_name, "file_store", "checksums", file[:checksum]], get_file(cookbook, file[:path]), :create)
|
515
|
-
end
|
516
|
-
end
|
517
|
-
end
|
518
|
-
end
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
def clear_data
|
523
|
-
data_store.clear
|
524
|
-
end
|
525
|
-
|
526
|
-
def request_handler(&block)
|
527
|
-
@request_handler = block
|
528
|
-
end
|
529
|
-
|
530
|
-
def to_s
|
531
|
-
"#<#{self.class} #{url}>"
|
532
|
-
end
|
533
|
-
|
534
|
-
def inspect
|
535
|
-
"#<#{self.class} @url=#{url.inspect}>"
|
536
|
-
end
|
537
|
-
|
538
|
-
private
|
539
|
-
|
540
|
-
def endpoints
|
541
|
-
result = if options[:osc_compat]
|
542
|
-
# OSC-only
|
543
|
-
[
|
544
|
-
[ "/organizations/*/users", ActorsEndpoint.new(self) ],
|
545
|
-
[ "/organizations/*/users/*", ActorEndpoint.new(self) ],
|
546
|
-
[ "/organizations/*/authenticate_user", OrganizationAuthenticateUserEndpoint.new(self) ],
|
547
|
-
]
|
548
|
-
else
|
549
|
-
# EC-only
|
550
|
-
[
|
551
|
-
[ "/organizations/*/users", OrganizationUsersEndpoint.new(self) ],
|
552
|
-
[ "/organizations/*/users/*", OrganizationUserEndpoint.new(self) ],
|
553
|
-
[ "/users", ActorsEndpoint.new(self, "username") ],
|
554
|
-
[ "/users/*", ActorEndpoint.new(self, "username") ],
|
555
|
-
[ "/users/*/_acl", AclsEndpoint.new(self) ],
|
556
|
-
[ "/users/*/_acl/*", AclEndpoint.new(self) ],
|
557
|
-
[ "/users/*/association_requests", UserAssociationRequestsEndpoint.new(self) ],
|
558
|
-
[ "/users/*/association_requests/count", UserAssociationRequestsCountEndpoint.new(self) ],
|
559
|
-
[ "/users/*/association_requests/*", UserAssociationRequestEndpoint.new(self) ],
|
560
|
-
[ "/users/*/keys", ActorKeysEndpoint.new(self) ],
|
561
|
-
[ "/users/*/keys/default", ActorDefaultKeyEndpoint.new(self) ],
|
562
|
-
[ "/users/*/keys/*", ActorKeyEndpoint.new(self) ],
|
563
|
-
[ "/users/*/organizations", UserOrganizationsEndpoint.new(self) ],
|
564
|
-
[ "/authenticate_user", AuthenticateUserEndpoint.new(self) ],
|
565
|
-
[ "/system_recovery", SystemRecoveryEndpoint.new(self) ],
|
566
|
-
[ "/license", LicenseEndpoint.new(self) ],
|
567
|
-
[ "/organizations", OrganizationsEndpoint.new(self) ],
|
568
|
-
[ "/organizations/*", OrganizationEndpoint.new(self) ],
|
569
|
-
[ "/organizations/*/_validator_key", OrganizationValidatorKeyEndpoint.new(self) ],
|
570
|
-
[ "/organizations/*/association_requests", OrganizationAssociationRequestsEndpoint.new(self) ],
|
571
|
-
[ "/organizations/*/association_requests/*", OrganizationAssociationRequestEndpoint.new(self) ],
|
572
|
-
[ "/organizations/*/containers", ContainersEndpoint.new(self) ],
|
573
|
-
[ "/organizations/*/containers/*", ContainerEndpoint.new(self) ],
|
574
|
-
[ "/organizations/*/groups", GroupsEndpoint.new(self) ],
|
575
|
-
[ "/organizations/*/groups/*", GroupEndpoint.new(self) ],
|
576
|
-
[ "/organizations/*/organization/_acl", AclsEndpoint.new(self) ],
|
577
|
-
[ "/organizations/*/organizations/_acl", AclsEndpoint.new(self) ],
|
578
|
-
[ "/organizations/*/*/*/_acl", AclsEndpoint.new(self) ],
|
579
|
-
[ "/organizations/*/organization/_acl/*", AclEndpoint.new(self) ],
|
580
|
-
[ "/organizations/*/organizations/_acl/*", AclEndpoint.new(self) ],
|
581
|
-
[ "/organizations/*/*/*/_acl/*", AclEndpoint.new(self) ],
|
582
|
-
]
|
583
|
-
end
|
584
|
-
result + [
|
585
|
-
# Both
|
586
|
-
[ "/dummy", DummyEndpoint.new(self) ],
|
587
|
-
[ "/organizations/*/clients", ActorsEndpoint.new(self) ],
|
588
|
-
[ "/organizations/*/clients/*", ActorEndpoint.new(self) ],
|
589
|
-
[ "/organizations/*/clients/*/keys", ActorKeysEndpoint.new(self) ],
|
590
|
-
[ "/organizations/*/clients/*/keys/default", ActorDefaultKeyEndpoint.new(self) ],
|
591
|
-
[ "/organizations/*/clients/*/keys/*", ActorKeyEndpoint.new(self) ],
|
592
|
-
[ "/organizations/*/users/*/keys", OrganizationUserKeysEndpoint.new(self) ],
|
593
|
-
[ "/organizations/*/users/*/keys/default", OrganizationUserDefaultKeyEndpoint.new(self) ],
|
594
|
-
[ "/organizations/*/users/*/keys/*", OrganizationUserKeyEndpoint.new(self) ],
|
595
|
-
[ "/organizations/*/controls", ControlsEndpoint.new(self) ],
|
596
|
-
[ "/organizations/*/cookbooks", CookbooksEndpoint.new(self) ],
|
597
|
-
[ "/organizations/*/cookbooks/*", CookbookEndpoint.new(self) ],
|
598
|
-
[ "/organizations/*/cookbooks/*/*", CookbookVersionEndpoint.new(self) ],
|
599
|
-
[ "/organizations/*/cookbook_artifacts", CookbookArtifactsEndpoint.new(self) ],
|
600
|
-
[ "/organizations/*/cookbook_artifacts/*", CookbookArtifactEndpoint.new(self) ],
|
601
|
-
[ "/organizations/*/cookbook_artifacts/*/*", CookbookArtifactIdentifierEndpoint.new(self) ],
|
602
|
-
[ "/organizations/*/data", DataBagsEndpoint.new(self) ],
|
603
|
-
[ "/organizations/*/data/*", DataBagEndpoint.new(self) ],
|
604
|
-
[ "/organizations/*/data/*/*", DataBagItemEndpoint.new(self) ],
|
605
|
-
[ "/organizations/*/environments", RestListEndpoint.new(self) ],
|
606
|
-
[ "/organizations/*/environments/*", EnvironmentEndpoint.new(self) ],
|
607
|
-
[ "/organizations/*/environments/*/cookbooks", EnvironmentCookbooksEndpoint.new(self) ],
|
608
|
-
[ "/organizations/*/environments/*/cookbooks/*", EnvironmentCookbookEndpoint.new(self) ],
|
609
|
-
[ "/organizations/*/environments/*/cookbook_versions", EnvironmentCookbookVersionsEndpoint.new(self) ],
|
610
|
-
[ "/organizations/*/environments/*/nodes", EnvironmentNodesEndpoint.new(self) ],
|
611
|
-
[ "/organizations/*/environments/*/recipes", EnvironmentRecipesEndpoint.new(self) ],
|
612
|
-
[ "/organizations/*/environments/*/roles/*", EnvironmentRoleEndpoint.new(self) ],
|
613
|
-
[ "/organizations/*/nodes", NodesEndpoint.new(self) ],
|
614
|
-
[ "/organizations/*/nodes/*", NodeEndpoint.new(self) ],
|
615
|
-
[ "/organizations/*/nodes/*/_identifiers", NodeIdentifiersEndpoint.new(self) ],
|
616
|
-
[ "/organizations/*/policies", PoliciesEndpoint.new(self) ],
|
617
|
-
[ "/organizations/*/policies/*", PolicyEndpoint.new(self) ],
|
618
|
-
[ "/organizations/*/policies/*/revisions", PolicyRevisionsEndpoint.new(self) ],
|
619
|
-
[ "/organizations/*/policies/*/revisions/*", PolicyRevisionEndpoint.new(self) ],
|
620
|
-
[ "/organizations/*/policy_groups", PolicyGroupsEndpoint.new(self) ],
|
621
|
-
[ "/organizations/*/policy_groups/*", PolicyGroupEndpoint.new(self) ],
|
622
|
-
[ "/organizations/*/policy_groups/*/policies/*", PolicyGroupPolicyEndpoint.new(self) ],
|
623
|
-
[ "/organizations/*/principals/*", PrincipalEndpoint.new(self) ],
|
624
|
-
[ "/organizations/*/roles", RestListEndpoint.new(self) ],
|
625
|
-
[ "/organizations/*/roles/*", RoleEndpoint.new(self) ],
|
626
|
-
[ "/organizations/*/roles/*/environments", RoleEnvironmentsEndpoint.new(self) ],
|
627
|
-
[ "/organizations/*/roles/*/environments/*", EnvironmentRoleEndpoint.new(self) ],
|
628
|
-
[ "/organizations/*/sandboxes", SandboxesEndpoint.new(self) ],
|
629
|
-
[ "/organizations/*/sandboxes/*", SandboxEndpoint.new(self) ],
|
630
|
-
[ "/organizations/*/search", SearchesEndpoint.new(self) ],
|
631
|
-
[ "/organizations/*/search/*", SearchEndpoint.new(self) ],
|
632
|
-
[ "/organizations/*/universe", UniverseEndpoint.new(self) ],
|
633
|
-
[ "/version", VersionEndpoint.new(self) ],
|
634
|
-
[ "/server_api_version", ServerAPIVersionEndpoint.new(self) ],
|
635
|
-
|
636
|
-
# Internal
|
637
|
-
[ "/organizations/*/file_store/**", FileStoreFileEndpoint.new(self) ],
|
638
|
-
]
|
639
|
-
end
|
640
|
-
|
641
|
-
def global_endpoint?(ep)
|
642
|
-
GLOBAL_ENDPOINTS.any? do |g_ep|
|
643
|
-
ep.start_with?(g_ep)
|
644
|
-
end
|
645
|
-
end
|
646
|
-
|
647
|
-
def app
|
648
|
-
return @app if @app
|
649
|
-
|
650
|
-
router = RestRouter.new(endpoints)
|
651
|
-
router.not_found = NotFoundEndpoint.new
|
652
|
-
|
653
|
-
if options[:single_org]
|
654
|
-
rest_base_prefix = [ "organizations", options[:single_org] ]
|
655
|
-
else
|
656
|
-
rest_base_prefix = []
|
657
|
-
end
|
658
|
-
@app = proc do |env|
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
end
|
696
|
-
@app
|
697
|
-
end
|
698
|
-
|
699
|
-
def dejsonize_children(hash)
|
700
|
-
result = {}
|
701
|
-
hash.each_pair do |key, value|
|
702
|
-
result[key] = dejsonize(value)
|
703
|
-
end
|
704
|
-
result
|
705
|
-
end
|
706
|
-
|
707
|
-
def dejsonize(value)
|
708
|
-
value.is_a?(Hash) ? FFI_Yajl::Encoder.encode(value, pretty: true) : value
|
709
|
-
end
|
710
|
-
|
711
|
-
def get_file(directory, path)
|
712
|
-
value = directory
|
713
|
-
path.split("/").each do |part|
|
714
|
-
value = value[part]
|
715
|
-
end
|
716
|
-
value
|
717
|
-
end
|
718
|
-
|
719
|
-
## Disable unsecure ssl
|
720
|
-
## Ref: https://www.ruby-lang.org/en/news/2014/10/27/changing-default-settings-of-ext-openssl/
|
721
|
-
def ssl_opts
|
722
|
-
ssl_opts = OpenSSL::SSL::OP_ALL
|
723
|
-
ssl_opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
724
|
-
ssl_opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
|
725
|
-
ssl_opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
|
726
|
-
ssl_opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
|
727
|
-
ssl_opts
|
728
|
-
end
|
729
|
-
end
|
730
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: John Keiser (<jkeiser@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012-2017, Chef Software Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require "openssl" unless defined?(OpenSSL)
|
20
|
+
require "open-uri" unless defined?(OpenURI)
|
21
|
+
require "rubygems" unless defined?(Gem)
|
22
|
+
require "timeout" unless defined?(Timeout)
|
23
|
+
require "stringio" unless defined?(StringIO)
|
24
|
+
|
25
|
+
require "rack" unless defined?(Rack)
|
26
|
+
require "rackup" unless defined?(Rackup)
|
27
|
+
require "webrick" unless defined?(WEBrick)
|
28
|
+
require "webrick/https"
|
29
|
+
|
30
|
+
require_relative "../chef_zero"
|
31
|
+
require_relative "socketless_server_map"
|
32
|
+
require_relative "chef_data/cookbook_data"
|
33
|
+
require_relative "chef_data/acl_path"
|
34
|
+
require_relative "rest_router"
|
35
|
+
require_relative "data_store/memory_store_v2"
|
36
|
+
require_relative "data_store/v1_to_v2_adapter"
|
37
|
+
require_relative "data_store/default_facade"
|
38
|
+
require_relative "version"
|
39
|
+
require "chef_zero/dist"
|
40
|
+
|
41
|
+
require_relative "endpoints/rest_list_endpoint"
|
42
|
+
require_relative "endpoints/authenticate_user_endpoint"
|
43
|
+
require_relative "endpoints/acls_endpoint"
|
44
|
+
require_relative "endpoints/acl_endpoint"
|
45
|
+
require_relative "endpoints/actor_endpoint"
|
46
|
+
require_relative "endpoints/actors_endpoint"
|
47
|
+
require_relative "endpoints/actor_key_endpoint"
|
48
|
+
require_relative "endpoints/organization_user_key_endpoint"
|
49
|
+
require_relative "endpoints/organization_user_default_key_endpoint"
|
50
|
+
require_relative "endpoints/organization_user_keys_endpoint"
|
51
|
+
require_relative "endpoints/actor_default_key_endpoint"
|
52
|
+
require_relative "endpoints/actor_keys_endpoint"
|
53
|
+
require_relative "endpoints/cookbooks_endpoint"
|
54
|
+
require_relative "endpoints/cookbook_endpoint"
|
55
|
+
require_relative "endpoints/cookbook_version_endpoint"
|
56
|
+
require_relative "endpoints/cookbook_artifacts_endpoint"
|
57
|
+
require_relative "endpoints/cookbook_artifact_endpoint"
|
58
|
+
require_relative "endpoints/cookbook_artifact_identifier_endpoint"
|
59
|
+
require_relative "endpoints/containers_endpoint"
|
60
|
+
require_relative "endpoints/container_endpoint"
|
61
|
+
require_relative "endpoints/controls_endpoint"
|
62
|
+
require_relative "endpoints/dummy_endpoint"
|
63
|
+
require_relative "endpoints/data_bags_endpoint"
|
64
|
+
require_relative "endpoints/data_bag_endpoint"
|
65
|
+
require_relative "endpoints/data_bag_item_endpoint"
|
66
|
+
require_relative "endpoints/groups_endpoint"
|
67
|
+
require_relative "endpoints/group_endpoint"
|
68
|
+
require_relative "endpoints/environment_endpoint"
|
69
|
+
require_relative "endpoints/environment_cookbooks_endpoint"
|
70
|
+
require_relative "endpoints/environment_cookbook_endpoint"
|
71
|
+
require_relative "endpoints/environment_cookbook_versions_endpoint"
|
72
|
+
require_relative "endpoints/environment_nodes_endpoint"
|
73
|
+
require_relative "endpoints/environment_recipes_endpoint"
|
74
|
+
require_relative "endpoints/environment_role_endpoint"
|
75
|
+
require_relative "endpoints/license_endpoint"
|
76
|
+
require_relative "endpoints/node_endpoint"
|
77
|
+
require_relative "endpoints/nodes_endpoint"
|
78
|
+
require_relative "endpoints/node_identifiers_endpoint"
|
79
|
+
require_relative "endpoints/organizations_endpoint"
|
80
|
+
require_relative "endpoints/organization_endpoint"
|
81
|
+
require_relative "endpoints/organization_association_requests_endpoint"
|
82
|
+
require_relative "endpoints/organization_association_request_endpoint"
|
83
|
+
require_relative "endpoints/organization_authenticate_user_endpoint"
|
84
|
+
require_relative "endpoints/organization_users_endpoint"
|
85
|
+
require_relative "endpoints/organization_user_endpoint"
|
86
|
+
require_relative "endpoints/organization_validator_key_endpoint"
|
87
|
+
require_relative "endpoints/policies_endpoint"
|
88
|
+
require_relative "endpoints/policy_endpoint"
|
89
|
+
require_relative "endpoints/policy_revisions_endpoint"
|
90
|
+
require_relative "endpoints/policy_revision_endpoint"
|
91
|
+
require_relative "endpoints/policy_groups_endpoint"
|
92
|
+
require_relative "endpoints/policy_group_endpoint"
|
93
|
+
require_relative "endpoints/policy_group_policy_endpoint"
|
94
|
+
require_relative "endpoints/principal_endpoint"
|
95
|
+
require_relative "endpoints/role_endpoint"
|
96
|
+
require_relative "endpoints/role_environments_endpoint"
|
97
|
+
require_relative "endpoints/sandboxes_endpoint"
|
98
|
+
require_relative "endpoints/sandbox_endpoint"
|
99
|
+
require_relative "endpoints/searches_endpoint"
|
100
|
+
require_relative "endpoints/search_endpoint"
|
101
|
+
require_relative "endpoints/system_recovery_endpoint"
|
102
|
+
require_relative "endpoints/user_association_requests_endpoint"
|
103
|
+
require_relative "endpoints/user_association_requests_count_endpoint"
|
104
|
+
require_relative "endpoints/user_association_request_endpoint"
|
105
|
+
require_relative "endpoints/user_organizations_endpoint"
|
106
|
+
require_relative "endpoints/file_store_file_endpoint"
|
107
|
+
require_relative "endpoints/not_found_endpoint"
|
108
|
+
require_relative "endpoints/version_endpoint"
|
109
|
+
require_relative "endpoints/server_api_version_endpoint"
|
110
|
+
require_relative "endpoints/universe_endpoint"
|
111
|
+
|
112
|
+
module ChefZero
|
113
|
+
|
114
|
+
class Server
|
115
|
+
|
116
|
+
DEFAULT_OPTIONS = {
|
117
|
+
host: ["127.0.0.1"],
|
118
|
+
port: 8889,
|
119
|
+
log_level: :warn,
|
120
|
+
generate_real_keys: true,
|
121
|
+
single_org: "chef",
|
122
|
+
ssl: false,
|
123
|
+
}.freeze
|
124
|
+
|
125
|
+
GLOBAL_ENDPOINTS = [
|
126
|
+
"/license",
|
127
|
+
"/version",
|
128
|
+
"/server_api_version",
|
129
|
+
].freeze
|
130
|
+
|
131
|
+
def initialize(options = {})
|
132
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
133
|
+
if @options[:single_org] && !@options.key?(:osc_compat)
|
134
|
+
@options[:osc_compat] = true
|
135
|
+
end
|
136
|
+
@options.freeze
|
137
|
+
ChefZero::Log.level = @options[:log_level].to_sym
|
138
|
+
@app = nil
|
139
|
+
end
|
140
|
+
|
141
|
+
# @return [Hash]
|
142
|
+
attr_reader :options
|
143
|
+
|
144
|
+
# @return [Integer]
|
145
|
+
def port
|
146
|
+
if @port
|
147
|
+
@port
|
148
|
+
# If options[:port] is not an Array or an Enumerable, it is just an Integer.
|
149
|
+
elsif !options[:port].respond_to?(:each)
|
150
|
+
options[:port]
|
151
|
+
else
|
152
|
+
raise "port cannot be determined until server is started"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return [WEBrick::HTTPServer]
|
157
|
+
attr_reader :server
|
158
|
+
|
159
|
+
include ChefZero::Endpoints
|
160
|
+
|
161
|
+
#
|
162
|
+
# The URL for this Chef Zero server. If the given host is an IPV6 address,
|
163
|
+
# it is escaped in brackets according to RFC-2732.
|
164
|
+
#
|
165
|
+
# @see http://www.ietf.org/rfc/rfc2732.txt RFC-2732
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
#
|
169
|
+
def url
|
170
|
+
sch = @options[:ssl] ? "https" : "http"
|
171
|
+
hosts = Array(@options[:host])
|
172
|
+
@url ||= if hosts.first.include?(":")
|
173
|
+
URI("#{sch}://[#{hosts.first}]:#{port}").to_s
|
174
|
+
else
|
175
|
+
URI("#{sch}://#{hosts.first}:#{port}").to_s
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def local_mode_url
|
180
|
+
raise "Port not yet set, cannot generate URL" unless port.is_a?(Integer)
|
181
|
+
|
182
|
+
"chefzero://localhost:#{port}"
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# The data store for this server (default is in-memory).
|
187
|
+
#
|
188
|
+
# @return [ChefZero::DataStore]
|
189
|
+
#
|
190
|
+
def data_store
|
191
|
+
@data_store ||= begin
|
192
|
+
result = @options[:data_store] || DataStore::DefaultFacade.new(DataStore::MemoryStoreV2.new, options[:single_org], options[:osc_compat])
|
193
|
+
if options[:single_org]
|
194
|
+
|
195
|
+
if !result.respond_to?(:interface_version) || result.interface_version == 1
|
196
|
+
result = ChefZero::DataStore::V1ToV2Adapter.new(result, options[:single_org])
|
197
|
+
result = ChefZero::DataStore::DefaultFacade.new(result, options[:single_org], options[:osc_compat])
|
198
|
+
end
|
199
|
+
|
200
|
+
else
|
201
|
+
if !result.respond_to?(:interface_version) || result.interface_version == 1
|
202
|
+
raise "Multi-org not supported by data store #{result}!"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
result
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
#
|
211
|
+
# Boolean method to determine if real Public/Private keys should be
|
212
|
+
# generated.
|
213
|
+
#
|
214
|
+
# @return [Boolean]
|
215
|
+
# true if real keys should be created, false otherwise
|
216
|
+
#
|
217
|
+
def generate_real_keys?
|
218
|
+
!!@options[:generate_real_keys]
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# Start a Chef Zero server in the current thread. You can stop this server
|
223
|
+
# by canceling the current thread.
|
224
|
+
#
|
225
|
+
# @param [Boolean|IO] publish
|
226
|
+
# publish the server information to the publish parameter or to STDOUT if it's "true"
|
227
|
+
#
|
228
|
+
# @return [nil]
|
229
|
+
# this method will block the main thread until interrupted
|
230
|
+
#
|
231
|
+
def start(publish = true)
|
232
|
+
publish = publish[:publish] if publish.is_a?(Hash) # Legacy API
|
233
|
+
|
234
|
+
if publish
|
235
|
+
output = publish.respond_to?(:puts) ? publish : STDOUT
|
236
|
+
output.puts <<-EOH.gsub(/^ {10}/, "")
|
237
|
+
>> Starting #{ChefZero::Dist::PRODUCT} (v#{ChefZero::VERSION})...
|
238
|
+
EOH
|
239
|
+
end
|
240
|
+
|
241
|
+
thread = start_background
|
242
|
+
|
243
|
+
if publish
|
244
|
+
output = publish.respond_to?(:puts) ? publish : STDOUT
|
245
|
+
output.puts <<-EOH.gsub(/^ {10}/, "")
|
246
|
+
>> WEBrick (v#{WEBrick::VERSION}) on Rack (v#{Rack.release}) is listening at #{url}
|
247
|
+
>> Press CTRL+C to stop
|
248
|
+
|
249
|
+
EOH
|
250
|
+
end
|
251
|
+
|
252
|
+
%w{INT TERM}.each do |signal|
|
253
|
+
Signal.trap(signal) do
|
254
|
+
puts "\n>> Stopping #{ChefZero::Dist::PRODUCT}..."
|
255
|
+
@server.shutdown
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Move the background process to the main thread
|
260
|
+
thread.join
|
261
|
+
end
|
262
|
+
|
263
|
+
#
|
264
|
+
# Start a Chef Zero server in a forked process. This method returns the PID
|
265
|
+
# to the forked process.
|
266
|
+
#
|
267
|
+
# @param [Fixnum] wait
|
268
|
+
# the number of seconds to wait for the server to start
|
269
|
+
#
|
270
|
+
# @return [Thread]
|
271
|
+
# the thread the background process is running in
|
272
|
+
#
|
273
|
+
def listen(hosts, port)
|
274
|
+
hosts.each do |host|
|
275
|
+
@server.listen(host, port)
|
276
|
+
end
|
277
|
+
true
|
278
|
+
rescue Errno::EADDRINUSE
|
279
|
+
ChefZero::Log.warn("Port #{port} not available")
|
280
|
+
@server.listeners.each(&:close)
|
281
|
+
@server.listeners.clear
|
282
|
+
false
|
283
|
+
end
|
284
|
+
|
285
|
+
def start_background(wait = 5)
|
286
|
+
@server = WEBrick::HTTPServer.new(
|
287
|
+
DoNotListen: true,
|
288
|
+
AccessLog: [],
|
289
|
+
Logger: WEBrick::Log.new(StringIO.new, 7),
|
290
|
+
RequestTimeout: 300,
|
291
|
+
SSLEnable: options[:ssl],
|
292
|
+
SSLOptions: ssl_opts,
|
293
|
+
SSLCertName: [ [ "CN", WEBrick::Utils.getservername ] ],
|
294
|
+
StartCallback: proc do
|
295
|
+
@running = true
|
296
|
+
end
|
297
|
+
)
|
298
|
+
ENV["HTTPS"] = "on" if options[:ssl]
|
299
|
+
@server.mount("/", Rackup::Handler::WEBrick, app)
|
300
|
+
|
301
|
+
# Pick a port
|
302
|
+
# If options[:port] can be an Enumerator, an Array, or an Integer,
|
303
|
+
# we need something that can respond to .each (Enum and Array can already).
|
304
|
+
Array(options[:port]).each do |port|
|
305
|
+
if listen(Array(options[:host]), port)
|
306
|
+
@port = port
|
307
|
+
break
|
308
|
+
end
|
309
|
+
end
|
310
|
+
unless @port
|
311
|
+
raise Errno::EADDRINUSE,
|
312
|
+
"No port in :port range #{options[:port]} is available"
|
313
|
+
end
|
314
|
+
|
315
|
+
# Start the server in the background
|
316
|
+
@thread = Thread.new do
|
317
|
+
|
318
|
+
Thread.current.abort_on_exception = true
|
319
|
+
@server.start
|
320
|
+
ensure
|
321
|
+
@port = nil
|
322
|
+
@running = false
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
# Do not return until the web server is genuinely started.
|
327
|
+
sleep(0.01) while !@running && @thread.alive?
|
328
|
+
|
329
|
+
SocketlessServerMap.instance.register_port(@port, self)
|
330
|
+
|
331
|
+
@thread
|
332
|
+
end
|
333
|
+
|
334
|
+
def start_socketless
|
335
|
+
@port = SocketlessServerMap.instance.register_no_listen_server(self)
|
336
|
+
end
|
337
|
+
|
338
|
+
def handle_socketless_request(request_env)
|
339
|
+
app.call(request_env)
|
340
|
+
end
|
341
|
+
|
342
|
+
#
|
343
|
+
# Boolean method to determine if the server is currently ready to accept
|
344
|
+
# requests. This method will attempt to make an HTTP request against the
|
345
|
+
# server. If this method returns true, you are safe to make a request.
|
346
|
+
#
|
347
|
+
# @return [Boolean]
|
348
|
+
# true if the server is accepting requests, false otherwise
|
349
|
+
#
|
350
|
+
def running?
|
351
|
+
!@server.nil? && @running && @server.status == :Running
|
352
|
+
end
|
353
|
+
|
354
|
+
#
|
355
|
+
# Gracefully stop the Chef Zero server.
|
356
|
+
#
|
357
|
+
# @param [Fixnum] wait
|
358
|
+
# the number of seconds to wait before raising force-terminating the
|
359
|
+
# server
|
360
|
+
#
|
361
|
+
def stop(wait = 5)
|
362
|
+
if @running
|
363
|
+
@server.shutdown if @server
|
364
|
+
@thread.join(wait) if @thread
|
365
|
+
end
|
366
|
+
rescue Timeout::Error
|
367
|
+
if @thread
|
368
|
+
ChefZero::Log.error("#{ChefZero::Dist::PRODUCT} did not stop within #{wait} seconds! Killing...")
|
369
|
+
@thread.kill
|
370
|
+
SocketlessServerMap.deregister(port)
|
371
|
+
end
|
372
|
+
ensure
|
373
|
+
@server = nil
|
374
|
+
@thread = nil
|
375
|
+
end
|
376
|
+
|
377
|
+
def gen_key_pair
|
378
|
+
if generate_real_keys?
|
379
|
+
private_key = OpenSSL::PKey::RSA.new(2048)
|
380
|
+
public_key = private_key.public_key.to_s
|
381
|
+
public_key.sub!(/^-----BEGIN RSA PUBLIC KEY-----/, "-----BEGIN PUBLIC KEY-----")
|
382
|
+
public_key.sub!(/-----END RSA PUBLIC KEY-----(\s+)$/, '-----END PUBLIC KEY-----\1')
|
383
|
+
[private_key.to_s, public_key]
|
384
|
+
else
|
385
|
+
[PRIVATE_KEY, PUBLIC_KEY]
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def on_request(&block)
|
390
|
+
@on_request_proc = block
|
391
|
+
end
|
392
|
+
|
393
|
+
def on_response(&block)
|
394
|
+
@on_response_proc = block
|
395
|
+
end
|
396
|
+
|
397
|
+
# Load data in a nice, friendly form:
|
398
|
+
# {
|
399
|
+
# 'roles' => {
|
400
|
+
# 'desert' => '{ "description": "Hot and dry"' },
|
401
|
+
# 'rainforest' => { "description" => 'Wet and humid' }
|
402
|
+
# },
|
403
|
+
# 'cookbooks' => {
|
404
|
+
# 'apache2-1.0.1' => {
|
405
|
+
# 'templates' => { 'default' => { 'blah.txt' => 'hi' }}
|
406
|
+
# 'recipes' => { 'default.rb' => 'template "blah.txt"' }
|
407
|
+
# 'metadata.rb' => 'depends "mysql"'
|
408
|
+
# },
|
409
|
+
# 'apache2-1.2.0' => {
|
410
|
+
# 'templates' => { 'default' => { 'blah.txt' => 'lo' }}
|
411
|
+
# 'recipes' => { 'default.rb' => 'template "blah.txt"' }
|
412
|
+
# 'metadata.rb' => 'depends "mysql"'
|
413
|
+
# },
|
414
|
+
# 'mysql' => {
|
415
|
+
# 'recipes' => { 'default.rb' => 'file { contents "hi" }' },
|
416
|
+
# 'metadata.rb' => 'version "1.0.0"'
|
417
|
+
# }
|
418
|
+
# }
|
419
|
+
# }
|
420
|
+
def load_data(contents, org_name = nil)
|
421
|
+
org_name ||= options[:single_org]
|
422
|
+
if org_name.nil? && contents.keys != [ "users" ]
|
423
|
+
raise "Must pass an org name to load_data or run in single_org mode"
|
424
|
+
end
|
425
|
+
|
426
|
+
%w{clients containers environments groups nodes roles sandboxes}.each do |data_type|
|
427
|
+
if contents[data_type]
|
428
|
+
dejsonize_children(contents[data_type]).each_pair do |name, data|
|
429
|
+
data_store.set(["organizations", org_name, data_type, name], data, :create)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
if contents["users"]
|
435
|
+
dejsonize_children(contents["users"]).each_pair do |name, data|
|
436
|
+
if options[:osc_compat]
|
437
|
+
data_store.set(["organizations", org_name, "users", name], data, :create)
|
438
|
+
else
|
439
|
+
# Create the user and put them in the org
|
440
|
+
data_store.set(["users", name], data, :create)
|
441
|
+
if org_name
|
442
|
+
data_store.set(["organizations", org_name, "users", name], "{}", :create)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
if contents["members"]
|
449
|
+
contents["members"].each do |name|
|
450
|
+
data_store.set(["organizations", org_name, "users", name], "{}", :create)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
if contents["invites"]
|
455
|
+
contents["invites"].each do |name|
|
456
|
+
data_store.set(["organizations", org_name, "association_requests", name], "{}", :create)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
if contents["acls"]
|
461
|
+
dejsonize_children(contents["acls"]).each do |path, acl|
|
462
|
+
path = [ "organizations", org_name ] + path.split("/")
|
463
|
+
path = ChefData::AclPath.get_acl_data_path(path)
|
464
|
+
ChefZero::RSpec.server.data_store.set(path, acl)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
if contents["data"]
|
469
|
+
contents["data"].each_pair do |key, data_bag|
|
470
|
+
data_store.create_dir(["organizations", org_name, "data"], key, :recursive)
|
471
|
+
dejsonize_children(data_bag).each do |item_name, item|
|
472
|
+
data_store.set(["organizations", org_name, "data", key, item_name], item, :create)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
if contents["policies"]
|
478
|
+
contents["policies"].each_pair do |policy_name, policy_struct|
|
479
|
+
# data_store.create_dir(['organizations', org_name, 'policies', policy_name], "revisions", :recursive)
|
480
|
+
dejsonize_children(policy_struct).each do |revision, policy_data|
|
481
|
+
data_store.set(["organizations", org_name, "policies", policy_name,
|
482
|
+
"revisions", revision], policy_data, :create, :create_dir)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
if contents["policy_groups"]
|
488
|
+
contents["policy_groups"].each_pair do |group_name, group|
|
489
|
+
group["policies"].each do |policy_name, policy_revision|
|
490
|
+
data_store.set(["organizations", org_name, "policy_groups", group_name, "policies", policy_name], FFI_Yajl::Encoder.encode(policy_revision["revision_id"], pretty: true), :create, :create_dir)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
%w{cookbooks cookbook_artifacts}.each do |cookbook_type|
|
496
|
+
if contents[cookbook_type]
|
497
|
+
contents[cookbook_type].each_pair do |name_version, cookbook|
|
498
|
+
if cookbook_type == "cookbook_artifacts"
|
499
|
+
name, _, identifier = name_version.rpartition("-")
|
500
|
+
cookbook_data = ChefData::CookbookData.to_hash(cookbook, name, identifier)
|
501
|
+
elsif name_version =~ /(.+)-(\d+\.\d+\.\d+)$/
|
502
|
+
cookbook_data = ChefData::CookbookData.to_hash(cookbook, $1, $2)
|
503
|
+
else
|
504
|
+
cookbook_data = ChefData::CookbookData.to_hash(cookbook, name_version)
|
505
|
+
end
|
506
|
+
raise "No version specified" unless cookbook_data[:version]
|
507
|
+
|
508
|
+
data_store.create_dir(["organizations", org_name, cookbook_type], cookbook_data[:cookbook_name], :recursive)
|
509
|
+
data_store.set(["organizations", org_name, cookbook_type, cookbook_data[:cookbook_name], cookbook_data[:version]], FFI_Yajl::Encoder.encode(cookbook_data, pretty: true), :create)
|
510
|
+
cookbook_data.values.each do |files|
|
511
|
+
next unless files.is_a? Array
|
512
|
+
|
513
|
+
files.each do |file|
|
514
|
+
data_store.set(["organizations", org_name, "file_store", "checksums", file[:checksum]], get_file(cookbook, file[:path]), :create)
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def clear_data
|
523
|
+
data_store.clear
|
524
|
+
end
|
525
|
+
|
526
|
+
def request_handler(&block)
|
527
|
+
@request_handler = block
|
528
|
+
end
|
529
|
+
|
530
|
+
def to_s
|
531
|
+
"#<#{self.class} #{url}>"
|
532
|
+
end
|
533
|
+
|
534
|
+
def inspect
|
535
|
+
"#<#{self.class} @url=#{url.inspect}>"
|
536
|
+
end
|
537
|
+
|
538
|
+
private
|
539
|
+
|
540
|
+
def endpoints
|
541
|
+
result = if options[:osc_compat]
|
542
|
+
# OSC-only
|
543
|
+
[
|
544
|
+
[ "/organizations/*/users", ActorsEndpoint.new(self) ],
|
545
|
+
[ "/organizations/*/users/*", ActorEndpoint.new(self) ],
|
546
|
+
[ "/organizations/*/authenticate_user", OrganizationAuthenticateUserEndpoint.new(self) ],
|
547
|
+
]
|
548
|
+
else
|
549
|
+
# EC-only
|
550
|
+
[
|
551
|
+
[ "/organizations/*/users", OrganizationUsersEndpoint.new(self) ],
|
552
|
+
[ "/organizations/*/users/*", OrganizationUserEndpoint.new(self) ],
|
553
|
+
[ "/users", ActorsEndpoint.new(self, "username") ],
|
554
|
+
[ "/users/*", ActorEndpoint.new(self, "username") ],
|
555
|
+
[ "/users/*/_acl", AclsEndpoint.new(self) ],
|
556
|
+
[ "/users/*/_acl/*", AclEndpoint.new(self) ],
|
557
|
+
[ "/users/*/association_requests", UserAssociationRequestsEndpoint.new(self) ],
|
558
|
+
[ "/users/*/association_requests/count", UserAssociationRequestsCountEndpoint.new(self) ],
|
559
|
+
[ "/users/*/association_requests/*", UserAssociationRequestEndpoint.new(self) ],
|
560
|
+
[ "/users/*/keys", ActorKeysEndpoint.new(self) ],
|
561
|
+
[ "/users/*/keys/default", ActorDefaultKeyEndpoint.new(self) ],
|
562
|
+
[ "/users/*/keys/*", ActorKeyEndpoint.new(self) ],
|
563
|
+
[ "/users/*/organizations", UserOrganizationsEndpoint.new(self) ],
|
564
|
+
[ "/authenticate_user", AuthenticateUserEndpoint.new(self) ],
|
565
|
+
[ "/system_recovery", SystemRecoveryEndpoint.new(self) ],
|
566
|
+
[ "/license", LicenseEndpoint.new(self) ],
|
567
|
+
[ "/organizations", OrganizationsEndpoint.new(self) ],
|
568
|
+
[ "/organizations/*", OrganizationEndpoint.new(self) ],
|
569
|
+
[ "/organizations/*/_validator_key", OrganizationValidatorKeyEndpoint.new(self) ],
|
570
|
+
[ "/organizations/*/association_requests", OrganizationAssociationRequestsEndpoint.new(self) ],
|
571
|
+
[ "/organizations/*/association_requests/*", OrganizationAssociationRequestEndpoint.new(self) ],
|
572
|
+
[ "/organizations/*/containers", ContainersEndpoint.new(self) ],
|
573
|
+
[ "/organizations/*/containers/*", ContainerEndpoint.new(self) ],
|
574
|
+
[ "/organizations/*/groups", GroupsEndpoint.new(self) ],
|
575
|
+
[ "/organizations/*/groups/*", GroupEndpoint.new(self) ],
|
576
|
+
[ "/organizations/*/organization/_acl", AclsEndpoint.new(self) ],
|
577
|
+
[ "/organizations/*/organizations/_acl", AclsEndpoint.new(self) ],
|
578
|
+
[ "/organizations/*/*/*/_acl", AclsEndpoint.new(self) ],
|
579
|
+
[ "/organizations/*/organization/_acl/*", AclEndpoint.new(self) ],
|
580
|
+
[ "/organizations/*/organizations/_acl/*", AclEndpoint.new(self) ],
|
581
|
+
[ "/organizations/*/*/*/_acl/*", AclEndpoint.new(self) ],
|
582
|
+
]
|
583
|
+
end
|
584
|
+
result + [
|
585
|
+
# Both
|
586
|
+
[ "/dummy", DummyEndpoint.new(self) ],
|
587
|
+
[ "/organizations/*/clients", ActorsEndpoint.new(self) ],
|
588
|
+
[ "/organizations/*/clients/*", ActorEndpoint.new(self) ],
|
589
|
+
[ "/organizations/*/clients/*/keys", ActorKeysEndpoint.new(self) ],
|
590
|
+
[ "/organizations/*/clients/*/keys/default", ActorDefaultKeyEndpoint.new(self) ],
|
591
|
+
[ "/organizations/*/clients/*/keys/*", ActorKeyEndpoint.new(self) ],
|
592
|
+
[ "/organizations/*/users/*/keys", OrganizationUserKeysEndpoint.new(self) ],
|
593
|
+
[ "/organizations/*/users/*/keys/default", OrganizationUserDefaultKeyEndpoint.new(self) ],
|
594
|
+
[ "/organizations/*/users/*/keys/*", OrganizationUserKeyEndpoint.new(self) ],
|
595
|
+
[ "/organizations/*/controls", ControlsEndpoint.new(self) ],
|
596
|
+
[ "/organizations/*/cookbooks", CookbooksEndpoint.new(self) ],
|
597
|
+
[ "/organizations/*/cookbooks/*", CookbookEndpoint.new(self) ],
|
598
|
+
[ "/organizations/*/cookbooks/*/*", CookbookVersionEndpoint.new(self) ],
|
599
|
+
[ "/organizations/*/cookbook_artifacts", CookbookArtifactsEndpoint.new(self) ],
|
600
|
+
[ "/organizations/*/cookbook_artifacts/*", CookbookArtifactEndpoint.new(self) ],
|
601
|
+
[ "/organizations/*/cookbook_artifacts/*/*", CookbookArtifactIdentifierEndpoint.new(self) ],
|
602
|
+
[ "/organizations/*/data", DataBagsEndpoint.new(self) ],
|
603
|
+
[ "/organizations/*/data/*", DataBagEndpoint.new(self) ],
|
604
|
+
[ "/organizations/*/data/*/*", DataBagItemEndpoint.new(self) ],
|
605
|
+
[ "/organizations/*/environments", RestListEndpoint.new(self) ],
|
606
|
+
[ "/organizations/*/environments/*", EnvironmentEndpoint.new(self) ],
|
607
|
+
[ "/organizations/*/environments/*/cookbooks", EnvironmentCookbooksEndpoint.new(self) ],
|
608
|
+
[ "/organizations/*/environments/*/cookbooks/*", EnvironmentCookbookEndpoint.new(self) ],
|
609
|
+
[ "/organizations/*/environments/*/cookbook_versions", EnvironmentCookbookVersionsEndpoint.new(self) ],
|
610
|
+
[ "/organizations/*/environments/*/nodes", EnvironmentNodesEndpoint.new(self) ],
|
611
|
+
[ "/organizations/*/environments/*/recipes", EnvironmentRecipesEndpoint.new(self) ],
|
612
|
+
[ "/organizations/*/environments/*/roles/*", EnvironmentRoleEndpoint.new(self) ],
|
613
|
+
[ "/organizations/*/nodes", NodesEndpoint.new(self) ],
|
614
|
+
[ "/organizations/*/nodes/*", NodeEndpoint.new(self) ],
|
615
|
+
[ "/organizations/*/nodes/*/_identifiers", NodeIdentifiersEndpoint.new(self) ],
|
616
|
+
[ "/organizations/*/policies", PoliciesEndpoint.new(self) ],
|
617
|
+
[ "/organizations/*/policies/*", PolicyEndpoint.new(self) ],
|
618
|
+
[ "/organizations/*/policies/*/revisions", PolicyRevisionsEndpoint.new(self) ],
|
619
|
+
[ "/organizations/*/policies/*/revisions/*", PolicyRevisionEndpoint.new(self) ],
|
620
|
+
[ "/organizations/*/policy_groups", PolicyGroupsEndpoint.new(self) ],
|
621
|
+
[ "/organizations/*/policy_groups/*", PolicyGroupEndpoint.new(self) ],
|
622
|
+
[ "/organizations/*/policy_groups/*/policies/*", PolicyGroupPolicyEndpoint.new(self) ],
|
623
|
+
[ "/organizations/*/principals/*", PrincipalEndpoint.new(self) ],
|
624
|
+
[ "/organizations/*/roles", RestListEndpoint.new(self) ],
|
625
|
+
[ "/organizations/*/roles/*", RoleEndpoint.new(self) ],
|
626
|
+
[ "/organizations/*/roles/*/environments", RoleEnvironmentsEndpoint.new(self) ],
|
627
|
+
[ "/organizations/*/roles/*/environments/*", EnvironmentRoleEndpoint.new(self) ],
|
628
|
+
[ "/organizations/*/sandboxes", SandboxesEndpoint.new(self) ],
|
629
|
+
[ "/organizations/*/sandboxes/*", SandboxEndpoint.new(self) ],
|
630
|
+
[ "/organizations/*/search", SearchesEndpoint.new(self) ],
|
631
|
+
[ "/organizations/*/search/*", SearchEndpoint.new(self) ],
|
632
|
+
[ "/organizations/*/universe", UniverseEndpoint.new(self) ],
|
633
|
+
[ "/version", VersionEndpoint.new(self) ],
|
634
|
+
[ "/server_api_version", ServerAPIVersionEndpoint.new(self) ],
|
635
|
+
|
636
|
+
# Internal
|
637
|
+
[ "/organizations/*/file_store/**", FileStoreFileEndpoint.new(self) ],
|
638
|
+
]
|
639
|
+
end
|
640
|
+
|
641
|
+
def global_endpoint?(ep)
|
642
|
+
GLOBAL_ENDPOINTS.any? do |g_ep|
|
643
|
+
ep.start_with?(g_ep)
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
def app
|
648
|
+
return @app if @app
|
649
|
+
|
650
|
+
router = RestRouter.new(endpoints)
|
651
|
+
router.not_found = NotFoundEndpoint.new
|
652
|
+
|
653
|
+
if options[:single_org]
|
654
|
+
rest_base_prefix = [ "organizations", options[:single_org] ]
|
655
|
+
else
|
656
|
+
rest_base_prefix = []
|
657
|
+
end
|
658
|
+
@app = proc do |env|
|
659
|
+
|
660
|
+
prefix = global_endpoint?(env["PATH_INFO"]) ? [] : rest_base_prefix
|
661
|
+
|
662
|
+
request = RestRequest.new(env, prefix)
|
663
|
+
if @on_request_proc
|
664
|
+
@on_request_proc.call(request)
|
665
|
+
end
|
666
|
+
response = nil
|
667
|
+
if @request_handler
|
668
|
+
response = @request_handler.call(request)
|
669
|
+
end
|
670
|
+
unless response
|
671
|
+
response = router.call(request)
|
672
|
+
end
|
673
|
+
if @on_response_proc
|
674
|
+
@on_response_proc.call(request, response)
|
675
|
+
end
|
676
|
+
|
677
|
+
# Insert Server header
|
678
|
+
response[1]["Server"] = "chef-zero"
|
679
|
+
|
680
|
+
# Add CORS header
|
681
|
+
response[1]["Access-Control-Allow-Origin"] = "*"
|
682
|
+
|
683
|
+
# Puma expects the response to be an array (chunked responses). Since
|
684
|
+
# we are statically generating data, we won't ever have said chunked
|
685
|
+
# response, so fake it.
|
686
|
+
response[-1] = Array(response[-1])
|
687
|
+
|
688
|
+
response
|
689
|
+
rescue
|
690
|
+
if options[:log_level] == :debug
|
691
|
+
STDERR.puts "Request Error: #{$!}"
|
692
|
+
STDERR.puts $!.backtrace.join("\n")
|
693
|
+
end
|
694
|
+
|
695
|
+
end
|
696
|
+
@app
|
697
|
+
end
|
698
|
+
|
699
|
+
def dejsonize_children(hash)
|
700
|
+
result = {}
|
701
|
+
hash.each_pair do |key, value|
|
702
|
+
result[key] = dejsonize(value)
|
703
|
+
end
|
704
|
+
result
|
705
|
+
end
|
706
|
+
|
707
|
+
def dejsonize(value)
|
708
|
+
value.is_a?(Hash) ? FFI_Yajl::Encoder.encode(value, pretty: true) : value
|
709
|
+
end
|
710
|
+
|
711
|
+
def get_file(directory, path)
|
712
|
+
value = directory
|
713
|
+
path.split("/").each do |part|
|
714
|
+
value = value[part]
|
715
|
+
end
|
716
|
+
value
|
717
|
+
end
|
718
|
+
|
719
|
+
## Disable unsecure ssl
|
720
|
+
## Ref: https://www.ruby-lang.org/en/news/2014/10/27/changing-default-settings-of-ext-openssl/
|
721
|
+
def ssl_opts
|
722
|
+
ssl_opts = OpenSSL::SSL::OP_ALL
|
723
|
+
ssl_opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
724
|
+
ssl_opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
|
725
|
+
ssl_opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
|
726
|
+
ssl_opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
|
727
|
+
ssl_opts
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|