diff options
author | Joshua C. Burt <joshburt@shapeandshare.com> | 2016-05-11 08:22:26 -0600 |
---|---|---|
committer | Joshua C. Burt <joshburt@shapeandshare.com> | 2016-05-11 08:22:26 -0600 |
commit | 632e99689cbdc8108231ec28b781412d2481bd0a (patch) | |
tree | 1dd19747dbc477992b4e7d311c8b61db44317cce | |
parent | 5d91f332d1435edd4d44a855c60dc4340a006af3 (diff) | |
parent | 6fe31094c894780116d8438c040e04632d9d01c0 (diff) | |
download | chef-632e99689cbdc8108231ec28b781412d2481bd0a.tar.gz |
Merge branch 'master' into jb_git_new_resource_destination_to_cwd
67 files changed, 593 insertions, 406 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 24f67c60a9..ed8f5de90f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,9 +18,9 @@ GIT PATH remote: . specs: - chef (12.10.33) + chef (12.10.43) bundler (>= 1.10) - chef-config (= 12.10.33) + chef-config (= 12.10.43) chef-zero (~> 4.5) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -45,9 +45,9 @@ PATH specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (12.10.33-universal-mingw32) + chef (12.10.43-universal-mingw32) bundler (>= 1.10) - chef-config (= 12.10.33) + chef-config (= 12.10.43) chef-zero (~> 4.5) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -87,7 +87,7 @@ PATH PATH remote: chef-config specs: - chef-config (12.10.33) + chef-config (12.10.43) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) @@ -100,12 +100,12 @@ GEM mixlib-cli (~> 1.4) artifactory (2.3.2) ast (2.2.0) - aws-sdk (2.3.1) - aws-sdk-resources (= 2.3.1) - aws-sdk-core (2.3.1) + aws-sdk (2.3.2) + aws-sdk-resources (= 2.3.2) + aws-sdk-core (2.3.2) jmespath (~> 1.0) - aws-sdk-resources (2.3.1) - aws-sdk-core (= 2.3.1) + aws-sdk-resources (2.3.2) + aws-sdk-core (= 2.3.2) aws-sdk-v1 (1.66.0) json (~> 1.4) nokogiri (>= 1.4.4) @@ -145,8 +145,6 @@ GEM chef (>= 11.14) fauxhai (~> 3.2) rspec (~> 3.0) - childprocess (0.5.9) - ffi (~> 1.0, >= 1.0.11) coderay (1.1.1) colorize (0.7.7) compat_resource (12.9.1) @@ -183,14 +181,10 @@ GEM hashie (>= 3.4) multi_json (>= 1.7.5, < 2.0) oauth2 - github_changelog_generator (1.12.0) - bundler (>= 1.7) + github_changelog_generator (1.12.1) colorize (~> 0.7) github_api (~> 0.12) - overcommit (>= 0.31) rake (>= 10.0) - rspec (>= 3.2) - rubocop (>= 0.31) gssapi (1.2.0) ffi (>= 1.0.1) gyoku (1.3.1) @@ -229,7 +223,7 @@ GEM rspec-core (~> 3.2) rspec-expectations (~> 3.2) rspec-mocks (~> 3.2) - mixlib-cli (1.5.0) + mixlib-cli (1.6.0) mixlib-config (2.2.1) mixlib-install (1.0.11) artifactory @@ -281,9 +275,6 @@ GEM plist (~> 3.1) systemu (~> 2.6.4) wmi-lite (~> 1.0) - overcommit (0.33.0) - childprocess (~> 0.5.8) - iniparse (~> 1.4) parser (2.3.1.0) ast (~> 2.2) plist (3.2.0) @@ -339,11 +330,11 @@ GEM ruby-progressbar (1.8.0) ruby-shadow (2.5.0) rubyntlm (0.6.0) - rufus-lru (1.0.5) + rufus-lru (1.1.0) sawyer (0.7.0) addressable (>= 2.3.5, < 2.5) faraday (~> 0.8, < 0.10) - serverspec (2.33.0) + serverspec (2.34.0) multi_json rspec (~> 3.0) rspec-its @@ -392,7 +383,7 @@ GEM ffi windows-api (0.4.4) win32-api (>= 1.4.5) - winrm (1.8.0) + winrm (1.8.1) builder (>= 2.1.2) gssapi (~> 1.2) gyoku (~> 1.0) @@ -157,7 +157,7 @@ Additionally, periodically Chef will update the desired versions of chef compone Whenever a change is checked in to `master`, the patch version of `chef` is bumped. To do this, the `lita-versioner` bot listens to github for merged PRs, and when it finds one, takes these actions: 1. Bumps the patch version in `lib/chef/version.rb` (e.g. 0.9.14 -> 0.9.15). -2. Runs `rake dependencies:update_conservative` to update the `Gemfile.lock` to include the new version. +2. Runs `rake bundle:install` to update the `Gemfile.lock` to include the new version. 3. Pushes to `master` and submits a new build to Chef's Jenkins cluster. ## Component Versions @@ -1 +1 @@ -12.10.33
\ No newline at end of file +12.10.43
\ No newline at end of file diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock index 009c7cee73..108fb83509 100644 --- a/acceptance/Gemfile.lock +++ b/acceptance/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: git://github.com/chef/chef-acceptance.git - revision: 49458ec493dbd12588680eea9f2f9beb76463d09 + revision: 4abda84e2fadd0452b77a8a88d4fd534843ced55 specs: chef-acceptance (0.2.0) mixlib-shellout (~> 2.0) @@ -11,12 +11,12 @@ GEM specs: addressable (2.4.0) artifactory (2.3.2) - aws-sdk (2.2.34) - aws-sdk-resources (= 2.2.34) - aws-sdk-core (2.2.34) + aws-sdk (2.3.2) + aws-sdk-resources (= 2.3.2) + aws-sdk-core (2.3.2) jmespath (~> 1.0) - aws-sdk-resources (2.2.34) - aws-sdk-core (= 2.2.34) + aws-sdk-resources (2.3.2) + aws-sdk-core (= 2.3.2) berkshelf (4.3.2) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) @@ -52,7 +52,7 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.9.38) + chef-config (12.9.41) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) @@ -72,14 +72,14 @@ GEM ffi (>= 1.0.1) gyoku (1.3.1) builder (>= 2.1.2) - hashie (3.4.3) - hitimes (1.2.3) - httpclient (2.7.1) - inspec (0.19.1) + hashie (3.4.4) + hitimes (1.2.4) + httpclient (2.7.2) + inspec (0.20.1) json (~> 1.8) method_source (~> 0.8) pry (~> 0) - r-train (~> 0.10.5) + r-train (~> 0.11) rainbow (~> 2) rspec (~> 3) rspec-its (~> 1.2) @@ -112,15 +112,15 @@ GEM rspec-expectations (~> 3.2) rspec-mocks (~> 3.2) mixlib-config (2.2.1) - mixlib-install (1.0.7) + mixlib-install (1.0.11) artifactory mixlib-shellout mixlib-versioning mixlib-log (1.6.0) mixlib-shellout (2.2.6) mixlib-versioning (1.1.0) - molinillo (0.4.4) - multi_json (1.11.2) + molinillo (0.4.5) + multi_json (1.12.0) multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) @@ -133,10 +133,10 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - r-train (0.10.5) + r-train (0.11.2) docker-api (~> 1.26.2) json (~> 1.8) - mixlib-shellout (~> 2.1) + mixlib-shellout (~> 2.0) net-scp (~> 1.2) net-ssh (>= 2.9, < 4.0) winrm (~> 1.6) @@ -188,7 +188,7 @@ GEM solve (2.0.3) molinillo (~> 0.4.2) semverse (~> 1.1) - test-kitchen (1.7.3) + test-kitchen (1.8.0) mixlib-install (~> 1.0, >= 1.0.4) mixlib-shellout (>= 1.2, < 3.0) net-scp (~> 1.1) @@ -203,7 +203,7 @@ GEM hashie (>= 2.0.2, < 4.0.0) windows_chef_zero (2.0.0) test-kitchen (>= 1.2.1) - winrm (1.7.3) + winrm (1.8.1) builder (>= 2.1.2) gssapi (~> 1.2) gyoku (~> 1.0) diff --git a/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb b/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb index 59a888bef1..2a087f8029 100644 --- a/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb +++ b/acceptance/fips/test/integration/fips-integration/serverspec/fips-integration_spec.rb @@ -46,6 +46,7 @@ describe "Chef Fips Integration Specs" do end it "passes the integration specs" do - run_rspec_test("spec/integration") + skip + #run_rspec_test("spec/integration") end end diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index 0ce305aaf3..aab13cea46 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.10.33" + VERSION = "12.10.43" end # diff --git a/ci/verify-chef.sh b/ci/verify-chef.sh index 6c6c4606de..1f2413a07e 100755 --- a/ci/verify-chef.sh +++ b/ci/verify-chef.sh @@ -100,10 +100,10 @@ if [ "x$ACCEPTANCE" != "x" ]; then export PATH # Test against the Chef bundle - sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD pwd - sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD bundle config - sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD bundle install - sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID ARTIFACTORY_USERNAME=$ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD KITCHEN_DRIVER=ec2 bundle exec chef-acceptance test --force-destroy + sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID pwd + sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID bundle config + sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID bundle install + sudo env PATH=$PATH AWS_SSH_KEY_ID=$AWS_SSH_KEY_ID KITCHEN_DRIVER=ec2 KITCHEN_CHEF_CHANNEL=unstable bundle exec chef-acceptance test --force-destroy --data-path $WORKSPACE/chef-acceptance-data else PATH=/opt/$PROJECT_NAME/bin:/opt/$PROJECT_NAME/embedded/bin:$PATH export PATH diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock index ee5027e4b7..2c3b22b985 100644 --- a/kitchen-tests/Berksfile.lock +++ b/kitchen-tests/Berksfile.lock @@ -46,14 +46,14 @@ GRAPH database (~> 2.3.1) mysql (>= 0.0.0) php (>= 0.0.0) - windows (1.39.2) + windows (1.40.0) chef_handler (>= 0.0.0) xfs (2.0.1) xml (2.0.0) build-essential (>= 0.0.0) chef-sugar (>= 0.0.0) yum (3.10.0) - yum-epel (0.6.6) - yum (~> 3.10.0) + yum-epel (0.7.0) + yum (>= 3.6.3) yum-mysql-community (0.2.0) yum (>= 3.2) diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock index 78caba0014..3067871ab3 100644 --- a/kitchen-tests/Gemfile.lock +++ b/kitchen-tests/Gemfile.lock @@ -14,12 +14,12 @@ GEM specs: addressable (2.4.0) artifactory (2.3.2) - aws-sdk (2.2.34) - aws-sdk-resources (= 2.2.34) - aws-sdk-core (2.2.34) + aws-sdk (2.3.2) + aws-sdk-resources (= 2.3.2) + aws-sdk-core (2.3.2) jmespath (~> 1.0) - aws-sdk-resources (2.2.34) - aws-sdk-core (= 2.2.34) + aws-sdk-resources (2.3.2) + aws-sdk-core (= 2.3.2) berkshelf (4.3.2) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) @@ -54,7 +54,7 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.9.38) + chef-config (12.9.41) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) @@ -66,10 +66,10 @@ GEM multipart-post (>= 1.2, < 3) ffi (1.9.10-x86-mingw32) fuzzyurl (0.8.0) - hashie (3.4.3) - hitimes (1.2.3) - hitimes (1.2.3-x86-mingw32) - httpclient (2.7.1) + hashie (3.4.4) + hitimes (1.2.4) + hitimes (1.2.4-x86-mingw32) + httpclient (2.7.2) jmespath (1.2.4) json_pure (>= 1.8.1) json (1.8.3) @@ -84,7 +84,7 @@ GEM rspec-expectations (~> 3.2) rspec-mocks (~> 3.2) mixlib-config (2.2.1) - mixlib-install (1.0.7) + mixlib-install (1.0.11) artifactory mixlib-shellout mixlib-versioning @@ -94,8 +94,8 @@ GEM win32-process (~> 0.8.2) wmi-lite (~> 1.0) mixlib-versioning (1.1.0) - molinillo (0.4.4) - multi_json (1.11.2) + molinillo (0.4.5) + multi_json (1.12.0) multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) @@ -139,7 +139,7 @@ GEM solve (2.0.3) molinillo (~> 0.4.2) semverse (~> 1.1) - test-kitchen (1.7.3) + test-kitchen (1.8.0) mixlib-install (~> 1.0, >= 1.0.4) mixlib-shellout (>= 1.2, < 3.0) net-scp (~> 1.1) diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb index 4a9a712c20..c824bc90df 100644 --- a/lib/chef/chef_fs/command_line.rb +++ b/lib/chef/chef_fs/command_line.rb @@ -63,6 +63,13 @@ class Chef end when :deleted + # This is kind of a kludge - because the "new" entry isn't there, we can't predict + # it's true file name, because we've not got enough information. So because we know + # the two entries really ought to have the same extension, we'll just grab the old one + # and use it. (This doesn't affect cookbook files, since they'll always have extensions) + if File.extname(old_path) != File.extname(new_path) + new_path += File.extname(old_path) + end next if diff_filter && diff_filter !~ /D/ if output_mode == :name_only yield "#{new_path}\n" diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb index 6e3cc50ac1..63a1363724 100644 --- a/lib/chef/chef_fs/config.rb +++ b/lib/chef/chef_fs/config.rb @@ -242,7 +242,7 @@ class Chef # Print the given server path, relative to the current directory def format_path(entry) - server_path = entry.path + server_path = entry.respond_to?(:display_path) ? entry.display_path : entry.path if base_path && server_path[0, base_path.length] == base_path if server_path == base_path return "." diff --git a/lib/chef/chef_fs/data_handler/data_handler_base.rb b/lib/chef/chef_fs/data_handler/data_handler_base.rb index b30ae9c708..b34aff4b98 100644 --- a/lib/chef/chef_fs/data_handler/data_handler_base.rb +++ b/lib/chef/chef_fs/data_handler/data_handler_base.rb @@ -24,15 +24,14 @@ class Chef object end - # - # Takes a name like blah.json and removes the .json from it. - # - def remove_dot_json(name) - if name.length < 5 || name[-5, 5] != ".json" - raise "Invalid name #{path}: must end in .json" + def remove_file_extension(name, ext = ".*") + if %w{ .rb .json }.include?(File.extname(name)) + File.basename(name, ext) + else + name end - name[0, name.length - 5] end + alias_method :remove_dot_json, :remove_file_extension # # Return true if minimize() should preserve a key even if it is the same @@ -109,8 +108,10 @@ class Chef # # Bring in an instance of this object from Ruby. (Like roles/x.rb) # - def from_ruby(ruby) - chef_class.from_file(ruby).to_hash + def from_ruby(path) + r = chef_class.new + r.from_file(path) + r.to_hash end # @@ -192,7 +193,7 @@ class Chef # @yield [s] callback to handle errors # @yieldparam [s<string>] error message def verify_integrity(object, entry) - base_name = remove_dot_json(entry.name) + base_name = remove_file_extension(entry.name) if object["name"] != base_name yield("Name must be '#{base_name}' (is '#{object['name']}')") end diff --git a/lib/chef/chef_fs/data_handler/environment_data_handler.rb b/lib/chef/chef_fs/data_handler/environment_data_handler.rb index 8d066764be..68f6daee9a 100644 --- a/lib/chef/chef_fs/data_handler/environment_data_handler.rb +++ b/lib/chef/chef_fs/data_handler/environment_data_handler.rb @@ -7,7 +7,7 @@ class Chef class EnvironmentDataHandler < DataHandlerBase def normalize(environment, entry) normalize_hash(environment, { - "name" => remove_dot_json(entry.name), + "name" => remove_file_extension(entry.name), "description" => "", "cookbook_versions" => {}, "default_attributes" => {}, diff --git a/lib/chef/chef_fs/data_handler/role_data_handler.rb b/lib/chef/chef_fs/data_handler/role_data_handler.rb index 74533cff05..b09c146a5d 100644 --- a/lib/chef/chef_fs/data_handler/role_data_handler.rb +++ b/lib/chef/chef_fs/data_handler/role_data_handler.rb @@ -7,7 +7,7 @@ class Chef class RoleDataHandler < DataHandlerBase def normalize(role, entry) result = normalize_hash(role, { - "name" => remove_dot_json(entry.name), + "name" => remove_file_extension(entry.name), "description" => "", "json_class" => "Chef::Role", "chef_type" => "role", diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb index 2b553b19a7..69dce54f00 100644 --- a/lib/chef/chef_fs/file_system.rb +++ b/lib/chef/chef_fs/file_system.rb @@ -52,13 +52,13 @@ class Chef def list_from(entry, &block) # Include self in results if it matches - if pattern.match?(entry.path) + if pattern.match?(entry.display_path) yield(entry) end - if pattern.could_match_children?(entry.path) + if pattern.could_match_children?(entry.display_path) # If it's possible that our children could match, descend in and add matches. - exact_child_name = pattern.exact_child_name_under(entry.path) + exact_child_name = pattern.exact_child_name_under(entry.display_path) # If we've got an exact name, don't bother listing children; just grab the # child with the given name. @@ -186,16 +186,16 @@ class Chef # Make sure everything on the server is also on the filesystem, and diff found_paths = Set.new Chef::ChefFS::FileSystem.list(a_root, pattern).each do |a| - found_paths << a.path - b = Chef::ChefFS::FileSystem.resolve_path(b_root, a.path) + found_paths << a.display_path + b = Chef::ChefFS::FileSystem.resolve_path(b_root, a.display_path) yield [ a, b ] end # Check the outer regex pattern to see if it matches anything on the # filesystem that isn't on the server Chef::ChefFS::FileSystem.list(b_root, pattern).each do |b| - if !found_paths.include?(b.path) - a = Chef::ChefFS::FileSystem.resolve_path(a_root, b.path) + if !found_paths.include?(b.display_path) + a = Chef::ChefFS::FileSystem.resolve_path(a_root, b.display_path) yield [ a, b ] end end @@ -222,14 +222,14 @@ class Chef result = [] a_children_names = Set.new a.children.each do |a_child| - a_children_names << a_child.name - result << [ a_child, b.child(a_child.name) ] + a_children_names << a_child.bare_name + result << [ a_child, b.child(a_child.bare_name) ] end # Check b for children that aren't in a b.children.each do |b_child| - if !a_children_names.include?(b_child.name) - result << [ a.child(b_child.name), b_child ] + if !a_children_names.include?(b_child.bare_name) + result << [ a.child(b_child.bare_name), b_child ] end end result @@ -327,8 +327,8 @@ class Chef if options[:dry_run] ui.output "Would create #{dest_path}" if ui else - new_dest_parent.create_child(src_entry.name, src_entry.read) - ui.output "Created #{dest_path}" if ui + child = new_dest_parent.create_child(src_entry.name, src_entry.read) + ui.output "Created #{format_path.call(child)}" if ui end end end @@ -392,6 +392,8 @@ class Chef end end end + rescue RubyFileError => e + ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui rescue DefaultEnvironmentCannotBeModifiedError => e ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui rescue OperationFailedError => e diff --git a/lib/chef/chef_fs/file_system/base_fs_object.rb b/lib/chef/chef_fs/file_system/base_fs_object.rb index 6abbcf343f..9767b5b1ba 100644 --- a/lib/chef/chef_fs/file_system/base_fs_object.rb +++ b/lib/chef/chef_fs/file_system/base_fs_object.rb @@ -40,6 +40,10 @@ class Chef attr_reader :parent attr_reader :path + alias_method :display_path, :path + alias_method :display_name, :name + alias_method :bare_name, :name + # Override this if you have a special comparison algorithm that can tell # you whether this entry is the same as another--either a quicker or a # more reliable one. Callers will use this to decide whether to upload, diff --git a/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb b/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb index 9f4d13f5a9..006098a0c9 100644 --- a/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server/acl_dir.rb @@ -35,7 +35,7 @@ class Chef end def can_have_child?(name, is_dir) - name =~ /\.json$/ && !is_dir + !is_dir end def children diff --git a/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb b/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb index 68df3704bf..f4655412fa 100644 --- a/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_server/acl_entry.rb @@ -30,6 +30,15 @@ class Chef "#{super}/_acl" end + def display_path + pth = if parent.name == "acls" + "/acls/#{name}" + else + "/acls/#{parent.name}/#{name}" + end + File.extname(pth).empty? ? pth + ".json" : pth + end + def delete(recurse) raise Chef::ChefFS::FileSystem::OperationNotAllowedError.new(:delete, self, nil, "ACLs cannot be deleted") end diff --git a/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb b/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb index 5ad0063807..ee0ecd3b40 100644 --- a/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server/data_bag_dir.rb @@ -17,6 +17,7 @@ # require "chef/chef_fs/file_system/chef_server/rest_list_dir" +require "chef/chef_fs/file_system/chef_server/data_bag_entry" require "chef/chef_fs/file_system/exceptions" require "chef/chef_fs/data_handler/data_bag_item_data_handler" @@ -63,6 +64,11 @@ class Chef end end end + + def make_child_entry(name, exists = nil) + @children.find { |child| child.name == name } if @children + DataBagEntry.new(name, self, exists) + end end end end diff --git a/lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb b/lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb new file mode 100644 index 0000000000..c0093058b7 --- /dev/null +++ b/lib/chef/chef_fs/file_system/chef_server/data_bag_entry.rb @@ -0,0 +1,19 @@ +require "chef/chef_fs/file_system/chef_server/rest_list_entry" +require "chef/chef_fs/data_handler/data_bag_item_data_handler" + +class Chef + module ChefFS + module FileSystem + module ChefServer + # /policies/NAME-REVISION.json + # Represents the actual data at /organizations/ORG/policies/NAME/revisions/REVISION + class DataBagEntry < RestListEntry + def display_path + pth = "/data_bags/#{parent.name}/#{name}" + File.extname(pth).empty? ? pth + ".json" : pth + end + end + end + end + end +end diff --git a/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb b/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb index 81ae81ac0d..e4714cf089 100644 --- a/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server/environments_dir.rb @@ -26,7 +26,7 @@ class Chef module ChefServer class EnvironmentsDir < RestListDir def make_child_entry(name, exists = nil) - if name == "_default.json" + if File.basename(name, ".*") == "_default" DefaultEnvironmentEntry.new(name, self, exists) else super diff --git a/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb b/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb index 4f83b89711..df2388f1df 100644 --- a/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server/nodes_dir.rb @@ -30,7 +30,7 @@ class Chef def children begin @children ||= root.get_json(env_api_path).keys.sort.map do |key| - make_child_entry("#{key}.json", true) + make_child_entry(key, true) end rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}") diff --git a/lib/chef/chef_fs/file_system/chef_server/org_entry.rb b/lib/chef/chef_fs/file_system/chef_server/org_entry.rb index 87be36b932..7253de3449 100644 --- a/lib/chef/chef_fs/file_system/chef_server/org_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_server/org_entry.rb @@ -17,6 +17,10 @@ class Chef parent.api_path end + def display_path + "/org.json" + end + def exists? parent.exists? end diff --git a/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb b/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb index 37b7af8b5e..adaffb99a7 100644 --- a/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb @@ -27,6 +27,10 @@ class Chef File.join(parent.api_path, "association_requests") end + def display_path + "/invitations.json" + end + def exists? parent.exists? end diff --git a/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb b/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb index 2e45b74450..7e9c7141c4 100644 --- a/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_server/organization_members_entry.rb @@ -27,6 +27,10 @@ class Chef File.join(parent.api_path, "users") end + def display_path + "/members.json" + end + def exists? parent.exists? end diff --git a/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb b/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb index d083383a0e..325b18e429 100644 --- a/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_server/policy_revision_entry.rb @@ -14,6 +14,10 @@ class Chef "#{parent.api_path}/#{policy_name}/revisions/#{revision_id}" end + def display_path + "/policies/#{policy_name}-#{revision_id}.json" + end + def write(file_contents) raise OperationNotAllowedError.new(:write, self, nil, "cannot be updated: policy revisions are immutable once uploaded. If you want to change the policy, create a new revision with your changes") end diff --git a/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb b/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb index 276f7760b5..dfd26a0241 100644 --- a/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server/rest_list_dir.rb @@ -35,7 +35,7 @@ class Chef attr_reader :data_handler def can_have_child?(name, is_dir) - name =~ /\.json$/ && !is_dir + !is_dir end # @@ -74,7 +74,7 @@ class Chef begin # Grab the names of the children, append json, and make child entries @children ||= root.get_json(api_path).keys.sort.map do |key| - make_child_entry("#{key}.json", true) + make_child_entry(key, true) end rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e, "Timeout retrieving children: #{e}") @@ -85,7 +85,7 @@ class Chef if parent.is_a?(ChefServerRootDir) # GET /organizations/ORG/<container> returned 404, but that just might be because # we are talking to an older version of the server that doesn't support policies. - # Do GET /orgqanizations/ORG to find out if the org exists at all. + # Do GET /organizations/ORG to find out if the org exists at all. # TODO use server API version instead of a second network request. begin root.get_json(parent.api_path) diff --git a/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb b/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb index 9b16bd80de..b8ec5f8524 100644 --- a/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_server/rest_list_entry.rb @@ -37,16 +37,27 @@ class Chef end def api_child_name - if name.length < 5 || name[-5, 5] != ".json" - raise "Invalid name #{path}: must end in .json" + if %w{ .rb .json }.include? File.extname(name) + File.basename(name, ".*") + else + name end - name[0, name.length - 5] end def api_path "#{parent.api_path}/#{api_child_name}" end + def display_path + pth = api_path.start_with?("/") ? api_path : "/#{api_path}" + File.extname(pth).empty? ? pth + ".json" : pth + end + alias_method :path_for_printing, :display_path + + def display_name + File.basename(display_path) + end + def org parent.org end @@ -58,7 +69,7 @@ class Chef def exists? if @exists.nil? begin - @exists = parent.children.any? { |child| child.name == name } + @exists = parent.children.any? { |child| child.api_child_name == api_child_name } rescue Chef::ChefFS::FileSystem::NotFoundError @exists = false end diff --git a/lib/chef/chef_fs/file_system/exceptions.rb b/lib/chef/chef_fs/file_system/exceptions.rb index cf4916e4c8..2a1baba8f5 100644 --- a/lib/chef/chef_fs/file_system/exceptions.rb +++ b/lib/chef/chef_fs/file_system/exceptions.rb @@ -86,6 +86,13 @@ class Chef class CookbookFrozenError < AlreadyExistsError; end + class RubyFileError < OperationNotAllowedError + def reason + result = super + result + " (can't safely update ruby files)" + end + end + class DefaultEnvironmentCannotBeModifiedError < OperationNotAllowedError def reason result = super diff --git a/lib/chef/chef_fs/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb index e143dde9e8..d3951edd51 100644 --- a/lib/chef/chef_fs/file_system/multiplexed_dir.rb +++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb @@ -24,7 +24,7 @@ class Chef multiplexed_dirs.each do |dir| dir.children.each do |child| if seen[child.name] - Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{seen[child.name].path_for_printing} and #{child.path_for_printing}") + Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{seen[child.name].path_for_printing} and #{child.path_for_printing}") unless seen[child.name].path_for_printing == child.path_for_printing else result << child seen[child.name] = child @@ -41,7 +41,7 @@ class Chef child_entry = dir.child(name) if child_entry.exists? if result - Chef::Log.warn("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}") + Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}") unless seen[child.name].path_for_printing == child.path_for_printing else result = child_entry end diff --git a/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb b/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb index b78dcb6c0b..1a48d23047 100644 --- a/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb +++ b/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb @@ -23,10 +23,6 @@ class Chef module ChefFS module FileSystem class NonexistentFSObject < BaseFSObject - def initialize(name, parent) - super - end - def exists? false end diff --git a/lib/chef/chef_fs/file_system/repository/acl.rb b/lib/chef/chef_fs/file_system/repository/acl.rb index e2ba2e8771..023ae11379 100644 --- a/lib/chef/chef_fs/file_system/repository/acl.rb +++ b/lib/chef/chef_fs/file_system/repository/acl.rb @@ -31,6 +31,13 @@ class Chef super end + def bare_name + if name == "organization" && parent.kind_of?(AclDir) + "organization.json" + else + name + end + end end end end diff --git a/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb b/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb index 8eee8e1d70..70c7d77480 100644 --- a/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/acls_sub_dir.rb @@ -26,10 +26,6 @@ class Chef module Repository class AclsSubDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - protected def make_child_entry(child_name) diff --git a/lib/chef/chef_fs/file_system/repository/base_file.rb b/lib/chef/chef_fs/file_system/repository/base_file.rb index d5ef26887e..a768bcf971 100644 --- a/lib/chef/chef_fs/file_system/repository/base_file.rb +++ b/lib/chef/chef_fs/file_system/repository/base_file.rb @@ -29,23 +29,48 @@ class Chef attr_reader :file_path attr_reader :data_handler + alias_method :display_path, :path + alias_method :display_name, :name + def initialize(name, parent) @parent = parent + + if %w{ .rb .json }.include? File.extname(name) + name = File.basename(name, ".*") + end + + file_path = "#{parent.file_path}/#{name}" + + Chef::Log.debug "BaseFile: Detecting file extension for #{name}" + ext = File.exist?(file_path + ".rb") ? ".rb" : ".json" + name += ext + file_path += ext + + Chef::Log.debug "BaseFile: got a file path of #{file_path} for #{name}" @name = name @path = Chef::ChefFS::PathUtils.join(parent.path, name) - @file_path = "#{parent.file_path}/#{name}" + @file_path = file_path end def dir? false end + # Used to compare names on disk to the API, for diffing. + def bare_name + File.basename(name, ".*") + end + def is_json_file? - File.extname(name) == ".json" + File.extname(file_path) == ".json" + end + + def is_ruby_file? + File.extname(file_path) == ".rb" end def name_valid? - !name.start_with?(".") && is_json_file? + !name.start_with?(".") && (is_json_file? || is_ruby_file?) end def fs_entry_valid? @@ -91,12 +116,19 @@ class Chef end def read - File.open(file_path, "rb") { |f| f.read } + if is_ruby_file? + data_handler.from_ruby(file_path).to_json + else + File.open(file_path, "rb") { |f| f.read } + end rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end def write(content) + if is_ruby_file? + raise Chef::ChefFS::FileSystem::RubyFileError.new(:write, self) + end if content && write_pretty_json && is_json_file? content = minimize(content, self) end diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb index 703c0fc635..9d1538e46e 100644 --- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb +++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb @@ -37,6 +37,10 @@ class Chef attr_reader :recursive attr_reader :file_path + alias_method :display_path, :path + alias_method :display_name, :name + alias_method :bare_name, :name + def initialize(name, parent, file_path = nil, ruby_only = false, recursive = false) @parent = parent @name = name diff --git a/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb b/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb index d231e41e78..6aafcfe294 100644 --- a/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/client_keys_sub_dir.rb @@ -26,10 +26,6 @@ class Chef module Repository class ClientKeysSubDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - protected def make_child_entry(child_name) diff --git a/lib/chef/chef_fs/file_system/repository/clients_dir.rb b/lib/chef/chef_fs/file_system/repository/clients_dir.rb index 31aa80a28f..01027f83ac 100644 --- a/lib/chef/chef_fs/file_system/repository/clients_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/clients_dir.rb @@ -26,9 +26,6 @@ class Chef module FileSystem module Repository class ClientsDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end def make_child_entry(child_name) Client.new(child_name, self) diff --git a/lib/chef/chef_fs/file_system/repository/containers_dir.rb b/lib/chef/chef_fs/file_system/repository/containers_dir.rb index 0fe541417a..2af496f418 100644 --- a/lib/chef/chef_fs/file_system/repository/containers_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/containers_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class ContainersDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) Container.new(child_name, self) end diff --git a/lib/chef/chef_fs/file_system/repository/directory.rb b/lib/chef/chef_fs/file_system/repository/directory.rb index 20f6f82108..dae467993a 100644 --- a/lib/chef/chef_fs/file_system/repository/directory.rb +++ b/lib/chef/chef_fs/file_system/repository/directory.rb @@ -28,6 +28,10 @@ class Chef attr_reader :path attr_reader :file_path + alias_method :display_path, :path + alias_method :display_name, :name + alias_method :bare_name, :name + def initialize(name, parent, file_path = nil) @parent = parent @name = name diff --git a/lib/chef/chef_fs/file_system/repository/environments_dir.rb b/lib/chef/chef_fs/file_system/repository/environments_dir.rb index 2c9b31b29b..4d04348d6e 100644 --- a/lib/chef/chef_fs/file_system/repository/environments_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/environments_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class EnvironmentsDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) Environment.new(child_name, self) end diff --git a/lib/chef/chef_fs/file_system/repository/groups_dir.rb b/lib/chef/chef_fs/file_system/repository/groups_dir.rb index e307000ad8..20728d1248 100644 --- a/lib/chef/chef_fs/file_system/repository/groups_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/groups_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class GroupsDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) Group.new(child_name, self) end diff --git a/lib/chef/chef_fs/file_system/repository/nodes_dir.rb b/lib/chef/chef_fs/file_system/repository/nodes_dir.rb index d04bea0de2..33ca7ca709 100644 --- a/lib/chef/chef_fs/file_system/repository/nodes_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/nodes_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class NodesDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) Node.new(child_name, self) end diff --git a/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb b/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb index 020464a634..4db85a93f7 100644 --- a/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/policy_groups_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class PolicyGroupsDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) PolicyGroup.new(child_name, self) end diff --git a/lib/chef/chef_fs/file_system/repository/roles_dir.rb b/lib/chef/chef_fs/file_system/repository/roles_dir.rb index f59af093bb..42f4376e71 100644 --- a/lib/chef/chef_fs/file_system/repository/roles_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/roles_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class RolesDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) Role.new(child_name, self) end diff --git a/lib/chef/chef_fs/file_system/repository/users_dir.rb b/lib/chef/chef_fs/file_system/repository/users_dir.rb index 32cebf842d..9e8621575b 100644 --- a/lib/chef/chef_fs/file_system/repository/users_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/users_dir.rb @@ -27,10 +27,6 @@ class Chef module Repository class UsersDir < Repository::Directory - def can_have_child?(name, is_dir) - !is_dir && File.extname(name) == ".json" - end - def make_child_entry(child_name) User.new(child_name, self) end diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb index 802fdd792b..45f3061d87 100644 --- a/lib/chef/knife/cookbook_site_install.rb +++ b/lib/chef/knife/cookbook_site_install.rb @@ -141,10 +141,15 @@ class Chef def extract_cookbook(upstream_file, version) ui.info("Uncompressing #{@cookbook_name} version #{version}.") - # FIXME: Detect if we have the bad tar from git on Windows: https://github.com/opscode/chef/issues/1753 extract_command = "tar zxvf \"#{convert_path upstream_file}\"" if Chef::Platform.windows? - extract_command << " --force-local" + tar_version = shell_out("tar --version").stdout.tr("\n", " ") + if tar_version =~ /GNU tar/ + Chef::Log.debug("GNU tar detected, adding --force-local") + extract_command << " --force-local" + else + Chef::Log.debug("non-GNU tar detected, not adding --force-local") + end end shell_out!(extract_command, :cwd => @install_path) end diff --git a/lib/chef/knife/list.rb b/lib/chef/knife/list.rb index 3d1583b270..fcfde0eb45 100644 --- a/lib/chef/knife/list.rb +++ b/lib/chef/knife/list.rb @@ -44,7 +44,6 @@ class Chef patterns = name_args.length == 0 ? [""] : name_args # Get the top-level matches - args = pattern_args_from(patterns) all_results = parallelize(pattern_args_from(patterns)) do |pattern| pattern_results = Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).to_a @@ -95,7 +94,7 @@ class Chef printed_something = true end output "#{format_path(result)}:" - print_results(children.map { |result| maybe_add_slash(result.name, result.dir?) }.sort, "") + print_results(children.map { |result| maybe_add_slash(result.display_name, result.dir?) }.sort, "") end exit self.exit_code if self.exit_code diff --git a/lib/chef/monkey_patches/win32/registry.rb b/lib/chef/monkey_patches/win32/registry.rb index 1c1a103432..a08d67becf 100644 --- a/lib/chef/monkey_patches/win32/registry.rb +++ b/lib/chef/monkey_patches/win32/registry.rb @@ -22,6 +22,17 @@ require "win32/registry" module Win32 class Registry + # ::Win32::Registry#export_string is used when enumerating child + # keys and values and re encodes a UTF-16LE to the local codepage. + # This can result in encoding incompatibilities if the native codepage + # does not support the characters in the registry. There is an open bug + # in ruby at https://bugs.ruby-lang.org/issues/11410. Rather than converting + # the UTF-16LE originally returned by the win32 api, we encode to UTF-8 + # which will likely not result in any conversion error. + def export_string(str, enc = Encoding.default_internal || "utf-8") + str.encode(enc) + end + module API extend Chef::ReservedNames::Win32::API::Registry diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb index 6d67cdbbb2..cac0fb6eb0 100644 --- a/lib/chef/provider/package.rb +++ b/lib/chef/provider/package.rb @@ -596,7 +596,7 @@ class Chef # @param args [String] variable number of string arguments # @return [String] nicely concatenated string or empty string def a_to_s(*args) - args.reject { |i| i.nil? || i == "" }.join(" ") + args.flatten.reject { |i| i.nil? || i == "" }.join(" ") end end end diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb index ac730202b8..8af089e14a 100644 --- a/lib/chef/provider/package/apt.rb +++ b/lib/chef/provider/package/apt.rb @@ -17,132 +17,64 @@ # require "chef/provider/package" -require "chef/mixin/command" -require "chef/resource/package" +require "chef/resource/apt_package" class Chef class Provider class Package class Apt < Chef::Provider::Package + use_multipackage_api provides :package, platform_family: "debian" provides :apt_package, os: "linux" - # return [Hash] mapping of package name to Boolean value - attr_accessor :is_virtual_package - def initialize(new_resource, run_context) super - @is_virtual_package = {} end def load_current_resource - @current_resource = Chef::Resource::Package.new(@new_resource.name) - @current_resource.package_name(@new_resource.package_name) - check_all_packages_state(@new_resource.package_name) - @current_resource + @current_resource = Chef::Resource::AptPackage.new(new_resource.name) + current_resource.package_name(new_resource.package_name) + current_resource.version(get_current_versions) + current_resource end def define_resource_requirements super requirements.assert(:all_actions) do |a| - a.assertion { !@new_resource.source } + a.assertion { !new_resource.source } a.failure_message(Chef::Exceptions::Package, "apt package provider cannot handle source attribute. Use dpkg provider instead") end end - def default_release_options - # Use apt::Default-Release option only if provider supports it - "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.respond_to?(:default_release) && @new_resource.default_release + def package_data + @package_data ||= Hash.new do |hash, key| + hash[key] = package_data_for(key) + end end - def check_package_state(pkg) - is_virtual_package = false - installed = false - installed_version = nil - candidate_version = nil - - shell_out_with_timeout!("apt-cache#{expand_options(default_release_options)} policy #{pkg}").stdout.each_line do |line| - case line - when /^\s{2}Installed: (.+)$/ - installed_version = $1 - if installed_version == "(none)" - Chef::Log.debug("#{@new_resource} current version is nil") - installed_version = nil - else - Chef::Log.debug("#{@new_resource} current version is #{installed_version}") - installed = true - end - when /^\s{2}Candidate: (.+)$/ - candidate_version = $1 - if candidate_version == "(none)" - # This may not be an appropriate assumption, but it shouldn't break anything that already worked -- btm - is_virtual_package = true - showpkg = shell_out_with_timeout!("apt-cache showpkg #{pkg}").stdout - providers = Hash.new - showpkg.rpartition(/Reverse Provides: ?#{$/}/)[2].each_line do |line| - provider, version = line.split - providers[provider] = version - end - # Check if the package providing this virtual package is installed - num_providers = providers.length - raise Chef::Exceptions::Package, "#{@new_resource.package_name} has no candidate in the apt-cache" if num_providers == 0 - # apt will only install a virtual package if there is a single providing package - raise Chef::Exceptions::Package, "#{@new_resource.package_name} is a virtual package provided by #{num_providers} packages, you must explicitly select one to install" if num_providers > 1 - # Check if the package providing this virtual package is installed - Chef::Log.info("#{@new_resource} is a virtual package, actually acting on package[#{providers.keys.first}]") - ret = check_package_state(providers.keys.first) - installed = ret[:installed] - installed_version = ret[:installed_version] - else - Chef::Log.debug("#{@new_resource} candidate version is #{$1}") - end - end + def get_current_versions + package_name_array.map do |package_name| + package_data[package_name][:current_version] end - - return { - installed_version: installed_version, - installed: installed, - candidate_version: candidate_version, - is_virtual_package: is_virtual_package, - } end - def check_all_packages_state(package) - installed_version = {} - candidate_version = {} - installed = {} - - [package].flatten.each do |pkg| - ret = check_package_state(pkg) - is_virtual_package[pkg] = ret[:is_virtual_package] - installed[pkg] = ret[:installed] - installed_version[pkg] = ret[:installed_version] - candidate_version[pkg] = ret[:candidate_version] + def get_candidate_versions + package_name_array.map do |package_name| + package_data[package_name][:candidate_version] end + end - if package.is_a?(Array) - @candidate_version = [] - final_installed_version = [] - [package].flatten.each do |pkg| - @candidate_version << candidate_version[pkg] - final_installed_version << installed_version[pkg] - end - @current_resource.version(final_installed_version) - else - @candidate_version = candidate_version[package] - @current_resource.version(installed_version[package]) - end + def candidate_version + @candidate_version ||= get_candidate_versions end def install_package(name, version) - name_array = [ name ].flatten - version_array = [ version ].flatten - package_name = name_array.zip(version_array).map do |n, v| - is_virtual_package[n] ? n : "#{n}=#{v}" - end.join(" ") - run_noninteractive("apt-get -q -y#{expand_options(default_release_options)}#{expand_options(@new_resource.options)} install #{package_name}") + package_name = name.zip(version).map do |n, v| + package_data[n][:virtual] ? n : "#{n}=#{v}" + end + run_noninteractive("apt-get -q -y", default_release_options, new_resource.options, "install", package_name) end def upgrade_package(name, version) @@ -150,24 +82,27 @@ class Chef end def remove_package(name, version) - package_name = [ name ].flatten.join(" ") - run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}") + package_name = name.map do |n| + package_data[n][:virtual] ? resolve_virtual_package_name(n) : n + end + run_noninteractive("apt-get -q -y", new_resource.options, "remove", package_name) end def purge_package(name, version) - package_name = [ name ].flatten.join(" ") - run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} purge #{package_name}") + package_name = name.map do |n| + package_data[n][:virtual] ? resolve_virtual_package_name(n) : n + end + run_noninteractive("apt-get -q -y", new_resource.options, "purge", package_name) end def preseed_package(preseed_file) - Chef::Log.info("#{@new_resource} pre-seeding package installation instructions") - run_noninteractive("debconf-set-selections #{preseed_file}") + Chef::Log.info("#{new_resource} pre-seeding package installation instructions") + run_noninteractive("debconf-set-selections", preseed_file) end def reconfig_package(name, version) - package_name = [ name ].flatten.join(" ") - Chef::Log.info("#{@new_resource} reconfiguring") - run_noninteractive("dpkg-reconfigure #{package_name}") + Chef::Log.info("#{new_resource} reconfiguring") + run_noninteractive("dpkg-reconfigure", name) end private @@ -175,8 +110,67 @@ class Chef # Runs command via shell_out with magic environment to disable # interactive prompts. Command is run with default localization rather # than forcing locale to "C", so command output may not be stable. - def run_noninteractive(command) - shell_out_with_timeout!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }) + def run_noninteractive(*args) + shell_out_with_timeout!(a_to_s(*args), :env => { "DEBIAN_FRONTEND" => "noninteractive" }) + end + + def default_release_options + # Use apt::Default-Release option only if provider supports it + "-o APT::Default-Release=#{new_resource.default_release}" if new_resource.respond_to?(:default_release) && new_resource.default_release + end + + def resolve_package_versions(pkg) + current_version = nil + candidate_version = nil + run_noninteractive("apt-cache", default_release_options, "policy", pkg).stdout.each_line do |line| + case line + when /^\s{2}Installed: (.+)$/ + current_version = ( $1 != "(none)" ) ? $1 : nil + Chef::Log.debug("#{new_resource} installed version for #{pkg} is #{$1}") + when /^\s{2}Candidate: (.+)$/ + candidate_version = ( $1 != "(none)" ) ? $1 : nil + Chef::Log.debug("#{new_resource} candidate version for #{pkg} is #{$1}") + end + end + [ current_version, candidate_version ] + end + + def resolve_virtual_package_name(pkg) + showpkg = run_noninteractive("apt-cache showpkg", pkg).stdout + partitions = showpkg.rpartition(/Reverse Provides: ?#{$/}/) + return nil if partitions[0] == "" && partitions[1] == "" # not found in output + set = partitions[2].lines.each_with_object(Set.new) do |line, acc| + # there may be multiple reverse provides for a single package + acc.add(line.split[0]) + end + if set.size > 1 + raise Chef::Exceptions::Package, "#{new_resource.package_name} is a virtual package provided by multiple packages, you must explicitly select one" + end + return set.to_a.first + end + + def package_data_for(pkg) + virtual = false + current_version = nil + candidate_version = nil + + current_version, candidate_version = resolve_package_versions(pkg) + + if candidate_version.nil? + newpkg = resolve_virtual_package_name(pkg) + + if newpkg + virtual = true + Chef::Log.info("#{new_resource} is a virtual package, actually acting on package[#{newpkg}]") + current_version, candidate_version = resolve_package_versions(newpkg) + end + end + + return { + current_version: current_version, + candidate_version: candidate_version, + virtual: virtual, + } end end diff --git a/lib/chef/provider/package/chocolatey.rb b/lib/chef/provider/package/chocolatey.rb index 44fb1de235..ebd3f987cd 100644 --- a/lib/chef/provider/package/chocolatey.rb +++ b/lib/chef/provider/package/chocolatey.rb @@ -256,6 +256,7 @@ EOS def parse_list_output(*args) list = [] choco_command(*args).stdout.each_line do |line| + next if line.start_with?("Chocolatey v") name, version = line.split("|") list << [ name.downcase, version.chomp ] end diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index dfd32fde55..64f1b2043c 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -46,7 +46,7 @@ class Chef lead = 0 tail = evr.size - if evr =~ %r{^([\d]+):} + if %r{^([\d]+):}.match(evr) # rubocop:disable Performance/RedundantMatch epoch = $1.to_i lead = $1.length + 1 elsif evr[0].ord == ":".ord @@ -54,7 +54,7 @@ class Chef lead = 1 end - if evr =~ %r{:?.*-(.*)$} + if %r{:?.*-(.*)$}.match(evr) # rubocop:disable Performance/RedundantMatch release = $1 tail = evr.length - release.length - lead - 1 @@ -443,7 +443,7 @@ class Chef # "mtr >= 2:0.71-3.0" # "mta" def self.parse(string) - if string =~ %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$} + if %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}.match(string) # rubocop:disable Performance/RedundantMatch name = $1 if $2 == "=" flag = :== diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 1afc811da1..19f3c78f13 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -21,7 +21,7 @@ class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.10.33" + VERSION = "12.10.43" end # diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index 5214847aa1..879e8ecf4f 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,15 +1,15 @@ GIT remote: https://github.com/chef/omnibus-software.git - revision: 6127be3af79941c32419228cbd9e63c4f061d76b + revision: 7e566482a86aca8b94e77285242b6a526fe77ffe specs: omnibus-software (4.0.0) omnibus (>= 5.2.0) GIT remote: https://github.com/chef/omnibus.git - revision: e73722f46fbd5f302d9e1541aaae0b2f8be2110e + revision: b62510242eceae2f52f2701f6ee0c4ef48ae8b4a specs: - omnibus (5.3.0) + omnibus (5.4.0) aws-sdk (~> 2) chef-sugar (~> 3.3) cleanroom (~> 1.0) @@ -38,12 +38,12 @@ GEM addressable (2.3.8) artifactory (2.3.2) awesome_print (1.6.1) - aws-sdk (2.2.34) - aws-sdk-resources (= 2.2.34) - aws-sdk-core (2.2.34) + aws-sdk (2.3.2) + aws-sdk-resources (= 2.3.2) + aws-sdk-core (2.3.2) jmespath (~> 1.0) - aws-sdk-resources (2.2.34) - aws-sdk-core (= 2.2.34) + aws-sdk-resources (2.3.2) + aws-sdk-core (= 2.3.2) berkshelf (3.3.0) addressable (~> 2.3.4) berkshelf-api-client (~> 1.2) @@ -75,13 +75,13 @@ GEM buff-shell_out (0.2.0) buff-ruby_engine (~> 0.1.0) builder (3.2.2) - byebug (8.2.4) + byebug (8.2.5) celluloid (0.16.0) timers (~> 4.0.0) celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.9.38) + chef-config (12.9.41) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) @@ -106,9 +106,9 @@ GEM ffi (>= 1.0.1) gyoku (1.3.1) builder (>= 2.1.2) - hashie (3.4.3) - hitimes (1.2.3) - hitimes (1.2.3-x86-mingw32) + hashie (3.4.4) + hitimes (1.2.4) + hitimes (1.2.4-x86-mingw32) httpclient (2.6.0.1) iostruct (0.0.4) ipaddress (0.8.3) @@ -130,9 +130,9 @@ GEM rspec-core (~> 3.2) rspec-expectations (~> 3.2) rspec-mocks (~> 3.2) - mixlib-cli (1.5.0) + mixlib-cli (1.6.0) mixlib-config (2.2.1) - mixlib-install (1.0.7) + mixlib-install (1.0.11) artifactory mixlib-shellout mixlib-versioning @@ -142,7 +142,7 @@ GEM win32-process (~> 0.8.2) wmi-lite (~> 1.0) mixlib-versioning (1.1.0) - multi_json (1.11.2) + multi_json (1.12.0) multipart-post (1.2.0) net-scp (1.2.1) net-ssh (>= 2.6.5) @@ -151,7 +151,7 @@ GEM nori (2.6.0) octokit (3.8.0) sawyer (~> 0.6.0, >= 0.5.3) - ohai (8.14.0) + ohai (8.15.1) chef-config (>= 12.5.0.alpha.1, < 13) ffi (~> 1.9) ffi-yajl (~> 2.2) @@ -203,7 +203,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-support (3.4.1) - ruby-progressbar (1.7.5) + ruby-progressbar (1.8.0) rubyntlm (0.6.0) rubyzip (1.2.0) safe_yaml (1.0.4) @@ -231,7 +231,7 @@ GEM hashie (>= 2.0.2, < 4.0.0) win32-process (0.8.3) ffi (>= 1.0.0) - winrm (1.7.3) + winrm (1.8.1) builder (>= 2.1.2) gssapi (~> 1.2) gyoku (~> 1.0) diff --git a/omnibus/config/software/chef-complete.rb b/omnibus/config/software/chef-complete.rb index 46fc8046e4..2c2b1ca205 100644 --- a/omnibus/config/software/chef-complete.rb +++ b/omnibus/config/software/chef-complete.rb @@ -6,6 +6,7 @@ dependency "chef" dependency "chef-appbundle" dependency "chef-remove-docs" +dependency "gem-permissions" dependency "shebang-cleanup" dependency "version-manifest" dependency "openssl-customization" diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb index 17d0dc4b89..3414f217fa 100644 --- a/omnibus_overrides.rb +++ b/omnibus_overrides.rb @@ -1,6 +1,6 @@ # DO NOT EDIT. Generated by "rake dependencies". Edit version_policy.rb instead. -override :rubygems, version: "2.6.3" -override :bundler, version: "1.11.2" +override :rubygems, version: "2.6.4" +override :bundler, version: "1.12.3" override "libffi", version: "3.2.1" override "libiconv", version: "1.14" override "liblzma", version: "5.2.2" diff --git a/spec/functional/win32/registry_spec.rb b/spec/functional/win32/registry_spec.rb index 4a6157a6d5..bcfa0ffd48 100644 --- a/spec/functional/win32/registry_spec.rb +++ b/spec/functional/win32/registry_spec.rb @@ -26,6 +26,7 @@ describe "Chef::Win32::Registry", :windows_only do #Create a registry item ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root" ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch" + ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\BĀ®anch" ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Flower" ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg["RootType1", Win32::Registry::REG_SZ] = "fibrous" diff --git a/spec/integration/knife/deps_spec.rb b/spec/integration/knife/deps_spec.rb index de0872d39c..292bce6002 100644 --- a/spec/integration/knife/deps_spec.rb +++ b/spec/integration/knife/deps_spec.rb @@ -289,11 +289,11 @@ EOM :stderr => "ERROR: /cookbooks/x: No such file or directory\n" ) end - it "knife deps /data_bags/bag/item reports an error" do - knife("deps /data_bags/bag/item").should_fail( + it "knife deps /data_bags/bag/item.json reports an error" do + knife("deps /data_bags/bag/item.json").should_fail( :exit_code => 2, - :stdout => "/data_bags/bag/item\n", - :stderr => "ERROR: /data_bags/bag/item: No such file or directory\n" + :stdout => "/data_bags/bag/item.json\n", + :stderr => "ERROR: /data_bags/bag/item.json: No such file or directory\n" ) end end @@ -628,10 +628,10 @@ EOM ) end it "knife deps /data_bags/bag/item reports an error" do - knife("deps --remote /data_bags/bag/item").should_fail( + knife("deps --remote /data_bags/bag/item.json").should_fail( :exit_code => 2, - :stdout => "/data_bags/bag/item\n", - :stderr => "ERROR: /data_bags/bag/item: No such file or directory\n" + :stdout => "/data_bags/bag/item.json\n", + :stderr => "ERROR: /data_bags/bag/item.json: No such file or directory\n" ) end end diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb index 39bf8d1c42..a924226ea2 100644 --- a/spec/integration/knife/download_spec.rb +++ b/spec/integration/knife/download_spec.rb @@ -532,6 +532,25 @@ EOM end end + when_the_chef_server "has a role" do + before do + role "x", {} + end + when_the_repository "has the role in ruby" do + before do + file "roles/x.rb", <<EOM +name "x" +description "x" +EOM + end + + it "knife download refuses to change the role" do + knife("download /roles/x.json").should_succeed "", :stderr => "WARNING: /roles/x.rb cannot be updated (can't safely update ruby files).\n" + knife("diff --name-status /roles/x.json").should_succeed "M\t/roles/x.rb\n" + end + end + end + when_the_chef_server "has an environment" do before do environment "x", {} diff --git a/spec/integration/knife/list_spec.rb b/spec/integration/knife/list_spec.rb index a5446dc08d..4aa74f3f0e 100644 --- a/spec/integration/knife/list_spec.rb +++ b/spec/integration/knife/list_spec.rb @@ -309,14 +309,6 @@ EOM it "knife list /blarghle reports missing directory" do knife("list /blarghle").should_fail "ERROR: /blarghle: No such file or directory\n" end - - it "knife list /roles/blarghle reports missing directory" do - knife("list /roles/blarghle").should_fail "ERROR: /roles/blarghle: No such file or directory\n" - end - - it "knife list /roles/blarghle/blorghle reports missing directory" do - knife("list /roles/blarghle/blorghle").should_fail "ERROR: /roles/blarghle/blorghle: No such file or directory\n" - end end context "symlink tests" do diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb index 038bbad216..55c95b59b7 100644 --- a/spec/integration/knife/upload_spec.rb +++ b/spec/integration/knife/upload_spec.rb @@ -154,6 +154,25 @@ EOM end end + context "the role is in ruby" do + before do + file "roles/x.rb", <<EOM +name "x" +description "blargle" +EOM + end + + it "knife upload changes the role" do + knife("upload /").should_succeed "Updated /roles/x.json\n" + knife("diff --name-status /").should_succeed "" + end + + it "knife upload --no-diff does not change the role" do + knife("upload --no-diff /").should_succeed "" + knife("diff --name-status /").should_succeed "M\t/roles/x.rb\n" + end + end + context "when cookbook metadata has a self-dependency" do before do file "cookbooks/x/metadata.rb", "name 'x'; version '1.0.0'; depends 'x'" @@ -632,14 +651,14 @@ WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error ERROR: /environments/x.json failed to write: Parse error reading JSON: parse error: premature EOF { (right here) ------^ -EOH + EOH warn = <<-EOH WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF { (right here) ------^ -EOH + EOH knife("upload /environments/x.json").should_fail(error1) knife("diff --name-status /environments/x.json").should_succeed("M\t/environments/x.json\n", :stderr => warn) end diff --git a/spec/unit/chef_fs/file_system/repository/base_file_spec.rb b/spec/unit/chef_fs/file_system/repository/base_file_spec.rb index bb0c267368..625ef32dca 100644 --- a/spec/unit/chef_fs/file_system/repository/base_file_spec.rb +++ b/spec/unit/chef_fs/file_system/repository/base_file_spec.rb @@ -47,7 +47,7 @@ describe Chef::ChefFS::FileSystem::Repository::BaseFile do end context "#is_json_file?" do - it "returns false when the file is not json" do + it "returns false when the file is not json", pending: "We assume that everything is ruby or JSON" do file = described_class.new("test_file.dpkg", parent) expect(file.is_json_file?).to be_falsey end @@ -63,11 +63,16 @@ describe Chef::ChefFS::FileSystem::Repository::BaseFile do expect(file.name_valid?).to be_falsey end - it "rejects non json files" do + it "rejects non json files", pending: "We assume that everything is ruby or JSON" do file = described_class.new("test_file.dpkg", parent) expect(file.name_valid?).to be_falsey end + it "allows ruby files" do + file = described_class.new("test_file.rb", parent) + expect(file.name_valid?).to be_truthy + end + it "allows correctly named files" do expect(file.name_valid?).to be_truthy end @@ -105,14 +110,7 @@ describe Chef::ChefFS::FileSystem::Repository::BaseFile do context "#write" do context "minimises a json object" do - it "not if pretty json is off" do - expect(file).to_not receive(:minimize) - file.write(content) - end - - it "not if it's not a json file" do - file = described_class.new("test_file.dpkg", parent) - file.write_pretty_json = true + it "unless pretty json is off" do expect(file).to_not receive(:minimize) file.write(content) end diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb index b5f0646b79..a549ecc72e 100644 --- a/spec/unit/provider/package/apt_spec.rb +++ b/spec/unit/provider/package/apt_spec.rb @@ -52,6 +52,7 @@ irssi: it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:shell_out!).with( "apt-cache policy #{@new_resource.package_name}", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(@shell_out) @provider.load_current_resource @@ -60,7 +61,7 @@ irssi: expect(current_resource).to be_a(Chef::Resource::Package) expect(current_resource.name).to eq("irssi") expect(current_resource.package_name).to eq("irssi") - expect(current_resource.version).to be_nil + expect(current_resource.version).to eql([nil]) end it "should set the installed version if package has one" do @@ -78,8 +79,32 @@ sudo: INSTALLED expect(@provider).to receive(:shell_out!).and_return(@shell_out) @provider.load_current_resource - expect(@provider.current_resource.version).to eq("1.7.2p1-1ubuntu5.3") - expect(@provider.candidate_version).to eql("1.7.2p1-1ubuntu5.3") + expect(@provider.current_resource.version).to eq(["1.7.2p1-1ubuntu5.3"]) + expect(@provider.candidate_version).to eql(["1.7.2p1-1ubuntu5.3"]) + end + + # it is the superclasses responsibility to throw most exceptions + it "if the package does not exist in the cache sets installed + candidate version to nil" do + @new_resource.package_name("conic-smarms") + policy_out = <<-POLICY_STDOUT +N: Unable to locate package conic-smarms + POLICY_STDOUT + policy = double(:stdout => policy_out, :exitstatus => 0) + expect(@provider).to receive(:shell_out!).with( + "apt-cache policy conic-smarms", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, + :timeout => @timeout + ).and_return(policy) + showpkg_out = <<-SHOWPKG_STDOUT +N: Unable to locate package conic-smarms + SHOWPKG_STDOUT + showpkg = double(:stdout => showpkg_out, :exitstatus => 0) + expect(@provider).to receive(:shell_out!).with( + "apt-cache showpkg conic-smarms", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, + :timeout => @timeout + ).and_return(showpkg) + @provider.load_current_resource end # libmysqlclient-dev is a real package in newer versions of debian + ubuntu @@ -95,6 +120,7 @@ libmysqlclient15-dev: virtual_package = double(:stdout => virtual_package_out, :exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache policy libmysqlclient15-dev", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(virtual_package) showpkg_out = <<-SHOWPKG_STDOUT @@ -118,6 +144,7 @@ libmysqlclient-dev 5.1.41-3ubuntu12 showpkg = double(:stdout => showpkg_out, :exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache showpkg libmysqlclient15-dev", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(showpkg) real_package_out = <<-RPKG_STDOUT @@ -136,6 +163,7 @@ libmysqlclient-dev: real_package = double(:stdout => real_package_out, :exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache policy libmysqlclient-dev", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(real_package) @provider.load_current_resource @@ -152,6 +180,7 @@ mp3-decoder: virtual_package = double(:stdout => virtual_package_out, :exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache policy mp3-decoder", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(virtual_package) showpkg_out = <<-SHOWPKG_STDOUT @@ -178,6 +207,7 @@ mpg123 1.12.1-0ubuntu1 showpkg = double(:stdout => showpkg_out, :exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache showpkg mp3-decoder", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(showpkg) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) @@ -191,6 +221,7 @@ mpg123 1.12.1-0ubuntu1 allow(@new_resource).to receive(:provider).and_return(nil) expect(@provider).to receive(:shell_out!).with( "apt-cache -o APT::Default-Release=lenny-backports policy irssi", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ).and_return(@shell_out) @provider.load_current_resource @@ -200,11 +231,9 @@ mpg123 1.12.1-0ubuntu1 @new_resource.source "pluto" expect(@provider).to receive(:shell_out!).with( "apt-cache policy #{@new_resource.package_name}", + :env => { "DEBIAN_FRONTEND" => "noninteractive" } , :timeout => @timeout ).and_return(@shell_out) - @provider.load_current_resource - @provider.define_resource_requirements - expect(@provider).to receive(:shell_out!).with("apt-cache policy irssi", { :timeout => 900 }).and_return(@shell_out) expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end end @@ -213,26 +242,38 @@ mpg123 1.12.1-0ubuntu1 before do @current_resource = resource_klass.new("irssi", @run_context) @provider.current_resource = @current_resource + allow(@provider).to receive(:package_data).and_return({ + "irssi" => { + virtual: false, + candidate_version: "0.8.12-7", + installed_version: nil, + }, + "libmysqlclient15-dev" => { + virtual: true, + candidate_version: nil, + installed_version: nil, + }, + }) end describe "install_package" do it "should run apt-get install with the package name and version" do expect(@provider).to receive(:shell_out!). with( "apt-get -q -y install irssi=0.8.12-7", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) - @provider.install_package("irssi", "0.8.12-7") + @provider.install_package(["irssi"], ["0.8.12-7"]) end it "should run apt-get install with the package name and version and options if specified" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y --force-yes install irssi=0.8.12-7", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) @new_resource.options("--force-yes") - @provider.install_package("irssi", "0.8.12-7") + @provider.install_package(["irssi"], ["0.8.12-7"]) end it "should run apt-get install with the package name and version and default_release if there is one and provider is explicitly defined" do @@ -244,19 +285,19 @@ mpg123 1.12.1-0ubuntu1 expect(@provider).to receive(:shell_out!).with( "apt-get -q -y -o APT::Default-Release=lenny-backports install irssi=0.8.12-7", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) - @provider.install_package("irssi", "0.8.12-7") + @provider.install_package(["irssi"], ["0.8.12-7"]) end end describe resource_klass, "upgrade_package" do it "should run install_package with the name and version" do - expect(@provider).to receive(:install_package).with("irssi", "0.8.12-7") - @provider.upgrade_package("irssi", "0.8.12-7") + expect(@provider).to receive(:install_package).with(["irssi"], ["0.8.12-7"]) + @provider.upgrade_package(["irssi"], ["0.8.12-7"]) end end @@ -265,21 +306,21 @@ mpg123 1.12.1-0ubuntu1 it "should run apt-get remove with the package name" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y remove irssi", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) - @provider.remove_package("irssi", "0.8.12-7") + @provider.remove_package(["irssi"], ["0.8.12-7"]) end it "should run apt-get remove with the package name and options if specified" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y --force-yes remove irssi", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) @new_resource.options("--force-yes") - @provider.remove_package("irssi", "0.8.12-7") + @provider.remove_package(["irssi"], ["0.8.12-7"]) end end @@ -288,21 +329,21 @@ mpg123 1.12.1-0ubuntu1 it "should run apt-get purge with the package name" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y purge irssi", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) - @provider.purge_package("irssi", "0.8.12-7") + @provider.purge_package(["irssi"], ["0.8.12-7"]) end it "should run apt-get purge with the package name and options if specified" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y --force-yes purge irssi", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) @new_resource.options("--force-yes") - @provider.purge_package("irssi", "0.8.12-7") + @provider.purge_package(["irssi"], ["0.8.12-7"]) end end @@ -316,7 +357,7 @@ mpg123 1.12.1-0ubuntu1 expect(@provider).to receive(:shell_out!).with( "debconf-set-selections /tmp/irssi-0.8.12-7.seed", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) @@ -326,7 +367,7 @@ mpg123 1.12.1-0ubuntu1 it "should run debconf-set-selections on the preseed file if it has changed" do expect(@provider).to receive(:shell_out!).with( "debconf-set-selections /tmp/irssi-0.8.12-7.seed", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) file = @provider.get_preseed_file("irssi", "0.8.12-7") @@ -347,7 +388,7 @@ mpg123 1.12.1-0ubuntu1 it "should run dpkg-reconfigure package" do expect(@provider).to receive(:shell_out!).with( "dpkg-reconfigure irssi", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) @provider.reconfig_package("irssi", "0.8.12-7") @@ -356,27 +397,49 @@ mpg123 1.12.1-0ubuntu1 describe "when installing a virtual package" do it "should install the package without specifying a version" do - @provider.is_virtual_package["libmysqlclient-dev"] = true + @provider.package_data["libmysqlclient15-dev"][:virtual] = true + expect(@provider).to receive(:shell_out!).with( + "apt-get -q -y install libmysqlclient15-dev", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, + :timeout => @timeout + ) + @provider.install_package(["libmysqlclient15-dev"], ["not_a_real_version"]) + end + end + + describe "when removing a virtual package" do + it "should remove the resolved name instead of the virtual package name" do + expect(@provider).to receive(:resolve_virtual_package_name).with("libmysqlclient15-dev").and_return("libmysqlclient-dev") + expect(@provider).to receive(:shell_out!).with( + "apt-get -q -y remove libmysqlclient-dev", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, + :timeout => @timeout + ) + @provider.remove_package(["libmysqlclient15-dev"], ["not_a_real_version"]) + end + end + + describe "when purging a virtual package" do + it "should purge the resolved name instead of the virtual package name" do + expect(@provider).to receive(:resolve_virtual_package_name).with("libmysqlclient15-dev").and_return("libmysqlclient-dev") expect(@provider).to receive(:shell_out!).with( - "apt-get -q -y install libmysqlclient-dev", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + "apt-get -q -y purge libmysqlclient-dev", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) - @provider.install_package("libmysqlclient-dev", "not_a_real_version") + @provider.purge_package(["libmysqlclient15-dev"], ["not_a_real_version"]) end end describe "when installing multiple packages" do it "can install a virtual package followed by a non-virtual package" do # https://github.com/chef/chef/issues/2914 - @provider.is_virtual_package["libmysqlclient-dev"] = true - @provider.is_virtual_package["irssi"] = false expect(@provider).to receive(:shell_out!).with( - "apt-get -q -y install libmysqlclient-dev irssi=0.8.12-7", - :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, + "apt-get -q -y install libmysqlclient15-dev irssi=0.8.12-7", + :env => { "DEBIAN_FRONTEND" => "noninteractive" }, :timeout => @timeout ) - @provider.install_package(["libmysqlclient-dev", "irssi"], ["not_a_real_version", "0.8.12-7"]) + @provider.install_package(["libmysqlclient15-dev", "irssi"], ["not_a_real_version", "0.8.12-7"]) end end diff --git a/spec/unit/provider/package/chocolatey_spec.rb b/spec/unit/provider/package/chocolatey_spec.rb index 8a69cf3da4..8eaa69b598 100644 --- a/spec/unit/provider/package/chocolatey_spec.rb +++ b/spec/unit/provider/package/chocolatey_spec.rb @@ -36,6 +36,7 @@ describe Chef::Provider::Package::Chocolatey do # installed packages (ConEmu is upgradable) let(:local_list_stdout) do <<-EOF +Chocolatey v0.9.9.11 chocolatey|0.9.9.11 ConEmu|15.10.25.0 EOF @@ -50,6 +51,7 @@ ConEmu|15.10.25.0 def allow_remote_list(package_names, args = nil) remote_list_stdout = <<-EOF +Chocolatey v0.9.9.11 chocolatey|0.9.9.11 ConEmu|15.10.25.1 Git|2.6.1 diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb index e4354d0db4..2ef58db9f3 100644 --- a/spec/unit/provider/package_spec.rb +++ b/spec/unit/provider/package_spec.rb @@ -458,8 +458,18 @@ describe "Subclass with use_multipackage_api" do expect(provider.use_multipackage_api?).to be true end - it "offers a_to_s to subclasses to convert an array of strings to a single string" do - expect(provider.send(:a_to_s, "a", nil, "b", "", "c", " ", "d e", "f-g")).to eq("a b c d e f-g") + context "#a_to_s utility for subclasses" do + it "converts varargs of strings to a single string" do + expect(provider.send(:a_to_s, "a", nil, "b", "", "c", " ", "d e", "f-g")).to eq("a b c d e f-g") + end + + it "converts an array of strings to a single string" do + expect(provider.send(:a_to_s, ["a", nil, "b", "", "c", " ", "d e", "f-g"])).to eq("a b c d e f-g") + end + + it "converts a mishmash of array args to a single string" do + expect(provider.send(:a_to_s, "a", [ nil, "b", "", [ "c" ] ], " ", [ "d e", "f-g" ])).to eq("a b c d e f-g") + end end it "when user passes string to package_name, passes arrays to install_package" do diff --git a/tasks/dependencies.rb b/tasks/dependencies.rb index 2faf3b883a..eb4bb1f44f 100644 --- a/tasks/dependencies.rb +++ b/tasks/dependencies.rb @@ -22,8 +22,8 @@ require_relative "../version_policy" desc "Tasks to update and check dependencies" namespace :dependencies do # Update all dependencies to the latest constraint-matching version - desc "Update all dependencies. dependencies:update[conservative] to update as little as possible." - task :update, [:conservative] => %w{ + desc "Update all dependencies. dependencies:update to update as little as possible." + task :update => %w{ dependencies:update_gemfile_lock dependencies:update_omnibus_overrides dependencies:update_omnibus_gemfile_lock @@ -32,28 +32,22 @@ namespace :dependencies do dependencies:update_kitchen_tests_berksfile_lock } - desc "Update Gemfile.lock and all Gemfile.<platform>.locks. update_gemfile_lock[conservative] to update as little as possible." - task :update_gemfile_lock, [:conservative] do |t, rake_args| - conservative = rake_args[:conservative] - if conservative - Rake::Task["bundle:install"].invoke - else - Rake::Task["bundle:update"].invoke - end + desc "Update Gemfile.lock and all Gemfile.<platform>.locks." + task :update_gemfile_lock do |t, rake_args| + Rake::Task["bundle:update"].invoke end def gemfile_lock_task(task_name, dirs: [], other_platforms: true, leave_frozen: true) dirs.each do |dir| - desc "Update #{dir}/Gemfile.lock. #{task_name}[conservative] to update as little as possible." - task task_name, [:conservative] do |t, rake_args| + desc "Update #{dir}/Gemfile.lock." + task task_name do |t, rake_args| extend BundleUtil - conservative = rake_args[:conservative] puts "" puts "-------------------------------------------------------------------" - puts "Updating #{dir}/Gemfile.lock#{conservative ? " (conservatively)" : ""} ..." + puts "Updating #{dir}/Gemfile.lock ..." puts "-------------------------------------------------------------------" with_bundle_unfrozen(cwd: dir, leave_frozen: leave_frozen) do - bundle "install", cwd: dir, delete_gemfile_lock: !conservative + bundle "install", cwd: dir, delete_gemfile_lock: true if other_platforms # Include all other supported platforms into the lockfile as well platforms.each do |platform| @@ -67,15 +61,14 @@ namespace :dependencies do def berksfile_lock_task(task_name, dirs: []) dirs.each do |dir| - desc "Update #{dir}/Berksfile.lock. #{task_name}[conservative] to update as little as possible." - task task_name, [:conservative] do |t, rake_args| + desc "Update #{dir}/Berksfile.lock." + task task_name do |t, rake_args| extend BundleUtil - conservative = rake_args[:conservative] puts "" puts "-------------------------------------------------------------------" - puts "Updating #{dir}/Berksfile.lock#{conservative ? " (conservatively)" : ""} ..." + puts "Updating #{dir}/Berksfile.lock ..." puts "-------------------------------------------------------------------" - if !conservative && File.exist?("#{project_root}/#{dir}/Berksfile.lock") + if File.exist?("#{project_root}/#{dir}/Berksfile.lock") File.delete("#{project_root}/#{dir}/Berksfile.lock") end Dir.chdir("#{project_root}/#{dir}") do @@ -100,48 +93,45 @@ namespace :dependencies do } # kitchen-tests/cookbooks/webapp isn't solving right now .... - desc "Update omnibus overrides, including versions in version_policy.rb and latest version of gems: #{OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.keys}. update_omnibus_overrides[conservative] does nothing." - task :update_omnibus_overrides, [:conservative] do |t, rake_args| - conservative = rake_args[:conservative] - unless conservative - puts "" - puts "-------------------------------------------------------------------" - puts "Updating omnibus_overrides.rb ..." - puts "-------------------------------------------------------------------" - - # Generate the new overrides file - overrides = "# DO NOT EDIT. Generated by \"rake dependencies\". Edit version_policy.rb instead.\n" + desc "Update omnibus overrides, including versions in version_policy.rb and latest version of gems: #{OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.keys}." + task :update_omnibus_overrides do |t, rake_args| + puts "" + puts "-------------------------------------------------------------------" + puts "Updating omnibus_overrides.rb ..." + puts "-------------------------------------------------------------------" - # Replace the bundler and rubygems versions - OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.each do |override_name, gem_name| - # Get the latest bundler version - puts "Running gem list -r #{gem_name} ..." - gem_list = `gem list -r #{gem_name}` - unless gem_list =~ /^#{gem_name}\s*\(([^)]*)\)$/ - raise "gem list -r #{gem_name} failed with output:\n#{gem_list}" - end + # Generate the new overrides file + overrides = "# DO NOT EDIT. Generated by \"rake dependencies\". Edit version_policy.rb instead.\n" - # Emit it - puts "Latest version of #{gem_name} is #{$1}" - overrides << "override #{override_name.inspect}, version: #{$1.inspect}\n" + # Replace the bundler and rubygems versions + OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.each do |override_name, gem_name| + # Get the latest bundler version + puts "Running gem list -r #{gem_name} ..." + gem_list = `gem list -r #{gem_name}` + unless gem_list =~ /^#{gem_name}\s*\(([^)]*)\)$/ + raise "gem list -r #{gem_name} failed with output:\n#{gem_list}" end - # Add explicit overrides - OMNIBUS_OVERRIDES.each do |override_name, version| - overrides << "override #{override_name.inspect}, version: #{version.inspect}\n" - end + # Emit it + puts "Latest version of #{gem_name} is #{$1}" + overrides << "override #{override_name.inspect}, version: #{$1.inspect}\n" + end - # Write the file out (if changed) - overrides_path = File.expand_path("../../omnibus_overrides.rb", __FILE__) - if overrides != IO.read(overrides_path) - puts "Overrides changed!" - puts `git diff #{overrides_path}` - puts "Writing modified #{overrides_path} ..." - IO.write(overrides_path, overrides) - end + # Add explicit overrides + OMNIBUS_OVERRIDES.each do |override_name, version| + overrides << "override #{override_name.inspect}, version: #{version.inspect}\n" + end + + # Write the file out (if changed) + overrides_path = File.expand_path("../../omnibus_overrides.rb", __FILE__) + if overrides != IO.read(overrides_path) + puts "Overrides changed!" + puts `git diff #{overrides_path}` + puts "Writing modified #{overrides_path} ..." + IO.write(overrides_path, overrides) end end end -desc "Update all dependencies and check for outdated gems. Call dependencies[conservative] to update as little as possible." -task :dependencies, [:conservative] => [ "dependencies:update", "bundle:outdated" ] -task :update, [:conservative] => [ "dependencies:update", "bundle:outdated"] +desc "Update all dependencies and check for outdated gems." +task :dependencies => [ "dependencies:update", "bundle:outdated" ] +task :update => [ "dependencies:update", "bundle:outdated"] diff --git a/version_policy.rb b/version_policy.rb index 59ebd58160..8d450010d3 100644 --- a/version_policy.rb +++ b/version_policy.rb @@ -71,12 +71,16 @@ OMNIBUS_RUBYGEMS_AT_LATEST_VERSION = { # jwt - expected to update with new oauth2 release # mini_portile2 - should go away *entirely* with new nokogiri release (not a dep anymore) # slop - expected to disappear with new pry release +# stove - halite pins to ~> 3.2 in 1.2.1 +# rubocop - chef-style pins to 0.39.0 in 0.3.1 # ACCEPTABLE_OUTDATED_GEMS = %w{ gherkin jwt mini_portile2 slop + stove + rubocop } # |