knife-tidy 2.0.6 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 562e7dd8fc15d4b707b8b3d70c0bc54a3226f9d54d8bcc72ef4cf5436320fc1a
4
- data.tar.gz: 61a42597e3340788e0f4040b5c251c5fce14622afadb077048886936e445815f
3
+ metadata.gz: a35c35e40e4f4c982f86ab15be05fa79192c55a8c748b4fd0a4dacb4e2332fee
4
+ data.tar.gz: 7c1a6ac3c38567e5015520c66fb2431c161f1edc2c8af0335870b8d5f1afd183
5
5
  SHA512:
6
- metadata.gz: 3c475ece3e2a94d08a521f4262820e88e5103d8e1cc3cc7273dbd6c1ce639d122e2c00904a46fe71ddec0eefee87aa567f7de574fd965764cebe70135f65d7d0
7
- data.tar.gz: 33c2841cfc1d1c51db87a06c7a61ffd26c630096962e0ce70aac4df6d9ac234818e82c0b7ab2894a0a440fd804cb5a15a05fdbe9d691dc84ca0f1b762157d5b7
6
+ metadata.gz: 40d92b7427aff1df3a7c4fcc7cc1df7742285cc5332b64e66213413b6dbc0a6e2b80d96554b2f76f21d3173499a53533d33366986f3b4446d09b5e42f312fc5a
7
+ data.tar.gz: 1a34a9e016c5fcc1eb81bea371f7fae6a2ffa5faf48b7e83771aa2b354517c3b951970929dc58de230e2ade19ea6e0d8b1c77b74b5ad7e1d9ed256b4f38f45a9
@@ -0,0 +1,10 @@
1
+ {
2
+ "your-problem-descriptor":{
3
+ "organizations/*/cookbooks/*/metadata.rb":[
4
+ {
5
+ "pattern":"^version .*GO_PIPELINE_LABEL",
6
+ "replace":"version !COOKBOOK_VERSION!"
7
+ }
8
+ ]
9
+ }
10
+ }
@@ -1,15 +1,15 @@
1
- require "chef/knife/tidy_base"
1
+ require_relative "tidy_base"
2
2
 
3
3
  class Chef
4
4
  class Knife
5
5
  class TidyBackupClean < Knife
6
6
  deps do
7
- require "chef/cookbook_loader"
7
+ require "chef/cookbook/cookbook_version_loader"
8
8
  require "chef/cookbook/metadata"
9
9
  require "chef/role"
10
10
  require "chef/run_list"
11
- require "chef/tidy_substitutions"
12
- require "chef/tidy_acls"
11
+ require_relative "../tidy_substitutions"
12
+ require_relative "../tidy_acls"
13
13
  require "ffi_yajl"
14
14
  require "fileutils"
15
15
  require "securerandom"
@@ -75,7 +75,7 @@ class Chef
75
75
  tidy.global_user_names.each do |user|
76
76
  email = ""
77
77
  ui.stdout.puts "INFO: Validating #{user}"
78
- the_user = FFI_Yajl::Parser.parse(::File.read(::File.join(tidy.users_path, "#{user}.json")), symbolize_names: false)
78
+ the_user = tidy.json_file_to_hash(File.join(tidy.users_path, "#{user}.json"), symbolize_names: false)
79
79
  if the_user.key?("email") && the_user["email"].match(/\A[^@\s]+@[^@\s]+\z/)
80
80
  if emails_seen.include?(the_user["email"])
81
81
  ui.stdout.puts "REPAIRING: Already saw #{user}'s email, creating a unique one."
@@ -147,7 +147,7 @@ class Chef
147
147
  add_cookbook_name_to_metadata(cookbook_name, rb_path) if lines.empty?
148
148
  else
149
149
  if ::File.exist?(json_path)
150
- metadata = FFI_Yajl::Parser.parse(::File.read(json_path), symbolize_names: false)
150
+ metadata = tidy.json_file_to_hash(json_path, symbolize_names: false)
151
151
  if metadata["name"] != cookbook_name
152
152
  metadata["name"] = cookbook_name
153
153
  ui.stdout.puts "REPAIRING: Correcting `name` in #{json_path}`"
@@ -161,26 +161,24 @@ class Chef
161
161
  end
162
162
 
163
163
  def load_cookbooks(org)
164
- cl = Chef::CookbookLoader.new(tidy.cookbooks_path(org))
165
- for_each_cookbook_basename(org) do |cookbook|
164
+ for_each_cookbook_path(org) do |cookbook|
165
+ cl = Chef::Cookbook::CookbookVersionLoader.new(cookbook)
166
166
  ui.stdout.puts "INFO: Loading #{cookbook}"
167
- ret = cl.load_cookbook(cookbook)
167
+ ret = cl.load!
168
168
  if ret.nil?
169
169
  action_needed("ACTION NEEDED: Something's wrong with the #{cookbook} cookbook in org #{org} - cannot load it! Moving to cookbooks.broken folder.")
170
170
  broken_cookooks_add(org, cookbook)
171
171
  end
172
172
  end
173
- rescue LoadError => e
173
+ rescue LoadError, Exceptions::MetadataNotValid => e
174
174
  ui.error e
175
175
  exit 1
176
176
  end
177
177
 
178
- def broken_cookooks_add(org, cookbook)
178
+ def broken_cookooks_add(org, cookbook_path)
179
179
  broken_path = ::File.join(tidy.org_path(org), "cookbooks.broken")
180
180
  FileUtils.mkdir(broken_path) unless ::File.directory?(broken_path)
181
- Dir[::File.join(tidy.cookbooks_path(org), "#{cookbook}*")].each do |cb|
182
- FileUtils.mv(cb, broken_path, verbose: true, force: true)
183
- end
181
+ FileUtils.mv(cookbook_path, broken_path, verbose: true, force: true)
184
182
  end
185
183
 
186
184
  def generate_new_metadata(org)
@@ -229,7 +227,7 @@ class Chef
229
227
 
230
228
  def fix_metadata_fields(cookbook_path)
231
229
  json_path = ::File.join(cookbook_path, "metadata.json")
232
- metadata = FFI_Yajl::Parser.parse(::File.read(json_path), symbolize_names: false)
230
+ metadata = tidy.json_file_to_hash(json_path, symbolize_names: false)
233
231
  md = metadata.dup
234
232
  metadata.each_pair do |key, value|
235
233
  if value.nil?
@@ -351,7 +349,7 @@ class Chef
351
349
  end
352
350
 
353
351
  def repair_role_run_lists(role_path)
354
- the_role = FFI_Yajl::Parser.parse(::File.read(role_path), symbolize_names: false)
352
+ the_role = tidy.json_file_to_hash(role_path, symbolize_names: false)
355
353
  new_role = the_role.clone
356
354
  rl = Chef::RunList.new
357
355
  new_role["run_list"] = []
@@ -377,14 +375,14 @@ class Chef
377
375
  end
378
376
  end
379
377
  write_role(role_path, new_role)
380
- # rubocop:enable MethodLength
378
+ # rubocop:enable Metrics/MethodLength
381
379
  end
382
380
 
383
381
  def validate_roles(org)
384
382
  for_each_role(org) do |role_path|
385
383
  ui.stdout.puts "INFO: Validating Role at #{role_path}"
386
384
  begin
387
- Chef::Role.from_hash(FFI_Yajl::Parser.parse(::File.read(role_path), symbolize_names: false))
385
+ Chef::Role.from_hash(tidy.json_file_to_hash(role_path, symbolize_names: false))
388
386
  rescue ArgumentError
389
387
  repair_role_run_lists(role_path)
390
388
  end
@@ -394,7 +392,7 @@ class Chef
394
392
  def validate_clients_group(org)
395
393
  ui.stdout.puts "INFO: validating all clients for org #{org} exist in clients group"
396
394
  clients_group_path = ::File.join(tidy.groups_path(org), "clients.json")
397
- existing_group_data = FFI_Yajl::Parser.parse(::File.read(clients_group_path), symbolize_names: false)
395
+ existing_group_data = tidy.json_file_to_hash(clients_group_path, symbolize_names: false)
398
396
  existing_group_data["clients"] = [] unless existing_group_data.key?("clients")
399
397
  if existing_group_data["clients"].length != tidy.client_names(org).length
400
398
  ui.stdout.puts "REPAIRING: Adding #{(existing_group_data["clients"].length - tidy.client_names(org).length).abs} missing clients into #{org}'s client group file #{clients_group_path}"
@@ -408,7 +406,7 @@ class Chef
408
406
  def validate_invitations(org)
409
407
  invite_file = tidy.invitations_path(org)
410
408
  ui.stdout.puts "INFO: validating org #{org} invites in #{invite_file}"
411
- invitations = FFI_Yajl::Parser.parse(::File.read(invite_file), symbolize_names: false)
409
+ invitations = tidy.json_file_to_hash(invite_file, symbolize_names: false)
412
410
  invitations_new = []
413
411
  invitations.each do |invite|
414
412
  if invite["username"].nil?
@@ -24,8 +24,8 @@ class Chef
24
24
  def self.included(includer)
25
25
  includer.class_eval do
26
26
  deps do
27
- require "chef/tidy_server"
28
- require "chef/tidy_common"
27
+ require_relative "../tidy_server"
28
+ require_relative "../tidy_common"
29
29
  end
30
30
 
31
31
  option :org_list,
@@ -1,4 +1,4 @@
1
- require "chef/knife/tidy_base"
1
+ require_relative "tidy_base"
2
2
 
3
3
  class Chef
4
4
  class Knife
@@ -88,7 +88,7 @@ class Chef
88
88
  file_name = "#{reports_dir}/#{org}#{report}"
89
89
  ui.info(" Parsing file #{file_name}")
90
90
  json_string = File.read(file_name)
91
- reports[org][report] = FFI_Yajl::Parser.parse(json_string)
91
+ reports[org][report] = tidy.json_file_to_hash(json_string, symbolize_names: false)
92
92
  rescue Errno::ENOENT
93
93
  ui.info(" Skipping file #{file_name} - not found for organization #{org}")
94
94
  reports[org][report] = {}
@@ -1,4 +1,4 @@
1
- require "chef/knife/tidy_base"
1
+ require_relative "tidy_base"
2
2
 
3
3
  class Chef
4
4
  class Knife
@@ -91,7 +91,7 @@ class Chef
91
91
  return unless ::File.exist?(unused_cookbooks_file)
92
92
 
93
93
  ui.stdout.puts "INFO: Cleaning cookbooks for Org: #{org}, using #{unused_cookbooks_file}"
94
- unused_cookbooks = FFI_Yajl::Parser.parse(::File.read(unused_cookbooks_file), symbolize_names: true)
94
+ unused_cookbooks = tidy.json_file_to_hash(unused_cookbooks_file, symbolize_names: true)
95
95
  unused_cookbooks.keys.each do |cookbook|
96
96
  versions = unused_cookbooks[cookbook]
97
97
  versions.each do |version|
@@ -118,7 +118,7 @@ class Chef
118
118
  return unless ::File.exist?(stale_nodes_file)
119
119
 
120
120
  ui.stdout.puts "INFO: Cleaning stale nodes for Org: #{org}, using #{stale_nodes_file}"
121
- stale_nodes = FFI_Yajl::Parser.parse(::File.read(stale_nodes_file), symbolize_names: true)
121
+ stale_nodes = tidy.json_file_to_hash(stale_nodes_file, symbolize_names: true)
122
122
  stale_nodes[:list].each do |node|
123
123
  queue << -> { delete_node_job(org, node) }
124
124
  end
@@ -1,4 +1,4 @@
1
- require "chef/knife/tidy_base"
1
+ require_relative "tidy_base"
2
2
 
3
3
  class Chef
4
4
  class Knife
@@ -17,6 +17,11 @@ class Chef
17
17
  default: 30,
18
18
  description: "Maximum number of days since last checkin before node is considered stale (default: 30)"
19
19
 
20
+ option :keep_versions,
21
+ long: "--keep-versions MIN",
22
+ default: 0,
23
+ description: "Keep a minimum of this many versions of each cookbook (default: 0)"
24
+
20
25
  def run
21
26
  ensure_reports_dir!
22
27
  FileUtils.rm_f(server_warnings_file_path)
@@ -32,6 +37,7 @@ class Chef
32
37
 
33
38
  stale_orgs = []
34
39
  node_threshold = config[:node_threshold].to_i
40
+ keep_versions = config[:keep_versions].to_i
35
41
 
36
42
  orgs.each do |org|
37
43
  pre_12_3_nodes = []
@@ -76,6 +82,9 @@ class Chef
76
82
  end
77
83
  end
78
84
 
85
+ used_cookbooks = keep_cookbook_versions(cb_list, keep_versions)
86
+
87
+ Chef::Log.debug("Used cookbook list before checking environments: #{used_cookbooks}")
79
88
  pins = environment_constraints(org)
80
89
  used_cookbooks = check_environment_pins(used_cookbooks, pins, cb_list)
81
90
 
@@ -153,6 +162,15 @@ class Chef
153
162
  cb_list
154
163
  end
155
164
 
165
+ def keep_cookbook_versions(cb_list, min)
166
+ retain = {}
167
+ cb_list.each do |name, versions|
168
+ keep = versions.sort { |a, b| Gem::Version.new(a) <=> Gem::Version.new(b) }.last(min)
169
+ retain[name] = keep
170
+ end
171
+ retain
172
+ end
173
+
156
174
  def cookbook_count(cb_list)
157
175
  cb_count_list = {}
158
176
  cb_list.each do |name, versions|
@@ -20,26 +20,26 @@ class Chef
20
20
  def load_users
21
21
  @tidy.ui.stdout.puts "INFO: Loading users"
22
22
  Dir[::File.join(@tidy.users_path, "*.json")].each do |user|
23
- @users.push(FFI_Yajl::Parser.parse(::File.read(user), symbolize_names: true))
23
+ @users.push(@tidy.json_file_to_hash(user, symbolize_names: true))
24
24
  end
25
25
  end
26
26
 
27
27
  def load_members
28
28
  @tidy.ui.stdout.puts "INFO: Loading members for #{@org}"
29
- @members = FFI_Yajl::Parser.parse(::File.read(@tidy.members_path(@org)), symbolize_names: true)
29
+ @members = @tidy.json_file_to_hash(@tidy.members_path(@org), symbolize_names: true)
30
30
  end
31
31
 
32
32
  def load_clients
33
33
  @tidy.ui.stdout.puts "INFO: Loading clients for #{@org}"
34
34
  Dir[::File.join(@tidy.clients_path(@org), "*.json")].each do |client|
35
- @clients.push(FFI_Yajl::Parser.parse(::File.read(client), symbolize_names: true))
35
+ @clients.push(@tidy.json_file_to_hash(client, symbolize_names: true))
36
36
  end
37
37
  end
38
38
 
39
39
  def load_groups
40
40
  @tidy.ui.stdout.puts "INFO: Loading groups for #{@org}"
41
41
  Dir[::File.join(@tidy.groups_path(@org), "*.json")].each do |group|
42
- @groups.push(FFI_Yajl::Parser.parse(::File.read(group), symbolize_names: true))
42
+ @groups.push(@tidy.json_file_to_hash(group, symbolize_names: true))
43
43
  end
44
44
  end
45
45
 
@@ -128,7 +128,7 @@ class Chef
128
128
 
129
129
  def remove_group_from_acl(group, acl_file)
130
130
  @tidy.ui.stdout.puts "REPAIRING: Removing invalid group: #{group} from #{acl_file}"
131
- acl = FFI_Yajl::Parser.parse(::File.read(acl_file), symbolize_names: false)
131
+ acl = @tidy.json_file_to_hash(acl_file, symbolize_names: false)
132
132
  acl_ops.each do |op|
133
133
  acl[op]["groups"].reject! { |the_group| the_group == group }
134
134
  end
@@ -137,7 +137,7 @@ class Chef
137
137
 
138
138
  # Appends the proper acls for ::server-admins and the org's read access group if they are missing.
139
139
  def ensure_global_group_acls(acl_file)
140
- acl = FFI_Yajl::Parser.parse(::File.read(acl_file), symbolize_names: false)
140
+ acl = @tidy.json_file_to_hash(acl_file, symbolize_names: false)
141
141
  acl_ops.each do |op|
142
142
  unless acl[op]["groups"].include? "::server-admins"
143
143
  @tidy.ui.stdout.puts "REPAIRING: Adding #{op} acl for ::server-admins in #{acl_file}"
@@ -152,7 +152,7 @@ class Chef
152
152
  end
153
153
 
154
154
  def ensure_client_read_acls(acl_file)
155
- acl = FFI_Yajl::Parser.parse(::File.read(acl_file), symbolize_names: false)
155
+ acl = @tidy.json_file_to_hash(acl_file, symbolize_names: false)
156
156
  %w{users admins}.each do |group|
157
157
  unless acl["read"]["groups"].include? group
158
158
  @tidy.ui.stdout.puts "REPAIRING: Adding read acl for #{group} in #{acl_file}"
@@ -164,7 +164,7 @@ class Chef
164
164
 
165
165
  def validate_acls
166
166
  org_acls.each do |acl_file|
167
- acl = FFI_Yajl::Parser.parse(::File.read(acl_file), symbolize_names: false)
167
+ acl = @tidy.json_file_to_hash(acl_file, symbolize_names: false)
168
168
  actors_groups = acl_actors_groups(acl)
169
169
  actors_groups[:actors].each do |actor|
170
170
  next if actor == "pivotal"
@@ -203,11 +203,11 @@ class Chef
203
203
  @members.each do |member|
204
204
  user_acl_path = ::File.join(@tidy.user_acls_path, "#{member[:user][:username]}.json")
205
205
  begin
206
- user_acl = FFI_Yajl::Parser.parse(::File.read(user_acl_path), symbolize_names: false)
206
+ user_acl = @tidy.json_file_to_hash(user_acl_path, symbolize_names: false)
207
207
  rescue Errno::ENOENT
208
208
  @tidy.ui.stdout.puts "REPAIRING: Replacing missing user acl for #{member[:user][:username]}."
209
209
  @tidy.write_new_file(default_user_acl(member), user_acl_path, backup = false)
210
- user_acl = FFI_Yajl::Parser.parse(::File.read(user_acl_path), symbolize_names: false)
210
+ user_acl = @tidy.json_file_to_hash(user_acl_path, symbolize_names: false)
211
211
  end
212
212
  ensure_global_group_acls(user_acl_path)
213
213
  actors_groups = acl_actors_groups(user_acl)
@@ -221,11 +221,11 @@ class Chef
221
221
  @clients.each do |client|
222
222
  client_acl_path = ::File.join(@tidy.org_acls_path(@org), "clients", "#{client[:name]}.json")
223
223
  begin
224
- client_acl = FFI_Yajl::Parser.parse(::File.read(client_acl_path), symbolize_names: false)
224
+ client_acl = @tidy.json_file_to_hash(client_acl_path, symbolize_names: false)
225
225
  rescue Errno::ENOENT
226
226
  @tidy.ui.stdout.puts "REPAIRING: Replacing missing client acl for #{client[:name]} in #{client_acl_path}."
227
227
  @tidy.write_new_file(default_client_acl(client[:name]), client_acl_path, backup = false)
228
- client_acl = FFI_Yajl::Parser.parse(::File.read(client_acl_path), symbolize_names: false)
228
+ client_acl = @tidy.json_file_to_hash(client_acl_path, symbolize_names: false)
229
229
  end
230
230
  ensure_client_read_acls(client_acl_path)
231
231
  end
@@ -149,6 +149,23 @@ class Chef
149
149
  end
150
150
  end
151
151
 
152
+ # Read a json file and return a hash of parsed content with optional symbolized keys
153
+ #
154
+ # @param [String] path to file
155
+ # @param [double splat] options to pass FFI_Yajl::Parser.parse()
156
+ #
157
+ # @return [Hash] original json content as hash
158
+ #
159
+ # @example
160
+ # json_file_to_hash('/path/to/file.json', symbolize_names: true) => { foo: "bar" }
161
+ #
162
+ def json_file_to_hash(file_path, **options)
163
+ FFI_Yajl::Parser.parse(File.read(file_path), options)
164
+ rescue Errno::ENOENT, Errno::EACCES, FFI_Yajl::ParseError
165
+ puts "ERROR: unable to parse file: '#{file_path}'"
166
+ raise
167
+ end
168
+
152
169
  #
153
170
  # Determine the cookbook name from path
154
171
  #
@@ -17,7 +17,7 @@ class Chef
17
17
 
18
18
  def load_data
19
19
  @tidy.ui.stdout.puts "INFO: Loading substitutions from #{file_path}"
20
- @data = FFI_Yajl::Parser.parse(::File.read(@file_path), symbolize_names: false)
20
+ @data = @tidy.json_file_to_hash(@file_path, symbolize_names: false)
21
21
  rescue Errno::ENOENT
22
22
  raise NoSubstitutionFile, file_path
23
23
  end
@@ -1,4 +1,4 @@
1
1
  module KnifeTidy
2
- VERSION = "2.0.6".freeze
2
+ VERSION = "2.1.0".freeze
3
3
  MAJOR, MINOR, TINY = VERSION.split(".")
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-tidy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.6
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Miller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-04 00:00:00.000000000 Z
11
+ date: 2020-06-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Report on stale Chef Server nodes and cookbooks and clean up data integrity
14
14
  issues in a knife-ec-backup object based backup
@@ -19,6 +19,7 @@ extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - LICENSE
22
+ - conf/substitutions.json.example
22
23
  - lib/chef/knife/tidy_backup_clean.rb
23
24
  - lib/chef/knife/tidy_base.rb
24
25
  - lib/chef/knife/tidy_notify.rb