diff --git a/.expeditor/config.yml b/.expeditor/config.yml index 215fc8e..d2e9975 100644 --- a/.expeditor/config.yml +++ b/.expeditor/config.yml @@ -1,5 +1,6 @@ # Documentation available at https://expeditor.chef.io/docs/getting-started/ --- + # Slack channel in Chef Software slack to send notifications about build failures, etc slack: notify_channel: chef-infra-notify @@ -23,11 +24,6 @@ github: changelog: rollup_header: Changes not yet released to rubygems.org -promote: - actions: - - built_in:rollover_changelog - - built_in:publish_rubygems - # These actions are taken, in order they are specified, anytime a Pull Request is merged. merge_actions: - built_in:bump_version: @@ -43,6 +39,11 @@ merge_actions: - built_in:build_gem: only_if: built_in:bump_version +promote: + actions: + - built_in:rollover_changelog + - built_in:publish_rubygems + pipelines: - verify: description: Pull Request validation tests diff --git a/.expeditor/run_linux_tests.sh b/.expeditor/run_linux_tests.sh new file mode 100755 index 0000000..2e6c34c --- /dev/null +++ b/.expeditor/run_linux_tests.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# This script runs a passed in command, but first setups up the bundler caching on the repo + +set -ue + +export USER="root" + +echo "--- dependencies" +export LANG=C.UTF-8 LANGUAGE=C.UTF-8 +S3_URL="s3://public-cd-buildkite-cache/${BUILDKITE_PIPELINE_SLUG}/${BUILDKITE_LABEL}" + +pull_s3_file() { + aws s3 cp "${S3_URL}/$1" "$1" || echo "Could not pull $1 from S3" +} + +push_s3_file() { + if [ -f "$1" ]; then + aws s3 cp "$1" "${S3_URL}/$1" || echo "Could not push $1 to S3 for caching." + fi +} + +apt-get update -y +apt-get install awscli -y + +echo "--- bundle install" +pull_s3_file "bundle.tar.gz" +pull_s3_file "bundle.sha256" + +if [ -f bundle.tar.gz ]; then + tar -xzf bundle.tar.gz +fi + +if [ -n "${RESET_BUNDLE_CACHE:-}" ]; then + rm bundle.sha256 +fi + +bundle config --local path vendor/bundle +bundle install --jobs=7 --retry=3 + +echo "--- bundle cache" +if test -f bundle.sha256 && shasum --check bundle.sha256 --status; then + echo "Bundled gems have not changed. Skipping upload to s3" +else + echo "Bundled gems have changed. Uploading to s3" + shasum -a 256 Gemfile.lock > bundle.sha256 + tar -czf bundle.tar.gz vendor/ + push_s3_file bundle.tar.gz + push_s3_file bundle.sha256 +fi + +echo "+++ bundle exec task" +bundle exec $@ diff --git a/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml index e69de29..0fae120 100644 --- a/.expeditor/verify.pipeline.yml +++ b/.expeditor/verify.pipeline.yml @@ -0,0 +1,33 @@ +--- +expeditor: + defaults: + buildkite: + retry: + automatic: + limit: 1 + timeout_in_minutes: 30 + +steps: +- label: lint-chefstyle + command: + - .expeditor/run_linux_tests.sh "rake style" + expeditor: + executor: + docker: + image: ruby:2.6-buster + +- label: run-specs-ruby-2.6 + command: + - .expeditor/run_linux_tests.sh rspec + expeditor: + executor: + docker: + image: ruby:2.6-buster + +- label: run-specs-ruby-2.7 + command: + - .expeditor/run_linux_tests.sh rspec + expeditor: + executor: + docker: + image: ruby:2.7-buster diff --git a/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md index f28915b..87ac9b4 100644 --- a/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md @@ -1,7 +1,7 @@ --- name: � Bug Report about: If something isn't working as expected �. -labels: "Status: Untriaged" +labels: "Status: Untriaged, Type: Bug" --- # Version: diff --git a/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md b/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md index 9f4a958..9508b09 100644 --- a/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md +++ b/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md @@ -1,7 +1,7 @@ --- name: Design Proposal about: I have a significant change I would like to propose and discuss before starting -labels: "Status: Untriaged" +labels: "Status: Untriaged, Type: Design Proposal" --- ### When a Change Needs a Design Proposal diff --git a/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md b/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md index 65bf5a0..0cf83a0 100644 --- a/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md @@ -4,14 +4,14 @@ about: I have a suggestion (and may want to implement it 🙂)! labels: "Status: Untriaged" --- -### Describe the Enhancement: +### Describe the Enhancement -### Describe the Need: +### Describe the Need ### Current Alternative -### Can We Help You Implement This?: +### Can We Help You Implement This? diff --git a/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md b/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md index 921a5f0..80435f8 100644 --- a/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md +++ b/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md @@ -5,7 +5,7 @@ about: If you have a question 💬, please check out our Slack! We use GitHub issues to track bugs and feature requests. If you need help please post to our Mailing List or join the Chef Community Slack. - * Chef Community Slack at http://community-slack.chef.io/. - * Chef Mailing List https://discourse.chef.io/ +* Chef Community Slack at +* Chef Mailing List Support issues opened here will be closed and redirected to Slack or Discourse. diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index 66d5d49..0000000 --- a/.github/lock.yml +++ /dev/null @@ -1 +0,0 @@ -daysUntilLock: 60 diff --git a/.rspec b/.rspec index eb3ef03..42b555a 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1 @@ ---color --fd +-f documentation --color diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..ae18fce --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,2 @@ +AllCops: + TargetRubyVersion: 2.6 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2c05284..0000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: ruby -cache: bundler - -branches: - only: - - master - -before_install: - - gem update --system - - gem install bundler - - bundle --version - - gem --version - -bundler_args: --jobs=3 --retry=3 --without docs debug - -script: - - bundle exec rake build - - bundle exec rake spec - -matrix: - include: - - rvm: 2.4.5 - - rvm: 2.5.3 - - rvm: 2.6.1 - - rvm: ruby-head - - rvm: 2.6.1 - env: - CHEFSTYLE: 1 - script: bundle exec rake style - allow_failures: - - rvm: ruby-head diff --git a/Gemfile b/Gemfile index ab6d347..afe8906 100644 --- a/Gemfile +++ b/Gemfile @@ -14,8 +14,8 @@ if ENV["GEMFILE_MOD"] instance_eval(ENV["GEMFILE_MOD"]) else group :development do - gem "chef", "~> 14" - gem "ohai", "~> 14" + gem "chef", "~> 15" + gem "ohai", "~> 15" end end diff --git a/README.md b/README.md index 5464c34..11718e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Cheffish -[![Status](https://travis-ci.org/chef/cheffish.svg?branch=master)](https://travis-ci.org/chef/cheffish) +[![Build status](https://badge.buildkite.com/3faec2ae5810e1b2f530e478b63859554663fc023b3ad6c3d4.svg?branch=master)](https://buildkite.com/chef-oss/chef-cheffish-master-verify) [![Gem Version](https://badge.fury.io/rb/cheffish.svg)](http://badge.fury.io/rb/cheffish) **Umbrella Project**: [Chef Infra](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-infra.md) diff --git a/Rakefile b/Rakefile index 3a9ba3a..179f296 100644 --- a/Rakefile +++ b/Rakefile @@ -39,4 +39,4 @@ task :console do IRB.start end -task default: [:style, :spec] +task default: %i{style spec} diff --git a/lib/chef/resource/chef_acl.rb b/lib/chef/resource/chef_acl.rb index bdda348..7a2dd10 100644 --- a/lib/chef/resource/chef_acl.rb +++ b/lib/chef/resource/chef_acl.rb @@ -99,6 +99,7 @@ def create_acl(path) # NOTE: if superusers exist, this should turn into a warning. raise "chef_acl attempted to remove all actors from GRANT! I'm sorry Dave, I can't let you remove access to an object with no hope of recovery." end + modify[differences] ||= {} modify[differences][permission] = desired_json end @@ -107,7 +108,7 @@ def create_acl(path) if modify.size > 0 changed = true description = [ "update acl #{path} at #{rest_url(path)}" ] + modify.flat_map do |diffs, permissions| - diffs.map { |diff| " #{permissions.keys.join(', ')}:#{diff}" } + diffs.map { |diff| " #{permissions.keys.join(", ")}:#{diff}" } end converge_by description do modify.values.each do |permissions| @@ -127,11 +128,13 @@ def create_acl(path) children, _error = list(path, "*") Chef::ChefFS::Parallelizer.parallel_do(children) do |child| next if child.split("/")[-1] == "containers" + create_acl(child) end # containers mess up our descent, so we do them last Chef::ChefFS::Parallelizer.parallel_do(children) do |child| next if child.split("/")[-1] != "containers" + create_acl(child) end @@ -141,7 +144,7 @@ def create_acl(path) # Get the current ACL for the given path def current_acl(acl_path) @current_acls ||= {} - if !@current_acls.key?(acl_path) + unless @current_acls.key?(acl_path) @current_acls[acl_path] = begin rest.get(rest_url(acl_path)) rescue Net::HTTPServerException => e @@ -252,8 +255,7 @@ def remove_rights(json) end end - def load_current_resource - end + def load_current_resource; end # # Matches chef_acl paths like nodes, nodes/*. @@ -270,7 +272,7 @@ def load_current_resource def match_paths(path) # Turn multiple slashes into one # nodes//x -> nodes/x - path = path.gsub(/[\/]+/, "/") + path = path.gsub(%r{[/]+}, "/") # If it's absolute, start the matching with /. If it's relative, start with '' (relative root). if path[0] == "/" matches = [ "/" ] @@ -305,6 +307,7 @@ def match_paths(path) if parts[0..index - 1].all? { |p| p != "*" } raise error end + [] else found @@ -421,21 +424,21 @@ def list(path, child) when 1 # /organizations/*, /users/*, roles/*, nodes/*, etc. results, error = rest_list(path) - if !error + unless error results = results.map { |result| ::File.join(path, result) } end when 2 # /organizations/NAME/* results, error = rest_list(::File.join(path, "containers")) - if !error + unless error results = results.map { |result| ::File.join(path, result) } end when 3 # /organizations/NAME/TYPE/* results, error = rest_list(path) - if !error + unless error results = results.map { |result| ::File.join(path, result) } end end diff --git a/lib/chef/resource/chef_client.rb b/lib/chef/resource/chef_client.rb index 78ada46..21871b8 100644 --- a/lib/chef/resource/chef_client.rb +++ b/lib/chef/resource/chef_client.rb @@ -18,7 +18,7 @@ class ChefClient < Cheffish::ChefActorBase # Output public key (if so desired) property :output_key_path, String - property :output_key_format, Symbol, default: :openssh, equal_to: [ :pem, :der, :openssh ] + property :output_key_format, Symbol, default: :openssh, equal_to: %i{pem der openssh} # Proc that runs just before the resource executes. Called with (resource) def before(&block) diff --git a/lib/chef/resource/chef_container.rb b/lib/chef/resource/chef_container.rb index c2de5f4..d529f8c 100644 --- a/lib/chef/resource/chef_container.rb +++ b/lib/chef/resource/chef_container.rb @@ -10,7 +10,7 @@ class ChefContainer < Cheffish::BaseResource property :chef_container_name, Cheffish::NAME_REGEX, name_property: true action :create do - if !@current_exists + unless @current_exists converge_by "create container #{new_resource.chef_container_name} at #{rest.url}" do rest.post("containers", normalize_for_post(new_json)) end diff --git a/lib/chef/resource/chef_data_bag.rb b/lib/chef/resource/chef_data_bag.rb index 72875a5..1eba9aa 100644 --- a/lib/chef/resource/chef_data_bag.rb +++ b/lib/chef/resource/chef_data_bag.rb @@ -9,7 +9,7 @@ class ChefDataBag < Cheffish::BaseResource property :data_bag_name, Cheffish::NAME_REGEX, name_property: true action :create do - if !current_resource_exists? + unless current_resource_exists? converge_by "create data bag #{new_resource.data_bag_name} at #{rest.url}" do rest.post("data", { "name" => new_resource.data_bag_name }) end diff --git a/lib/chef/resource/chef_data_bag_item.rb b/lib/chef/resource/chef_data_bag_item.rb index a3a2181..0fe4c8f 100644 --- a/lib/chef/resource/chef_data_bag_item.rb +++ b/lib/chef/resource/chef_data_bag_item.rb @@ -137,7 +137,7 @@ def load_current_resource # If the current secret doesn't work, look through the specified old secrets - if !current_resource.secret + unless current_resource.secret old_secrets = [] if new_resource.old_secret old_secrets += Array(new_resource.old_secret) @@ -148,17 +148,17 @@ def load_current_resource end end old_secrets.each do |secret| - begin - Chef::EncryptedDataBagItem::Decryptor.for(first_real_value, secret).for_decrypted_item - current_resource.secret secret - rescue Chef::EncryptedDataBagItem::DecryptionFailure - decrypt_error = $! - end + + Chef::EncryptedDataBagItem::Decryptor.for(first_real_value, secret).for_decrypted_item + current_resource.secret secret + rescue Chef::EncryptedDataBagItem::DecryptionFailure + decrypt_error = $! + end # If we couldn't figure out the secret, emit a warning (this isn't a fatal flaw unless we # need to reuse one of the values from the data bag) - if !current_resource.secret + unless current_resource.secret if decrypt_error Chef::Log.warn "Existing data bag is encrypted, but could not decrypt: #{decrypt_error.message}." else @@ -280,9 +280,10 @@ def calculate_differences elsif current_resource.encrypt # Encryption is different and we can't read the old values. Only allow the change # if we're overwriting the data bag item - if !new_resource.complete + unless new_resource.complete raise "Cannot encrypt #{new_resource.name} due to failure to decrypt existing resource. Set 'complete true' to overwrite or add the old secret as old_secret / old_secret_path." end + _differences = [ "overwrite data bag item (cannot decrypt old data bag item)"] differences = (new_resource.raw_data.keys & current_resource.raw_data.keys).map { |key| "overwrite #{key}" } differences += (new_resource.raw_data.keys - current_resource.raw_data.keys).map { |key| "add #{key}" } diff --git a/lib/chef/resource/chef_mirror.rb b/lib/chef/resource/chef_mirror.rb index 02c48bf..4fb8e89 100644 --- a/lib/chef/resource/chef_mirror.rb +++ b/lib/chef/resource/chef_mirror.rb @@ -95,6 +95,7 @@ def copy_to(src_root, dest_root) if new_resource.concurrency <= 0 raise "chef_mirror.concurrency must be above 0! Was set to #{new_resource.concurrency}" end + # Honor concurrency Chef::ChefFS::Parallelizer.threads = new_resource.concurrency - 1 @@ -103,13 +104,14 @@ def copy_to(src_root, dest_root) if new_resource.path[0] == "/" raise "Absolute paths in chef_mirror not yet supported." end + # Copy! path = Chef::ChefFS::FilePattern.new("/#{new_resource.path}") ui = CopyListener.new(self) error = Chef::ChefFS::FileSystem.copy_to(path, src_root, dest_root, nil, options, ui, proc { |p| p.path }) if error - raise "Errors while copying:#{ui.errors.map { |e| "#{e}\n" }.join('')}" + raise "Errors while copying:#{ui.errors.map { |e| "#{e}\n" }.join("")}" end end @@ -163,7 +165,7 @@ def remote_fs end def repo_mode - new_resource.chef_server[:chef_server_url] =~ /\/organizations\// ? "hosted_everything" : "everything" + new_resource.chef_server[:chef_server_url] =~ %r{/organizations/} ? "hosted_everything" : "everything" end def options @@ -179,8 +181,7 @@ def options result end - def load_current_resource - end + def load_current_resource; end class CopyListener def initialize(mirror) diff --git a/lib/chef/resource/chef_organization.rb b/lib/chef/resource/chef_organization.rb index cc2d405..3447c43 100644 --- a/lib/chef/resource/chef_organization.rb +++ b/lib/chef/resource/chef_organization.rb @@ -68,7 +68,7 @@ class ChefOrganization < Cheffish::BaseResource end end new_resource.members.each do |user| - if !existing_members.include?(user) + unless existing_members.include?(user) converge_by "Add #{user} to organization #{new_resource.organization_name}" do rest.post("#{rest.root_url}/organizations/#{new_resource.organization_name}/users/", { "username" => user }) end diff --git a/lib/chef/resource/chef_resolved_cookbooks.rb b/lib/chef/resource/chef_resolved_cookbooks.rb index 74a6b48..25a4568 100644 --- a/lib/chef/resource/chef_resolved_cookbooks.rb +++ b/lib/chef/resource/chef_resolved_cookbooks.rb @@ -43,7 +43,8 @@ def cookbooks_from(path = nil) new_resource.berksfile.upload( server_url: new_resource.chef_server[:chef_server_url], client_name: new_resource.chef_server[:options][:client_name], - client_key: new_resource.chef_server[:options][:signing_key_filename]) + client_key: new_resource.chef_server[:options][:signing_key_filename] + ) else file = Tempfile.new("privatekey") begin @@ -53,7 +54,8 @@ def cookbooks_from(path = nil) new_resource.berksfile.upload( server_url: new_resource.chef_server[:chef_server_url], client_name: new_resource.chef_server[:options][:client_name] || "me", - client_key: file.path) + client_key: file.path + ) ensure file.close diff --git a/lib/chef/resource/chef_role.rb b/lib/chef/resource/chef_role.rb index 20c2751..e20c5e4 100644 --- a/lib/chef/resource/chef_role.rb +++ b/lib/chef/resource/chef_role.rb @@ -62,6 +62,7 @@ def recipe(*recipes) if recipes.size == 0 raise ArgumentError, "At least one recipe must be specified" end + @run_list_modifiers ||= [] @run_list_modifiers += recipes.map { |recipe| Chef::RunList::RunListItem.new("recipe[#{recipe}]") } end @@ -70,6 +71,7 @@ def role(*roles) if roles.size == 0 raise ArgumentError, "At least one role must be specified" end + @run_list_modifiers ||= [] @run_list_modifiers += roles.map { |role| Chef::RunList::RunListItem.new("role[#{role}]") } end @@ -78,6 +80,7 @@ def remove_recipe(*recipes) if recipes.size == 0 raise ArgumentError, "At least one recipe must be specified" end + @run_list_removers ||= [] @run_list_removers += recipes.map { |recipe| Chef::RunList::RunListItem.new("recipe[#{recipe}]") } end @@ -86,6 +89,7 @@ def remove_role(*roles) if roles.size == 0 raise ArgumentError, "At least one role must be specified" end + @run_list_removers ||= [] @run_list_removers += roles.map { |recipe| Chef::RunList::RunListItem.new("role[#{role}]") } end diff --git a/lib/chef/resource/chef_user.rb b/lib/chef/resource/chef_user.rb index 0a8f594..7e088b5 100644 --- a/lib/chef/resource/chef_user.rb +++ b/lib/chef/resource/chef_user.rb @@ -25,7 +25,7 @@ class ChefUser < Cheffish::ChefActorBase # Output public key (if so desired) property :output_key_path, String - property :output_key_format, [ :pem, :der, :openssh ], default: :openssh + property :output_key_format, %i{pem der openssh}, default: :openssh # Proc that runs just before the resource executes. Called with (resource) def before(&block) diff --git a/lib/chef/resource/private_key.rb b/lib/chef/resource/private_key.rb index 664bfab..8161d17 100644 --- a/lib/chef/resource/private_key.rb +++ b/lib/chef/resource/private_key.rb @@ -13,11 +13,11 @@ class PrivateKey < Cheffish::BaseResource # Path to private key. Set to :none to create the key in memory and not on disk. property :path, [ String, :none ], name_property: true - property :format, [ :pem, :der ], default: :pem - property :type, [ :rsa, :dsa ], default: :rsa # TODO support :ec + property :format, %i{pem der}, default: :pem + property :type, %i{rsa dsa}, default: :rsa # TODO support :ec # These specify an optional public_key you can spit out if you want. property :public_key_path, String - property :public_key_format, [ :openssh, :pem, :der ], default: :openssh + property :public_key_format, %i{openssh pem der}, default: :openssh # Specify this if you want to copy another private key but give it a different format / password property :source_key property :source_key_path, String @@ -31,7 +31,7 @@ class PrivateKey < Cheffish::BaseResource # PEM-only property :pass_phrase, String - property :cipher, String, equal_to: OpenSSL::Cipher.ciphers.map { |x| x.downcase }, default: "des-ede3-cbc", coerce: proc { |x| x.downcase } + property :cipher, String, equal_to: OpenSSL::Cipher.ciphers.map(&:downcase), default: "des-ede3-cbc", coerce: proc { |x| x.downcase } # Set this to regenerate the key if it does not have the desired characteristics (like size, type, etc.) property :regenerate_if_different, [TrueClass, FalseClass] diff --git a/lib/chef/resource/public_key.rb b/lib/chef/resource/public_key.rb index cc612c0..e21a656 100644 --- a/lib/chef/resource/public_key.rb +++ b/lib/chef/resource/public_key.rb @@ -12,7 +12,7 @@ class PublicKey < Cheffish::BaseResource default_action :create property :path, String, name_property: true - property :format, [ :pem, :der, :openssh ], default: :openssh + property :format, %i{pem der openssh}, default: :openssh property :source_key property :source_key_path, String @@ -24,9 +24,10 @@ def load_prior_resource(*args) end action :create do - if !new_source_key + unless new_source_key raise "No source key specified" end + desired_output = encode_public_key(new_source_key) if Array(current_resource.action) == [ :delete ] || desired_output != IO.read(new_resource.path) converge_by "write #{new_resource.format} public key #{new_resource.path} from #{new_source_key_publicity} key #{new_resource.source_key_path}" do diff --git a/lib/cheffish.rb b/lib/cheffish.rb index 88bcfc1..164e86f 100644 --- a/lib/cheffish.rb +++ b/lib/cheffish.rb @@ -98,6 +98,7 @@ def self.get_private_key_with_path(name, config = profiled_config) elsif config[:private_key_paths] config[:private_key_paths].each do |private_key_path| next unless File.exist?(private_key_path) + Dir.entries(private_key_path).sort.each do |key| ext = File.extname(key) if key == name || ext == "" || ext == ".pem" diff --git a/lib/cheffish/base_resource.rb b/lib/cheffish/base_resource.rb index 5fc0664..acb7e19 100644 --- a/lib/cheffish/base_resource.rb +++ b/lib/cheffish/base_resource.rb @@ -91,7 +91,7 @@ def json_differences(old_json, new_json, print_values = true, name = "", result end def json_differences_internal(old_json, new_json, print_values, name, result) - if old_json.kind_of?(Hash) && new_json.kind_of?(Hash) + if old_json.is_a?(Hash) && new_json.is_a?(Hash) removed_keys = old_json.keys.inject({}) { |hash, key| hash[key] = true; hash } new_json.each_pair do |new_key, new_value| if old_json.key?(new_key) @@ -101,18 +101,18 @@ def json_differences_internal(old_json, new_json, print_values, name, result) end else if print_values - result << " add #{name == '' ? new_key : "#{name}.#{new_key}"} = #{new_value.inspect}" + result << " add #{name == "" ? new_key : "#{name}.#{new_key}"} = #{new_value.inspect}" else - result << " add #{name == '' ? new_key : "#{name}.#{new_key}"}" + result << " add #{name == "" ? new_key : "#{name}.#{new_key}"}" end end end removed_keys.keys.each do |removed_key| - result << " remove #{name == '' ? removed_key : "#{name}.#{removed_key}"}" + result << " remove #{name == "" ? removed_key : "#{name}.#{removed_key}"}" end else - old_json = old_json.to_s if old_json.kind_of?(Symbol) - new_json = new_json.to_s if new_json.kind_of?(Symbol) + old_json = old_json.to_s if old_json.is_a?(Symbol) + new_json = new_json.to_s if new_json.is_a?(Symbol) if old_json != new_json if print_values result << " update #{name} from #{old_json.inspect} to #{new_json.inspect}" @@ -134,8 +134,8 @@ def apply_modifiers(modifiers, json) end modifiers.each do |path, value| - path = [path] if !path.kind_of?(Array) - path = path.map { |path_part| path_part.to_s } + path = [path] unless path.is_a?(Array) + path = path.map(&:to_s) parent = 0.upto(path.size - 2).inject(json) do |hash, index| if hash.nil? nil @@ -148,6 +148,7 @@ def apply_modifiers(modifiers, json) if !parent.nil? && !parent.is_a?(Hash) raise "Attempt to set #{path} to #{value} when #{path[0..-2]} is not a hash" end + existing_value = parent ? parent[path[-1]] : nil if value.is_a?(Proc) @@ -158,7 +159,7 @@ def apply_modifiers(modifiers, json) else # Create parent if necessary, overwriting values parent = path[0..-2].inject(json) do |hash, path_part| - hash[path_part] = {} if !hash[path_part] + hash[path_part] = {} unless hash[path_part] hash[path_part] end if path.size > 0 @@ -173,6 +174,7 @@ def apply_modifiers(modifiers, json) def apply_run_list_modifiers(add_to_run_list, delete_from_run_list, run_list) return run_list if (!add_to_run_list || add_to_run_list.size == 0) && (!delete_from_run_list || !delete_from_run_list.size) + delete_from_run_list ||= [] add_to_run_list ||= [] @@ -191,7 +193,7 @@ def apply_run_list_modifiers(add_to_run_list, delete_from_run_list, run_list) # because found_desired will be less than add_to_run_list_index. The result will # be X, A, B, Y, Z. if found_desired >= add_to_run_list_index - result += add_to_run_list[add_to_run_list_index..found_desired].map { |item| item.to_s } + result += add_to_run_list[add_to_run_list_index..found_desired].map(&:to_s) add_to_run_list_index = found_desired + 1 end else @@ -204,7 +206,7 @@ def apply_run_list_modifiers(add_to_run_list, delete_from_run_list, run_list) end # Copy any remaining desired items at the end - result += add_to_run_list[add_to_run_list_index..-1].map { |item| item.to_s } + result += add_to_run_list[add_to_run_list_index..-1].map(&:to_s) result end diff --git a/lib/cheffish/basic_chef_client.rb b/lib/cheffish/basic_chef_client.rb index 64cbb08..5f960f5 100644 --- a/lib/cheffish/basic_chef_client.rb +++ b/lib/cheffish/basic_chef_client.rb @@ -14,7 +14,7 @@ class BasicChefClient include Chef::DSL::Recipe def initialize(node = nil, events = nil, **chef_config) - if !node + unless node node = Chef::Node.new node.name "basic_chef_client" node.automatic[:platform] = "basic_chef_client" @@ -31,7 +31,7 @@ def initialize(node = nil, events = nil, **chef_config) case events when Array events.each { |e| dispatcher.register(e) } if events - when !nil + when !nil # rubocop: disable Lint/LiteralAsCondition dispatcher.register(events) end @run_context = Chef::RunContext.new(node, {}, dispatcher) diff --git a/lib/cheffish/chef_actor_base.rb b/lib/cheffish/chef_actor_base.rb index 056320a..f1c26c2 100644 --- a/lib/cheffish/chef_actor_base.rb +++ b/lib/cheffish/chef_actor_base.rb @@ -24,9 +24,10 @@ def create_actor end else # Create the actor if it's missing - if !new_public_key + unless new_public_key raise "You must specify a public key to create a #{actor_type}! Use the private_key resource to create a key, and pass it in with source_key_path." end + description = [ "create #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences converge_by description do result = rest.post((actor_path).to_s, normalize_for_post(new_json)) diff --git a/lib/cheffish/chef_run.rb b/lib/cheffish/chef_run.rb index 5a46a03..0760734 100644 --- a/lib/cheffish/chef_run.rb +++ b/lib/cheffish/chef_run.rb @@ -34,15 +34,14 @@ def write(*args, &block) def client @client ||= begin chef_config = self.chef_config.dup - chef_config[:log_level] ||= :debug if !chef_config.key?(:log_level) - chef_config[:verbose_logging] = false if !chef_config.key?(:verbose_logging) + chef_config[:log_level] ||= :debug unless chef_config.key?(:log_level) + chef_config[:verbose_logging] = false unless chef_config.key?(:verbose_logging) chef_config[:stdout] = StringIOTee.new(chef_config[:stdout]) chef_config[:stderr] = StringIOTee.new(chef_config[:stderr]) chef_config[:log_location] = StringIOTee.new(chef_config[:log_location]) @client = ::Cheffish::BasicChefClient.new(nil, [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ], - chef_config - ) + chef_config) end end @@ -125,14 +124,14 @@ def output_for_failure_message message << "--- Chef Client Output ---\n" message << "--- ---\n" message << stdout - message << "\n" if !stdout.end_with?("\n") + message << "\n" unless stdout.end_with?("\n") end if stderr && !stderr.empty? message << "--- ---\n" message << "--- Chef Client Error Output ---\n" message << "--- ---\n" message << stderr - message << "\n" if !stderr.end_with?("\n") + message << "\n" unless stderr.end_with?("\n") end if logs && !logs.empty? message << "--- ---\n" diff --git a/lib/cheffish/chef_run_listener.rb b/lib/cheffish/chef_run_listener.rb index 26179f9..c93611a 100644 --- a/lib/cheffish/chef_run_listener.rb +++ b/lib/cheffish/chef_run_listener.rb @@ -21,9 +21,7 @@ def run_failed(exception) def disconnect # Stop the servers if node.run_context - node.run_context.cheffish.local_servers.each do |server| - server.stop - end + node.run_context.cheffish.local_servers.each(&:stop) end end end diff --git a/lib/cheffish/key_formatter.rb b/lib/cheffish/key_formatter.rb index efa1fba..40bf31e 100644 --- a/lib/cheffish/key_formatter.rb +++ b/lib/cheffish/key_formatter.rb @@ -58,6 +58,7 @@ def self.encode(key, key_format) if RUBY_VERSION.to_f >= 2.0 raise "PKCS8 SHA1 not supported in Ruby #{RUBY_VERSION}" end + require "openssl_pkcs8" pkcs8_pem = key.to_pem_pkcs8 pkcs8_base64 = pkcs8_pem.split("\n").reject { |l| l =~ /^-----/ } diff --git a/lib/cheffish/merged_config.rb b/lib/cheffish/merged_config.rb index 84c33c7..2d3cbd7 100644 --- a/lib/cheffish/merged_config.rb +++ b/lib/cheffish/merged_config.rb @@ -27,7 +27,7 @@ def [](name) result_configs = [] configs.each do |config| value = config[name] - if !value.nil? + unless value.nil? if value.respond_to?(:keys) result_configs << value elsif result_configs.size > 0 @@ -63,7 +63,7 @@ def key?(name) alias_method :has_key?, :key? def keys - configs.flat_map { |c| c.keys }.uniq + configs.flat_map(&:keys).uniq end def values diff --git a/lib/cheffish/node_properties.rb b/lib/cheffish/node_properties.rb index f9666c5..ed55ee5 100644 --- a/lib/cheffish/node_properties.rb +++ b/lib/cheffish/node_properties.rb @@ -39,7 +39,7 @@ def tag(*tags) attribute "tags" do |existing_tags| existing_tags ||= [] tags.each do |tag| - if !existing_tags.include?(tag.to_s) + unless existing_tags.include?(tag.to_s) existing_tags << tag.to_s end end @@ -64,8 +64,8 @@ def tags(*tags) if tags.size == 0 attribute("tags") else - tags = tags[0] if tags.size == 1 && tags[0].kind_of?(Array) - attribute("tags", tags.map { |tag| tag.to_s }) + tags = tags[0] if tags.size == 1 && tags[0].is_a?(Array) + attribute("tags", tags.map(&:to_s)) end end @@ -80,6 +80,7 @@ def recipe(*recipes) if recipes.size == 0 raise ArgumentError, "At least one recipe must be specified" end + @run_list_modifiers ||= [] @run_list_modifiers += recipes.map { |recipe| Chef::RunList::RunListItem.new("recipe[#{recipe}]") } end @@ -88,6 +89,7 @@ def role(*roles) if roles.size == 0 raise ArgumentError, "At least one role must be specified" end + @run_list_modifiers ||= [] @run_list_modifiers += roles.map { |role| Chef::RunList::RunListItem.new("role[#{role}]") } end @@ -96,6 +98,7 @@ def remove_recipe(*recipes) if recipes.size == 0 raise ArgumentError, "At least one recipe must be specified" end + @run_list_removers ||= [] @run_list_removers += recipes.map { |recipe| Chef::RunList::RunListItem.new("recipe[#{recipe}]") } end @@ -104,6 +107,7 @@ def remove_role(*roles) if roles.size == 0 raise ArgumentError, "At least one role must be specified" end + @run_list_removers ||= [] @run_list_removers += roles.map { |role| Chef::RunList::RunListItem.new("role[#{role}]") } end diff --git a/lib/cheffish/recipe_dsl.rb b/lib/cheffish/recipe_dsl.rb index 5d2a323..2331f84 100644 --- a/lib/cheffish/recipe_dsl.rb +++ b/lib/cheffish/recipe_dsl.rb @@ -52,7 +52,7 @@ def with_chef_local_server(options, &block) # Create the data store chef-zero will use options[:data_store] ||= begin - if !options[:chef_repo_path] + unless options[:chef_repo_path] raise "chef_repo_path must be specified to with_chef_local_server" end @@ -63,7 +63,7 @@ def with_chef_local_server(options, &block) symbol_key = "#{type}_path".to_sym options[symbol_key] ||= begin - if options[:chef_repo_path].kind_of?(String) + if options[:chef_repo_path].is_a?(String) Chef::Util::PathHelper.join(options[:chef_repo_path], "#{type}s") else options[:chef_repo_path].map { |path| Chef::Util::PathHelper.join(path, "#{type}s") } diff --git a/lib/cheffish/rspec/chef_run_support.rb b/lib/cheffish/rspec/chef_run_support.rb index 16a7ad1..96e5518 100644 --- a/lib/cheffish/rspec/chef_run_support.rb +++ b/lib/cheffish/rspec/chef_run_support.rb @@ -58,7 +58,7 @@ def expect_converge(str = nil, file = nil, line = nil, &recipe) end def recipe(str = nil, file = nil, line = nil, &recipe) - if !recipe + unless recipe if file && line recipe = proc { eval(str, nil, file, line) } # rubocop:disable Security/Eval else diff --git a/lib/cheffish/rspec/matchers/have_updated.rb b/lib/cheffish/rspec/matchers/have_updated.rb index b00c8a9..8987ddd 100644 --- a/lib/cheffish/rspec/matchers/have_updated.rb +++ b/lib/cheffish/rspec/matchers/have_updated.rb @@ -5,16 +5,16 @@ @recipe = recipe actual = @recipe.event_sink.events actual_actions = actual.select { |event, resource, action| event == :resource_updated && resource.to_s == resource_name } - .map { |event, resource, action| action } + .map { |event, resource, action| action } expect(actual_actions).to eq(expected_actions) end failure_message do actual = @recipe.event_sink.events updates = actual.select { |event, resource, action| event == :resource_updated }.to_a - result = "expected that the chef_run would #{expected_actions.join(',')} #{resource_name}." + result = "expected that the chef_run would #{expected_actions.join(",")} #{resource_name}." if updates.size > 0 - result << " Actual updates were #{updates.map { |event, resource, action| "#{resource} => #{action.inspect}" }.join(', ')}" + result << " Actual updates were #{updates.map { |event, resource, action| "#{resource} => #{action.inspect}" }.join(", ")}" else result << " Nothing was updated." end @@ -24,9 +24,9 @@ failure_message_when_negated do actual = @recipe.event_sink.events updates = actual.select { |event, resource, action| event == :resource_updated }.to_a - result = "expected that the chef_run would not #{expected_actions.join(',')} #{resource_name}." + result = "expected that the chef_run would not #{expected_actions.join(",")} #{resource_name}." if updates.size > 0 - result << " Actual updates were #{updates.map { |event, resource, action| "#{resource} => #{action.inspect}" }.join(', ')}" + result << " Actual updates were #{updates.map { |event, resource, action| "#{resource} => #{action.inspect}" }.join(", ")}" else result << " Nothing was updated." end diff --git a/lib/cheffish/rspec/recipe_run_wrapper.rb b/lib/cheffish/rspec/recipe_run_wrapper.rb index 5bcc043..ac03606 100644 --- a/lib/cheffish/rspec/recipe_run_wrapper.rb +++ b/lib/cheffish/rspec/recipe_run_wrapper.rb @@ -14,7 +14,7 @@ def initialize(chef_config, example: nil, &recipe) attr_reader :example def client - if !@client + unless @client super example = self.example diff --git a/lib/cheffish/rspec/repository_support.rb b/lib/cheffish/rspec/repository_support.rb index b405144..1ab0e1f 100644 --- a/lib/cheffish/rspec/repository_support.rb +++ b/lib/cheffish/rspec/repository_support.rb @@ -12,6 +12,7 @@ def when_the_repository(desc, *tags, &block) ::RSpec.shared_context "with a chef repo" do before :each do raise "Can only create one directory per test" if @repository_dir + @repository_dir = Dir.mktmpdir("chef_repo") Chef::Config.chef_repo_path = @repository_dir %w{client cookbook data_bag environment node role user}.each do |object_name| diff --git a/spec/functional/fingerprint_spec.rb b/spec/functional/fingerprint_spec.rb index 53f75ef..20dd523 100644 --- a/spec/functional/fingerprint_spec.rb +++ b/spec/functional/fingerprint_spec.rb @@ -41,22 +41,26 @@ def key_to_format(key, format) it "computes the PKCS#8 SHA1 private key fingerprint correctly", pending: (RUBY_VERSION.to_f >= 2.0) do expect(key_to_format(sample_private_key, :pkcs8sha1fingerprint)).to eq( - "88:7e:3a:bd:26:9f:b5:c5:d8:ae:52:f9:df:0b:64:a4:5c:17:0a:87") + "88:7e:3a:bd:26:9f:b5:c5:d8:ae:52:f9:df:0b:64:a4:5c:17:0a:87" + ) end it "computes the PKCS#1 MD5 public key fingerprint correctly" do expect(key_to_format(sample_public_key, :pkcs1md5fingerprint)).to eq( - "1f:e8:da:c1:16:c3:72:7d:90:e2:b7:64:c4:b4:55:20") + "1f:e8:da:c1:16:c3:72:7d:90:e2:b7:64:c4:b4:55:20" + ) end it "computes the RFC4716 MD5 public key fingerprint correctly" do expect(key_to_format(sample_public_key, :rfc4716md5fingerprint)).to eq( - "b0:13:4f:da:cf:8c:dc:a7:4a:1f:d2:3a:51:92:cf:6b") + "b0:13:4f:da:cf:8c:dc:a7:4a:1f:d2:3a:51:92:cf:6b" + ) end it "defaults to the PKCS#1 MD5 public key fingerprint" do expect(key_to_format(sample_public_key, :fingerprint)).to eq( - key_to_format(sample_public_key, :pkcs1md5fingerprint)) + key_to_format(sample_public_key, :pkcs1md5fingerprint) + ) end end diff --git a/spec/integration/chef_acl_spec.rb b/spec/integration/chef_acl_spec.rb index 07aeab5..cebd1aa 100644 --- a/spec/integration/chef_acl_spec.rb +++ b/spec/integration/chef_acl_spec.rb @@ -160,7 +160,7 @@ it 'Converging chef_acl "nodes/x" with rights [ :read, :create, :update, :delete, :grant ] modifies all rights' do expect_recipe do chef_acl "nodes/x" do - rights [ :create, :read, :update, :delete, :grant ], users: %w{u1 u2}, clients: %w{c1}, groups: %w{g1} + rights %i{create read update delete grant}, users: %w{u1 u2}, clients: %w{c1}, groups: %w{g1} end end.to be_updated expect(get("nodes/x/_acl")).to partially_match( @@ -229,11 +229,11 @@ end end.to be_updated expect(get("nodes/x/_acl")).to partially_match( - "create" => { "actors" => exclude("foo") }, - "read" => { "actors" => exclude("foo") }, - "update" => { "actors" => exclude("foo") }, - "delete" => { "actors" => exclude("foo") }, - "grant" => { "actors" => exclude("foo") } + "create" => { "actors" => exclude("foo") }, + "read" => { "actors" => exclude("foo") }, + "update" => { "actors" => exclude("foo") }, + "delete" => { "actors" => exclude("foo") }, + "grant" => { "actors" => exclude("foo") } ) end end @@ -361,7 +361,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles}.each do |type| expect(get("/organizations/foo/#{type}/x/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end end @@ -408,7 +409,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles sandboxes}.each do |type| expect(get("/organizations/foo/#{type}/x/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end @@ -420,7 +422,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles}.each do |type| expect(get("/organizations/foo/#{type}/x/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end @@ -447,7 +450,7 @@ chef_acl "/organizations/foo/cookbooks/x/1.0.0" do rights :read, users: %w{u} end - end.to raise_error(/ACLs cannot be set on children of \/organizations\/foo\/cookbooks\/x/) + end.to raise_error(%r{ACLs cannot be set on children of /organizations/foo/cookbooks/x}) end it "chef_acl '/organizations/foo/cookbooks/*/*' raises an error" do @@ -456,7 +459,7 @@ chef_acl "/organizations/foo/cookbooks/*/*" do rights :read, users: %w{u} end - end.to raise_error(/ACLs cannot be set on children of \/organizations\/foo\/cookbooks\/*/) + end.to raise_error(%r{ACLs cannot be set on children of /organizations/foo/cookbooks/*}) end it 'chef_acl "/organizations/foo/data/x/y" raises an error' do @@ -464,7 +467,7 @@ chef_acl "/organizations/foo/data/x/y" do rights :read, users: %w{u} end - end.to raise_error(/ACLs cannot be set on children of \/organizations\/foo\/data\/x/) + end.to raise_error(%r{ACLs cannot be set on children of /organizations/foo/data/x}) end it 'chef_acl "/organizations/foo/data/*/*" raises an error' do @@ -473,7 +476,7 @@ chef_acl "/organizations/foo/data/*/*" do rights :read, users: %w{u} end - end.to raise_error(/ACLs cannot be set on children of \/organizations\/foo\/data\/*/) + end.to raise_error(%r{ACLs cannot be set on children of /organizations/foo/data/*}) end it 'chef_acl "/organizations/foo" changes the acl' do @@ -609,7 +612,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles sandboxes}.each do |type| expect(get("#{type}/x/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end @@ -621,7 +625,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles}.each do |type| expect(get("#{type}/x/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end @@ -633,7 +638,8 @@ end.to be_updated %w{admins billing-admins clients users x}.each do |n| expect(get("groups/#{n}/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end @@ -713,7 +719,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles sandboxes}.each do |type| expect(get("/organizations/foo/containers/#{type}/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end @@ -768,7 +775,8 @@ end.to be_updated %w{clients containers cookbooks data environments groups nodes roles sandboxes}.each do |type| expect(get("containers/#{type}/_acl")).to partially_match( - "read" => { "actors" => %w{u} }) + "read" => { "actors" => %w{u} } + ) end end end @@ -820,7 +828,7 @@ it 'chef_acl with remove_rights [ :create, :read ], "u", "c", "g" removes all three' do expect_recipe do chef_acl "nodes/x" do - remove_rights [ :create, :read ], users: %w{u}, clients: %w{c}, groups: %w{g} + remove_rights %i{create read}, users: %w{u}, clients: %w{c}, groups: %w{g} end end.to be_updated expect(get("nodes/x/_acl")).to partially_match(