chef-dk 0.6.2 → 0.7.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.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/chef-dk/builtin_commands.rb +7 -0
- data/lib/chef-dk/command/env.rb +90 -0
- data/lib/chef-dk/command/export.rb +22 -1
- data/lib/chef-dk/command/generate.rb +1 -1
- data/lib/chef-dk/command/provision.rb +43 -0
- data/lib/chef-dk/command/push_archive.rb +126 -0
- data/lib/chef-dk/command/show_policy.rb +166 -0
- data/lib/chef-dk/command/verify.rb +58 -1
- data/lib/chef-dk/cookbook_omnifetch.rb +3 -2
- data/lib/chef-dk/exceptions.rb +27 -0
- data/lib/chef-dk/helpers.rb +29 -0
- data/lib/chef-dk/policyfile/chef_repo_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile/chef_server_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile/community_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile/cookbook_locks.rb +76 -6
- data/lib/chef-dk/policyfile/dsl.rb +10 -5
- data/lib/chef-dk/policyfile/lister.rb +230 -0
- data/lib/chef-dk/policyfile/null_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile_compiler.rb +35 -2
- data/lib/chef-dk/policyfile_lock.rb +43 -0
- data/lib/chef-dk/policyfile_services/clean_policies.rb +94 -0
- data/lib/chef-dk/policyfile_services/export_repo.rb +103 -16
- data/lib/chef-dk/policyfile_services/push_archive.rb +173 -0
- data/lib/chef-dk/policyfile_services/show_policy.rb +237 -0
- data/lib/chef-dk/service_exceptions.rb +21 -0
- data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +1 -0
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +2 -40
- data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +0 -2
- data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen.yml.erb +2 -2
- data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +1 -1
- data/lib/chef-dk/version.rb +1 -1
- data/spec/unit/command/env_spec.rb +52 -0
- data/spec/unit/command/exec_spec.rb +2 -2
- data/spec/unit/command/export_spec.rb +13 -0
- data/spec/unit/command/provision_spec.rb +56 -0
- data/spec/unit/command/push_archive_spec.rb +153 -0
- data/spec/unit/command/show_policy_spec.rb +235 -0
- data/spec/unit/command/verify_spec.rb +1 -0
- data/spec/unit/helpers_spec.rb +68 -0
- data/spec/unit/policyfile/cookbook_locks_spec.rb +107 -1
- data/spec/unit/policyfile/lister_spec.rb +256 -0
- data/spec/unit/policyfile_demands_spec.rb +202 -10
- data/spec/unit/policyfile_evaluation_spec.rb +30 -4
- data/spec/unit/policyfile_lock_serialization_spec.rb +45 -0
- data/spec/unit/policyfile_services/clean_policies_spec.rb +236 -0
- data/spec/unit/policyfile_services/export_repo_spec.rb +99 -6
- data/spec/unit/policyfile_services/push_archive_spec.rb +345 -0
- data/spec/unit/policyfile_services/show_policy_spec.rb +839 -0
- metadata +139 -8
@@ -93,7 +93,13 @@ module ChefDK
|
|
93
93
|
c.base_dir = "test-kitchen"
|
94
94
|
c.unit_test { sh("bundle exec rake unit") }
|
95
95
|
c.integration_test { sh("bundle exec rake features") }
|
96
|
-
|
96
|
+
|
97
|
+
# NOTE: By default, kitchen tries to be helpful and install a driver
|
98
|
+
# gem for you. This causes a race condition when running the tests
|
99
|
+
# concurrently, because rubygems breaks when there are partially
|
100
|
+
# installed gems in the gem repository. Instructing kitchen to create a
|
101
|
+
# gemfile instead avoids the gem installation.
|
102
|
+
c.smoke_test { run_in_tmpdir("kitchen init --create-gemfile") }
|
97
103
|
end
|
98
104
|
|
99
105
|
add_component "chef-client" do |c|
|
@@ -115,6 +121,57 @@ module ChefDK
|
|
115
121
|
c.smoke_test { run_in_tmpdir("chef generate cookbook example") }
|
116
122
|
end
|
117
123
|
|
124
|
+
# entirely possible this needs to be driven by a utility method in chef-provisioning.
|
125
|
+
add_component "chef-provisioning" do |c|
|
126
|
+
c.base_dir = "chef-dk"
|
127
|
+
|
128
|
+
c.smoke_test do
|
129
|
+
# ------------
|
130
|
+
# we want to avoid hard-coding driver names, but calling Gem::Specification produces a warning; this
|
131
|
+
# seems to be the best way to silence it.
|
132
|
+
verbose = $VERBOSE
|
133
|
+
$VERBOSE = nil
|
134
|
+
|
135
|
+
drivers = Gem::Specification.all.map { |gs| gs.name }.
|
136
|
+
select { |n| n =~ /^chef-provisioning-/ }.
|
137
|
+
uniq
|
138
|
+
|
139
|
+
versions = Gem::Specification.find_all_by_name("chef-provisioning").map { |s| s.version }
|
140
|
+
$VERBOSE = verbose
|
141
|
+
# ------------
|
142
|
+
failures = []
|
143
|
+
|
144
|
+
tmpdir do |cwd|
|
145
|
+
versions.each do |provisioning_version|
|
146
|
+
gemfile = "chef-provisioning-#{provisioning_version}-chefdk-test.gemfile"
|
147
|
+
|
148
|
+
# write out the gemfile for this chef-provisioning version, and see if Bundler can make it go.
|
149
|
+
with_file(File.join(cwd, gemfile)) do |f|
|
150
|
+
f.puts %Q(gem "chef-provisioning", "= #{provisioning_version}")
|
151
|
+
drivers.each { |d| f.puts %Q(gem "#{d}") }
|
152
|
+
end
|
153
|
+
|
154
|
+
result = sh("bundle install --local --quiet", cwd: cwd, env: {"BUNDLE_GEMFILE" => gemfile })
|
155
|
+
|
156
|
+
if result.exitstatus != 0
|
157
|
+
failures << result
|
158
|
+
end
|
159
|
+
|
160
|
+
end # end provisioning versions.
|
161
|
+
|
162
|
+
if failures.size > 0
|
163
|
+
failures.each { |fail| puts fail.stdout }
|
164
|
+
puts "\nDriver list (no version restrictions):\n #{drivers.join("\n ")}"
|
165
|
+
end
|
166
|
+
|
167
|
+
# dubious on Windows.
|
168
|
+
# this is weird, but we seem to require a Mixlib::ShellOut as the return value. suggestions
|
169
|
+
# welcome.
|
170
|
+
sh(failures.size > 0 ? "false" : "true")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
118
175
|
add_component "chefspec" do |c|
|
119
176
|
c.gem_base_dir = "chefspec"
|
120
177
|
c.unit_test { sh("rake unit") }
|
@@ -18,13 +18,14 @@
|
|
18
18
|
require 'cookbook-omnifetch'
|
19
19
|
require 'chef-dk/shell_out'
|
20
20
|
require 'chef-dk/cookbook_metadata'
|
21
|
+
require 'chef-dk/helpers'
|
21
22
|
|
22
23
|
require 'chef/http/simple'
|
23
24
|
|
24
25
|
# Configure CookbookOmnifetch's dependency injection settings to use our classes and config.
|
25
26
|
CookbookOmnifetch.configure do |c|
|
26
|
-
c.cache_path = File.expand_path('
|
27
|
-
c.storage_path = Pathname.new(File.expand_path('
|
27
|
+
c.cache_path = File.expand_path(File.join(ChefDK::Helpers.chefdk_home, 'cache'))
|
28
|
+
c.storage_path = Pathname.new(File.expand_path(File.join(ChefDK::Helpers.chefdk_home, 'cache', 'cookbooks')))
|
28
29
|
c.shell_out_class = ChefDK::ShellOut
|
29
30
|
c.cached_cookbook_class = ChefDK::CookbookMetadata
|
30
31
|
end
|
data/lib/chef-dk/exceptions.rb
CHANGED
@@ -71,4 +71,31 @@ module ChefDK
|
|
71
71
|
class BUG < RuntimeError
|
72
72
|
end
|
73
73
|
|
74
|
+
class CookbookSourceConflict < StandardError
|
75
|
+
|
76
|
+
attr_reader :conflicting_cookbooks
|
77
|
+
|
78
|
+
attr_reader :cookbook_sources
|
79
|
+
|
80
|
+
def initialize(conflicting_cookbooks, cookbook_sources)
|
81
|
+
@conflicting_cookbooks = conflicting_cookbooks
|
82
|
+
@cookbook_sources = cookbook_sources
|
83
|
+
super(compute_message)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def compute_message
|
89
|
+
conflicting_cookbook_sets = cookbook_sources.combination(2).map do |source_a, source_b|
|
90
|
+
overlapping_cookbooks = conflicting_cookbooks.select do |cookbook_name|
|
91
|
+
source_a.universe_graph.key?(cookbook_name) && source_b.universe_graph.key?(cookbook_name)
|
92
|
+
end
|
93
|
+
"Source #{source_a.desc} and #{source_b.desc} contain conflicting cookbooks:\n" +
|
94
|
+
overlapping_cookbooks.sort.map {|c| "- #{c}"}.join("\n")
|
95
|
+
end
|
96
|
+
conflicting_cookbook_sets.join("\n") + "\n"
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
74
101
|
end
|
data/lib/chef-dk/helpers.rb
CHANGED
@@ -20,6 +20,7 @@ require 'chef-dk/exceptions'
|
|
20
20
|
|
21
21
|
module ChefDK
|
22
22
|
module Helpers
|
23
|
+
extend self
|
23
24
|
|
24
25
|
#
|
25
26
|
# Runs given commands using mixlib-shellout
|
@@ -74,6 +75,17 @@ module ChefDK
|
|
74
75
|
@omnibus_chefdk_location ||= File.expand_path('embedded/apps/chef-dk', expected_omnibus_root)
|
75
76
|
end
|
76
77
|
|
78
|
+
def chefdk_home
|
79
|
+
@chefdk_home ||= begin
|
80
|
+
chefdk_home_set = !([nil, ''].include? ENV['CHEFDK_HOME'])
|
81
|
+
if chefdk_home_set
|
82
|
+
ENV['CHEFDK_HOME']
|
83
|
+
else
|
84
|
+
default_chefdk_home
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
77
89
|
private
|
78
90
|
|
79
91
|
def omnibus_expand_path(*paths)
|
@@ -86,6 +98,14 @@ module ChefDK
|
|
86
98
|
File.expand_path(File.join(Gem.ruby, "..", "..", ".."))
|
87
99
|
end
|
88
100
|
|
101
|
+
def default_chefdk_home
|
102
|
+
if Chef::Platform.windows?
|
103
|
+
File.join(ENV['LOCALAPPDATA'], 'chefdk')
|
104
|
+
else
|
105
|
+
File.expand_path('~/.chefdk')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
89
109
|
#
|
90
110
|
# environment vars for omnibus
|
91
111
|
#
|
@@ -109,5 +129,14 @@ module ChefDK
|
|
109
129
|
def with_file(path, mode='wb+', &block)
|
110
130
|
File.open(path, mode, &block)
|
111
131
|
end
|
132
|
+
|
133
|
+
#@api private
|
134
|
+
# This method resets all the instance variables used. It
|
135
|
+
# should only be used for testing
|
136
|
+
def reset!
|
137
|
+
self.instance_variables.each do |ivar|
|
138
|
+
self.instance_variable_set(ivar, nil)
|
139
|
+
end
|
140
|
+
end
|
112
141
|
end
|
113
142
|
end
|
@@ -57,6 +57,14 @@ module ChefDK
|
|
57
57
|
{ path: cookbook_version_paths[cookbook_name][cookbook_version], version: cookbook_version }
|
58
58
|
end
|
59
59
|
|
60
|
+
def null?
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
def desc
|
65
|
+
"chef_repo(#{path})"
|
66
|
+
end
|
67
|
+
|
60
68
|
private
|
61
69
|
|
62
70
|
# Setter for setting the path. It may either be a full chef-repo with
|
@@ -15,6 +15,8 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
17
|
|
18
|
+
require 'forwardable'
|
19
|
+
|
18
20
|
require 'chef-dk/exceptions'
|
19
21
|
|
20
22
|
require 'chef-dk/cookbook_profiler/null_scm'
|
@@ -98,11 +100,16 @@ module ChefDK
|
|
98
100
|
end
|
99
101
|
|
100
102
|
def cookbook_path
|
101
|
-
raise NotImplementedError, "#{self.class} must override #
|
103
|
+
raise NotImplementedError, "#{self.class} must override #cookbook_path with a specific implementation"
|
102
104
|
end
|
103
105
|
|
104
106
|
def to_lock
|
105
|
-
|
107
|
+
validate!
|
108
|
+
lock_data
|
109
|
+
end
|
110
|
+
|
111
|
+
def lock_data
|
112
|
+
raise NotImplementedError, "#{self.class} must override #lock_data a specific implementation"
|
106
113
|
end
|
107
114
|
|
108
115
|
def build_from_lock_data(lock_data)
|
@@ -227,8 +234,7 @@ module ChefDK
|
|
227
234
|
@source_options = symbolize_source_options_keys(lock_data["source_options"])
|
228
235
|
end
|
229
236
|
|
230
|
-
def
|
231
|
-
validate!
|
237
|
+
def lock_data
|
232
238
|
{
|
233
239
|
"version" => version,
|
234
240
|
"identifier" => identifier,
|
@@ -280,6 +286,7 @@ module ChefDK
|
|
280
286
|
@identifier_updated = false
|
281
287
|
@version_updated = false
|
282
288
|
@cookbook_in_git_repo = nil
|
289
|
+
@scm_info = nil
|
283
290
|
end
|
284
291
|
|
285
292
|
def cookbook_path
|
@@ -295,11 +302,15 @@ module ChefDK
|
|
295
302
|
end
|
296
303
|
|
297
304
|
def scm_info
|
298
|
-
|
305
|
+
@scm_info
|
299
306
|
end
|
300
307
|
|
301
308
|
def to_lock
|
302
|
-
|
309
|
+
refresh_scm_info
|
310
|
+
super
|
311
|
+
end
|
312
|
+
|
313
|
+
def lock_data
|
303
314
|
{
|
304
315
|
"version" => version,
|
305
316
|
"identifier" => identifier,
|
@@ -319,6 +330,7 @@ module ChefDK
|
|
319
330
|
@dotted_decimal_identifier = lock_data["dotted_decimal_identifier"]
|
320
331
|
@source = lock_data["source"]
|
321
332
|
@source_options = symbolize_source_options_keys(lock_data["source_options"])
|
333
|
+
@scm_info = lock_data["scm_info"]
|
322
334
|
end
|
323
335
|
|
324
336
|
def validate!
|
@@ -361,6 +373,10 @@ module ChefDK
|
|
361
373
|
|
362
374
|
private
|
363
375
|
|
376
|
+
def refresh_scm_info
|
377
|
+
@scm_info = scm_profiler.profile_data
|
378
|
+
end
|
379
|
+
|
364
380
|
def assert_required_keys_valid!(lock_data)
|
365
381
|
super
|
366
382
|
|
@@ -392,5 +408,59 @@ module ChefDK
|
|
392
408
|
end
|
393
409
|
|
394
410
|
end
|
411
|
+
|
412
|
+
class ArchivedCookbook < CookbookLock
|
413
|
+
|
414
|
+
extend Forwardable
|
415
|
+
|
416
|
+
def_delegator :@archived_lock, :name
|
417
|
+
def_delegator :@archived_lock, :source_options
|
418
|
+
def_delegator :@archived_lock, :identifier
|
419
|
+
def_delegator :@archived_lock, :dotted_decimal_identifier
|
420
|
+
def_delegator :@archived_lock, :version
|
421
|
+
def_delegator :@archived_lock, :source
|
422
|
+
|
423
|
+
# #to_lock calls #validate! which will typically ensure that the cookbook
|
424
|
+
# is present at the correct path for the lock type. For an archived
|
425
|
+
# cookbook, the cookbook is located in the archive, so it probably won't
|
426
|
+
# exist there. Therefore, we bypass #validate! and get the lock data
|
427
|
+
# directly.
|
428
|
+
def_delegator :@archived_lock, :lock_data, :to_lock
|
429
|
+
|
430
|
+
def initialize(archived_lock, storage_config)
|
431
|
+
@archived_lock = archived_lock
|
432
|
+
@storage_config = storage_config
|
433
|
+
end
|
434
|
+
|
435
|
+
def build_from_lock_data(lock_data)
|
436
|
+
raise NotImplementedError, "ArchivedCookbook cannot be built from lock data, it can only wrap an existing lock object"
|
437
|
+
end
|
438
|
+
|
439
|
+
def installed?
|
440
|
+
File.exist?(cookbook_path) && File.directory?(cookbook_path)
|
441
|
+
end
|
442
|
+
|
443
|
+
# The cookbook is assumed to be stored in a Chef Zero compatible repo as
|
444
|
+
# created by `chef export`. Currently that only creates "compatibility
|
445
|
+
# mode" repos since Chef Zero doesn't yet support cookbook_artifact APIs.
|
446
|
+
# So the cookbook will be located in a path like:
|
447
|
+
# cookbooks/nginx-111.222.333
|
448
|
+
def cookbook_path
|
449
|
+
File.join(relative_paths_root, "cookbooks", "#{name}-#{dotted_decimal_identifier}")
|
450
|
+
end
|
451
|
+
|
452
|
+
# We trust that archived cookbooks haven't been modified, so just return
|
453
|
+
# true for #validate!
|
454
|
+
def validate!
|
455
|
+
true
|
456
|
+
end
|
457
|
+
|
458
|
+
# We trust that archived cookbooks haven't been modified, so just return
|
459
|
+
# true for #refresh!
|
460
|
+
def refresh!
|
461
|
+
true
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
395
465
|
end
|
396
466
|
end
|
@@ -44,7 +44,7 @@ module ChefDK
|
|
44
44
|
@errors = []
|
45
45
|
@run_list = []
|
46
46
|
@named_run_lists = {}
|
47
|
-
@default_source = NullCookbookSource.new
|
47
|
+
@default_source = [ NullCookbookSource.new ]
|
48
48
|
@cookbook_location_specs = {}
|
49
49
|
@storage_config = storage_config
|
50
50
|
|
@@ -71,7 +71,7 @@ module ChefDK
|
|
71
71
|
def default_source(source_type = nil, source_argument = nil)
|
72
72
|
return @default_source if source_type.nil?
|
73
73
|
case source_type
|
74
|
-
when :community
|
74
|
+
when :community, :supermarket
|
75
75
|
set_default_community_source(source_argument)
|
76
76
|
when :chef_server
|
77
77
|
set_default_chef_server_source(source_argument)
|
@@ -140,14 +140,14 @@ module ChefDK
|
|
140
140
|
private
|
141
141
|
|
142
142
|
def set_default_community_source(source_uri)
|
143
|
-
|
143
|
+
set_default_source(CommunityCookbookSource.new(source_uri))
|
144
144
|
end
|
145
145
|
|
146
146
|
def set_default_chef_server_source(source_uri)
|
147
147
|
if source_uri.nil?
|
148
148
|
@errors << "You must specify the server's URI when using a default_source :chef_server"
|
149
149
|
else
|
150
|
-
|
150
|
+
set_default_source(ChefServerCookbookSource.new(source_uri))
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
@@ -155,10 +155,15 @@ module ChefDK
|
|
155
155
|
if path.nil?
|
156
156
|
@errors << "You must specify the path to the chef-repo when using a default_source :chef_repo"
|
157
157
|
else
|
158
|
-
|
158
|
+
set_default_source(ChefRepoCookbookSource.new(path))
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
+
def set_default_source(source)
|
163
|
+
@default_source.delete_at(0) if @default_source[0].null?
|
164
|
+
@default_source << source
|
165
|
+
end
|
166
|
+
|
162
167
|
def validate!
|
163
168
|
if @run_list.empty?
|
164
169
|
@errors << "Invalid run_list. run_list cannot be empty"
|
@@ -0,0 +1,230 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2015 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'set'
|
19
|
+
|
20
|
+
require 'chef-dk/authenticated_http'
|
21
|
+
require 'chef-dk/service_exceptions'
|
22
|
+
|
23
|
+
module ChefDK
|
24
|
+
module Policyfile
|
25
|
+
|
26
|
+
class RevIDLockDataMap
|
27
|
+
|
28
|
+
attr_reader :policy_name
|
29
|
+
attr_reader :lock_info_by_rev_id
|
30
|
+
|
31
|
+
def initialize(policy_name, lock_info_by_rev_id)
|
32
|
+
@policy_name = policy_name
|
33
|
+
@lock_info_by_rev_id = lock_info_by_rev_id
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def cb_info_for(rev_id, cookbook_name)
|
38
|
+
lock = lock_info_by_rev_id[rev_id]
|
39
|
+
cookbook_lock = lock["cookbook_locks"][cookbook_name]
|
40
|
+
|
41
|
+
if cookbook_lock
|
42
|
+
[cookbook_lock["version"], cookbook_lock["identifier"] ]
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def cbs_with_differing_ids
|
49
|
+
cbs_with_differing_ids = Set.new
|
50
|
+
all_cookbook_names.each do |cookbook_name|
|
51
|
+
all_identifiers = lock_info_by_rev_id.inject(Set.new) do |id_set, (_rev_id, rev_info)|
|
52
|
+
cookbook_lock = rev_info["cookbook_locks"][cookbook_name]
|
53
|
+
identifier = cookbook_lock && cookbook_lock["identifier"]
|
54
|
+
id_set << identifier
|
55
|
+
end
|
56
|
+
cbs_with_differing_ids << cookbook_name if all_identifiers.size > 1
|
57
|
+
end
|
58
|
+
cbs_with_differing_ids
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_cookbook_names
|
62
|
+
lock_info_by_rev_id.inject(Set.new) do |cb_set, (_rev_id, rev_info)|
|
63
|
+
cb_set.merge(rev_info["cookbook_locks"].keys)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class PolicyGroupRevIDMap
|
69
|
+
|
70
|
+
attr_reader :policy_name
|
71
|
+
attr_reader :revision_ids_by_group
|
72
|
+
|
73
|
+
def initialize(policy_name, revision_ids_by_group)
|
74
|
+
@policy_name = policy_name
|
75
|
+
@revision_ids_by_group = revision_ids_by_group
|
76
|
+
end
|
77
|
+
|
78
|
+
def unique_revision_ids
|
79
|
+
revision_ids_by_group.values.uniq
|
80
|
+
end
|
81
|
+
|
82
|
+
def policy_group_names
|
83
|
+
revision_ids_by_group.keys
|
84
|
+
end
|
85
|
+
|
86
|
+
def max_group_name_length
|
87
|
+
policy_group_names.map(&:size).max
|
88
|
+
end
|
89
|
+
|
90
|
+
def format_revision_ids
|
91
|
+
revision_ids_by_group.inject({}) do |map, (group_name, rev_id)|
|
92
|
+
map[group_name] = yield rev_id
|
93
|
+
map
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def empty?
|
98
|
+
policy_group_names.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
def each
|
102
|
+
revision_ids_by_group.each do |group_name, rev_id|
|
103
|
+
yield group_name, rev_id
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Lister
|
109
|
+
|
110
|
+
attr_accessor :policy_lock_content
|
111
|
+
|
112
|
+
attr_reader :config
|
113
|
+
|
114
|
+
def initialize(config: nil)
|
115
|
+
@config = config
|
116
|
+
@policies_by_name = nil
|
117
|
+
@policies_by_group = nil
|
118
|
+
@policy_lock_content = {}
|
119
|
+
@active_revisions = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
# A Hash with the following format
|
123
|
+
# {
|
124
|
+
# "appserver" => {
|
125
|
+
# "1111111111111111111111111111111111111111111111111111111111111111" => {},
|
126
|
+
# "2222222222222222222222222222222222222222222222222222222222222222" => {}
|
127
|
+
# },
|
128
|
+
def policies_by_name
|
129
|
+
@policies_by_name || fetch_policy_lists
|
130
|
+
@policies_by_name
|
131
|
+
end
|
132
|
+
|
133
|
+
# A Hash with the following format:
|
134
|
+
# "dev" => {
|
135
|
+
# "appserver" => "1111111111111111111111111111111111111111111111111111111111111111",
|
136
|
+
# "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555",
|
137
|
+
# "db" => "9999999999999999999999999999999999999999999999999999999999999999"
|
138
|
+
# }
|
139
|
+
def policies_by_group
|
140
|
+
@policies_by_group || fetch_policy_lists
|
141
|
+
@policies_by_group
|
142
|
+
end
|
143
|
+
|
144
|
+
def revision_info_for(policy_name, _revision_id_list)
|
145
|
+
RevIDLockDataMap.new(policy_name, policy_lock_content[policy_name])
|
146
|
+
end
|
147
|
+
|
148
|
+
def revision_ids_by_group_for_each_policy
|
149
|
+
policies_by_name.each do |policy_name, _policies|
|
150
|
+
rev_id_by_group = revision_ids_by_group_for(policy_name)
|
151
|
+
yield policy_name, rev_id_by_group
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def revision_ids_by_group_for(policy_name)
|
156
|
+
map = policies_by_group.inject({}) do |rev_id_map, (group_name, rev_id_map_for_group)|
|
157
|
+
rev_id_map[group_name] = rev_id_map_for_group[policy_name]
|
158
|
+
rev_id_map
|
159
|
+
end
|
160
|
+
PolicyGroupRevIDMap.new(policy_name, map)
|
161
|
+
end
|
162
|
+
|
163
|
+
def orphaned_revisions(policy_name)
|
164
|
+
orphans = []
|
165
|
+
policies_by_name[policy_name].each do |rev_id, _data|
|
166
|
+
orphans << rev_id unless active_revisions.include?(rev_id)
|
167
|
+
end
|
168
|
+
orphans
|
169
|
+
end
|
170
|
+
|
171
|
+
def active_revisions
|
172
|
+
@active_revisions ||= policies_by_group.inject(Set.new) do |set, (_group, policy_name_rev_id_map)|
|
173
|
+
policy_name_rev_id_map.each do |policy_name, rev_id|
|
174
|
+
set << rev_id
|
175
|
+
end
|
176
|
+
set
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def empty?
|
181
|
+
policies_by_name.empty? && policies_by_group.empty?
|
182
|
+
end
|
183
|
+
|
184
|
+
def http_client
|
185
|
+
@http_client ||= ChefDK::AuthenticatedHTTP.new(config.chef_server_url,
|
186
|
+
signing_key_filename: config.client_key,
|
187
|
+
client_name: config.node_name)
|
188
|
+
end
|
189
|
+
|
190
|
+
# @api private
|
191
|
+
# Sets internal copy of policyfile data to policies_by_name and
|
192
|
+
# policies_by_group. Used for internal testing.
|
193
|
+
def set!(policies_by_name, policies_by_group)
|
194
|
+
@policies_by_name = policies_by_name
|
195
|
+
@policies_by_group = policies_by_group
|
196
|
+
@active_revisions = nil
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def fetch_policy_lists
|
202
|
+
policy_list_data = http_client.get("policies")
|
203
|
+
set_policies_by_name_from_api(policy_list_data)
|
204
|
+
|
205
|
+
policy_group_data = http_client.get("policy_groups")
|
206
|
+
set_policies_by_group_from_api(policy_group_data)
|
207
|
+
end
|
208
|
+
|
209
|
+
def set_policies_by_name_from_api(policy_list_data)
|
210
|
+
@policies_by_name = policy_list_data.inject({}) do |map, (policy_name, policy_info)|
|
211
|
+
map[policy_name] = policy_info["revisions"]
|
212
|
+
map
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def set_policies_by_group_from_api(policy_group_data)
|
217
|
+
@policies_by_group = policy_group_data.inject({}) do |map, (policy_group, policy_info)|
|
218
|
+
map[policy_group] = policy_info["policies"].inject({}) do |rev_map, (policy_name, rev_info)|
|
219
|
+
rev_map[policy_name] = rev_info["revision_id"]; rev_map
|
220
|
+
end
|
221
|
+
|
222
|
+
map
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
|