diff options
41 files changed, 872 insertions, 85 deletions
diff --git a/.expeditor/release.omnibus.yml b/.expeditor/release.omnibus.yml index 1b744416fc..59850b1266 100644 --- a/.expeditor/release.omnibus.yml +++ b/.expeditor/release.omnibus.yml @@ -21,6 +21,7 @@ builder-to-testers-map: el-7-aarch64: - el-7-aarch64 - el-8-aarch64 + - amzn-2-aarch64 el-7-ppc64: - el-7-ppc64 el-7-ppc64le: @@ -28,6 +29,7 @@ builder-to-testers-map: el-7-x86_64: - el-7-x86_64 - el-8-x86_64 + - amzn-2-x86_64 freebsd-11-amd64: - freebsd-11-amd64 - freebsd-12-amd64 diff --git a/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml index 69b65ad155..d2389cde90 100644 --- a/.expeditor/verify.pipeline.yml +++ b/.expeditor/verify.pipeline.yml @@ -89,6 +89,7 @@ steps: - label: "Integration Specs openSUSE :ruby: 2.6" commands: - /workdir/scripts/bk_tests/bk_container_prep.sh + - zypper install -y cron insserv-compat - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package docgen - bundle exec rake spec:integration expeditor: @@ -100,7 +101,7 @@ steps: - label: "Functional Specs openSUSE :ruby: 2.6" commands: - /workdir/scripts/bk_tests/bk_container_prep.sh - - zypper install -y cronie + - zypper install -y cronie insserv-compat - cd /workdir; bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package docgen ruby_prof - bundle exec rake spec:functional expeditor: @@ -112,6 +113,7 @@ steps: - label: "Unit Specs openSUSE :ruby: 2.6" commands: - /workdir/scripts/bk_tests/bk_container_prep.sh + - zypper install -y cron insserv-compat - bundle install --jobs=3 --retry=3 --path=vendor/bundle --without omnibus_package docgen ruby_prof - bundle exec rake spec:unit - bundle exec rake component_specs diff --git a/CHANGELOG.md b/CHANGELOG.md index a5353e5f16..d0ff1746a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,24 @@ <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ --> -<!-- latest_release 16.0.194 --> -## [v16.0.194](https://github.com/chef/chef/tree/v16.0.194) (2020-04-08) +<!-- latest_release 16.0.203 --> +## [v16.0.203](https://github.com/chef/chef/tree/v16.0.203) (2020-04-13) #### Merged Pull Requests -- Bump train-core to 3.2.27 [#9628](https://github.com/chef/chef/pull/9628) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot])) +- Remove support for SLES11 and update tests to just on openSUSE [#9299](https://github.com/chef/chef/pull/9299) ([dheerajd-msys](https://github.com/dheerajd-msys)) <!-- latest_release --> <!-- release_rollup since=15.6.10 --> ### Changes not yet released to stable #### Merged Pull Requests +- Remove support for SLES11 and update tests to just on openSUSE [#9299](https://github.com/chef/chef/pull/9299) ([dheerajd-msys](https://github.com/dheerajd-msys)) <!-- 16.0.203 --> +- Add the plist resource from the macos cookbook [#9642](https://github.com/chef/chef/pull/9642) ([tas50](https://github.com/tas50)) <!-- 16.0.202 --> +- Support non-Linux hosts in chef_client_cron [#9647](https://github.com/chef/chef/pull/9647) ([tas50](https://github.com/tas50)) <!-- 16.0.201 --> +- Add resource partials [#9632](https://github.com/chef/chef/pull/9632) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 16.0.200 --> +- Add Amazon Linux 2 Testers [#9641](https://github.com/chef/chef/pull/9641) ([christopher-snapp](https://github.com/christopher-snapp)) <!-- 16.0.199 --> +- Add chef_client_systemd_timer resource [#9624](https://github.com/chef/chef/pull/9624) ([tas50](https://github.com/tas50)) <!-- 16.0.198 --> +- Fix knife bootstrap_version CLI option overriding config option [#9634](https://github.com/chef/chef/pull/9634) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 16.0.197 --> +- Bump highline to 2.x [#9633](https://github.com/chef/chef/pull/9633) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 16.0.196 --> +- Add arm? helper to chef-utils [#9622](https://github.com/chef/chef/pull/9622) ([tas50](https://github.com/tas50)) <!-- 16.0.195 --> - Bump train-core to 3.2.27 [#9628](https://github.com/chef/chef/pull/9628) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot])) <!-- 16.0.194 --> - Fixed systemd_unit not respecting sensitive property [#9623](https://github.com/chef/chef/pull/9623) ([sanga1794](https://github.com/sanga1794)) <!-- 16.0.193 --> - Bump inspec-core to 4.18.104 [#9625](https://github.com/chef/chef/pull/9625) ([chef-expeditor[bot]](https://github.com/chef-expeditor[bot])) <!-- 16.0.192 --> diff --git a/Gemfile.lock b/Gemfile.lock index 80077d2089..d1f4db4ca0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,12 +27,12 @@ GIT PATH remote: . specs: - chef (16.0.194) + chef (16.0.203) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 16.0.194) - chef-utils (= 16.0.194) + chef-config (= 16.0.203) + chef-utils (= 16.0.203) chef-vault chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) @@ -41,7 +41,7 @@ PATH ffi (~> 1.9, >= 1.9.25) ffi-libarchive ffi-yajl (~> 2.2) - highline (>= 1.6.9, < 2) + highline (>= 1.6.9, < 3) iniparse (~> 1.4) license-acceptance (~> 1.0, >= 1.0.5) mixlib-archive (>= 0.4, < 2.0) @@ -61,12 +61,12 @@ PATH train-winrm (>= 0.2.5) tty-screen (~> 0.6) uuidtools (~> 2.1.5) - chef (16.0.194-universal-mingw32) + chef (16.0.203-universal-mingw32) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 16.0.194) - chef-utils (= 16.0.194) + chef-config (= 16.0.203) + chef-utils (= 16.0.203) chef-vault chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) @@ -75,7 +75,7 @@ PATH ffi (~> 1.9, >= 1.9.25) ffi-libarchive ffi-yajl (~> 2.2) - highline (>= 1.6.9, < 2) + highline (>= 1.6.9, < 3) iniparse (~> 1.4) iso8601 (~> 0.12.1) license-acceptance (~> 1.0, >= 1.0.5) @@ -111,15 +111,15 @@ PATH PATH remote: chef-bin specs: - chef-bin (16.0.194) - chef (= 16.0.194) + chef-bin (16.0.203) + chef (= 16.0.203) PATH remote: chef-config specs: - chef-config (16.0.194) + chef-config (16.0.203) addressable - chef-utils (= 16.0.194) + chef-utils (= 16.0.203) fuzzyurl mixlib-config (>= 2.2.12, < 4.0) mixlib-shellout (>= 2.0, < 4.0) @@ -128,7 +128,7 @@ PATH PATH remote: chef-utils specs: - chef-utils (16.0.194) + chef-utils (16.0.203) GEM remote: https://rubygems.org/ @@ -197,7 +197,7 @@ GEM hana (1.3.5) hashdiff (1.0.1) hashie (3.6.0) - highline (1.7.10) + highline (2.0.3) htmlentities (4.3.4) http (2.2.2) addressable (~> 2.3) @@ -1 +1 @@ -16.0.194
\ No newline at end of file +16.0.203
\ No newline at end of file diff --git a/chef-bin/lib/chef-bin/version.rb b/chef-bin/lib/chef-bin/version.rb index a5ff71f8ef..547b2ff61e 100644 --- a/chef-bin/lib/chef-bin/version.rb +++ b/chef-bin/lib/chef-bin/version.rb @@ -21,7 +21,7 @@ module ChefBin CHEFBIN_ROOT = File.expand_path("../..", __FILE__) - VERSION = "16.0.194".freeze + VERSION = "16.0.203".freeze end # diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index c1c6ab7f44..116dbcfe38 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -15,5 +15,5 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "16.0.194".freeze + VERSION = "16.0.203".freeze end diff --git a/chef-utils/README.md b/chef-utils/README.md index e44d4d6937..18830d07c1 100644 --- a/chef-utils/README.md +++ b/chef-utils/README.md @@ -108,17 +108,18 @@ The OS helpers provide an alternative to comparing data from `node['os']`. Architecture Helpers allow you to determine the processor architecture of your node. -* `_64_bit?` * `_32_bit?` +* `_64_bit?` +* `arm?` +* `armhf?` * `i386?` * `intel?` -* `sparc?` +* `powerpc?` * `ppc64?` * `ppc64le?` -* `powerpc?` -* `armhf?` -* `s390x?` * `s390?` +* `s390x?` +* `sparc?` ### Cloud Helpers diff --git a/chef-utils/lib/chef-utils/dsl/architecture.rb b/chef-utils/lib/chef-utils/dsl/architecture.rb index d20c6c5a76..03f2756ab1 100644 --- a/chef-utils/lib/chef-utils/dsl/architecture.rb +++ b/chef-utils/lib/chef-utils/dsl/architecture.rb @@ -103,6 +103,16 @@ module ChefUtils %w{powerpc}.include?(node["kernel"]["machine"]) end + # Determine if the current architecture is arm + # + # @since 15.10 + # + # @return [Boolean] + # + def arm?(node = __getnode) + %w{armhf aarch64 arm64 arch64}.include?(node["kernel"]["machine"]) + end + # Determine if the current architecture is 32-bit ARM. # # @since 15.5 diff --git a/chef-utils/lib/chef-utils/version.rb b/chef-utils/lib/chef-utils/version.rb index b770c6154b..3fab094169 100644 --- a/chef-utils/lib/chef-utils/version.rb +++ b/chef-utils/lib/chef-utils/version.rb @@ -15,5 +15,5 @@ module ChefUtils CHEFUTILS_ROOT = File.expand_path("../..", __FILE__) - VERSION = "16.0.194".freeze + VERSION = "16.0.203".freeze end diff --git a/chef-utils/spec/unit/dsl/architecture_spec.rb b/chef-utils/spec/unit/dsl/architecture_spec.rb index a2ce300fe0..c43b2d0733 100644 --- a/chef-utils/spec/unit/dsl/architecture_spec.rb +++ b/chef-utils/spec/unit/dsl/architecture_spec.rb @@ -84,17 +84,17 @@ RSpec.describe ChefUtils::DSL::Architecture do context "on aarch64" do let(:arch) { "aarch64" } - arch_reports_true_for(:_64_bit?) + arch_reports_true_for(:_64_bit?, :arm?) end context "on arch64" do let(:arch) { "arch64" } - arch_reports_true_for(:_64_bit?) + arch_reports_true_for(:_64_bit?, :arm?) end context "on arm64" do let(:arch) { "arm64" } - arch_reports_true_for(:_64_bit?) + arch_reports_true_for(:_64_bit?, :arm?) end context "on sun4v" do let(:arch) { "sun4v" } @@ -129,7 +129,7 @@ RSpec.describe ChefUtils::DSL::Architecture do context "on armhf" do let(:arch) { "armhf" } - arch_reports_true_for(:armhf?, :_32_bit?) + arch_reports_true_for(:armhf?, :_32_bit?, :arm?) end context "on s390" do let(:arch) { "s390" } diff --git a/chef.gemspec b/chef.gemspec index 427a2cfece..0bdd455ab5 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |s| s.add_dependency "net-sftp", "~> 2.1", ">= 2.1.2" s.add_dependency "ed25519", "~> 1.2" # ed25519 ssh key support s.add_dependency "bcrypt_pbkdf", "~> 1.0" # ed25519 ssh key support - s.add_dependency "highline", ">= 1.6.9", "< 2" + s.add_dependency "highline", ">= 1.6.9", "< 3" s.add_dependency "tty-screen", "~> 0.6" # knife list s.add_dependency "pastel" # knife ui.color s.add_dependency "erubis", "~> 2.7" diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb index c1a335d98c..af1e516030 100644 --- a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb +++ b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb @@ -67,7 +67,6 @@ end include_recipe "chef-client::delete_validation" include_recipe "chef-client::config" -include_recipe "chef-client" include_recipe "openssh" @@ -149,6 +148,11 @@ chef_client_cron "Run chef-client with base recipe" do daemon_options ["--override-runlist mycorp_base::default"] end +chef_client_systemd_timer "Run chef-client as a systemd timer" do + interval "1hr" + only_if { systemd? } +end + include_recipe "::chef-vault" unless includes_recipe?("end_to_end::chef-vault") include_recipe "::alternatives" include_recipe "::tests" diff --git a/kitchen-tests/kitchen.yml b/kitchen-tests/kitchen.yml index 8a8e749b84..b13b31125e 100644 --- a/kitchen-tests/kitchen.yml +++ b/kitchen-tests/kitchen.yml @@ -151,6 +151,7 @@ platforms: intermediate_instructions: - RUN /usr/bin/zypper --non-interactive update - RUN /usr/bin/zypper --non-interactive install cron + - RUN /usr/bin/zypper --non-interactive install insserv-compat suites: - name: end-to-end diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb index d0b33628ee..3ab7773e21 100644 --- a/lib/chef/knife/bootstrap.rb +++ b/lib/chef/knife/bootstrap.rb @@ -1,6 +1,6 @@ # # Author:: Adam Jacob (<adam@chef.io>) -# Copyright:: Copyright 2010-2019, Chef Software Inc. +# Copyright:: Copyright 2010-2020, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -149,8 +149,7 @@ class Chef # client.rb content via chef-full/bootstrap_context option :bootstrap_version, long: "--bootstrap-version VERSION", - description: "The version of #{Chef::Dist::PRODUCT} to install.", - proc: lambda { |v| Chef::Config[:knife][:bootstrap_version] = v } + description: "The version of #{Chef::Dist::PRODUCT} to install." option :channel, long: "--channel CHANNEL", diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb index 8f07149ebe..f889e7b7ed 100644 --- a/lib/chef/knife/core/bootstrap_context.rb +++ b/lib/chef/knife/core/bootstrap_context.rb @@ -1,6 +1,6 @@ # # Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2011-2016, Chef Software, Inc. +# Copyright:: Copyright 2011-2020, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -194,6 +194,8 @@ class Chef s end + # XXX: this reads values only out of the config file and is NOT merged with the CLI options, and it is most likely + # a bug to be using this accessor and we should be using config and not knife_config. def knife_config @chef_config.key?(:knife) ? @chef_config[:knife] : {} end @@ -203,7 +205,7 @@ class Chef # # @return [String] download version string def version_to_install - return knife_config[:bootstrap_version] if knife_config[:bootstrap_version] + return @config[:bootstrap_version] if @config[:bootstrap_version] if @config[:channel] == "stable" Chef::VERSION.split(".").first diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb index 3783bd9d5f..40bd5a48a1 100644 --- a/lib/chef/provider.rb +++ b/lib/chef/provider.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Christopher Walters (<cw@chef.io>) -# Copyright:: Copyright 2008-2016, 2009-2020, Chef Software Inc. +# Copyright:: Copyright (c) Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -81,6 +81,20 @@ class Chef # Chef.deprecated(:use_inline_resources, "The use_inline_resources mode is no longer optional and the line enabling it can be removed") end + # Use a partial code fragment. This can be used for code sharing between multiple resources. + # + # Do not wrap the code fragment in a class or module. It also does not support the use of super + # to subclass any methods defined in the fragment, the methods will just be overwritten. + # + # @param partial [String] the code fragment to eval against the class + # + def self.use(partial) + dirname = ::File.dirname(partial) + basename = ::File.basename(partial, ".rb") + basename = basename[1..-1] if basename.start_with?("_") + class_eval IO.read(::File.expand_path("#{dirname}/_#{basename}.rb", ::File.dirname(caller_locations.first.absolute_path))) + end + # delegate to the resource # def_delegators :@new_resource, :property_is_set? diff --git a/lib/chef/provider/launchd.rb b/lib/chef/provider/launchd.rb index 3516cadda7..fbb9307712 100644 --- a/lib/chef/provider/launchd.rb +++ b/lib/chef/provider/launchd.rb @@ -161,7 +161,7 @@ class Chef def content plist_hash = new_resource.plist_hash || gen_hash - Plist::Emit.dump(plist_hash) unless plist_hash.nil? + ::Plist::Emit.dump(plist_hash) unless plist_hash.nil? end def gen_hash diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb index ba63d4b02a..07d35e633c 100644 --- a/lib/chef/provider/osx_profile.rb +++ b/lib/chef/provider/osx_profile.rb @@ -21,6 +21,7 @@ require_relative "../provider" require_relative "../resource" require_relative "../resource/file" require "uuidtools" +require "plist" class Chef class Provider @@ -232,7 +233,7 @@ class Chef end def read_plist(xml_file) - Plist.parse_xml(xml_file) + ::Plist.parse_xml(xml_file) end def profile_installed? diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index 687fc021da..a46ecb3a62 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -118,7 +118,7 @@ in 'password', with the associated 'salt' and 'iterations'.") # Calling shell_out directly since we want to give an input stream shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string) - shadow_hash = Plist.parse_xml(shadow_hash_xml) + shadow_hash = ::Plist.parse_xml(shadow_hash_xml) if shadow_hash["SALTED-SHA512-PBKDF2"] # 10.7+ contains this, but we retain the check in case it goes away in the future @password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2" @@ -541,7 +541,7 @@ in 'password', with the associated 'salt' and 'iterations'.") begin user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist" user_plist_info = run_plutil("convert", "xml1", "-o", "-", user_plist_file) - user_info = Plist.parse_xml(user_plist_info) + user_info = ::Plist.parse_xml(user_plist_info) rescue Chef::Exceptions::PlistUtilCommandFailed end @@ -554,7 +554,7 @@ in 'password', with the associated 'salt' and 'iterations'.") # def save_user_info(user_info) user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist" - Plist::Emit.save_plist(user_info, user_plist_file) + ::Plist::Emit.save_plist(user_info, user_plist_file) run_plutil("convert", "binary1", user_plist_file) end diff --git a/lib/chef/provider/user/mac.rb b/lib/chef/provider/user/mac.rb index 312d2a7b47..104cf51abe 100644 --- a/lib/chef/provider/user/mac.rb +++ b/lib/chef/provider/user/mac.rb @@ -22,6 +22,7 @@ require_relative "../../mixin/shell_out" require_relative "../../mixin/which" require_relative "../user" require_relative "../../resource/user/mac_user" +require "plist" class Chef class Provider @@ -79,7 +80,7 @@ class Chef admin_group_xml = run_dscl("read", "/Groups/admin") return nil unless admin_group_xml && admin_group_xml != "" - @admin_group_plist = Plist.new(::Plist.parse_xml(admin_group_xml)) + @admin_group_plist = ::Plist.new(::Plist.parse_xml(admin_group_xml)) end def reload_user_plist @@ -94,7 +95,7 @@ class Chef return nil if user_xml.nil? || user_xml == "" - @user_plist = Plist.new(::Plist.parse_xml(user_xml)) + @user_plist = ::Plist.new(::Plist.parse_xml(user_xml)) return unless user_plist[:shadow_hash] diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index bbbe714f3d..fcf22a36b1 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -1461,6 +1461,20 @@ class Chef @default_description end + # Use a partial code fragment. This can be used for code sharing between multiple resources. + # + # Do not wrap the code fragment in a class or module. It also does not support the use of super + # to subclass any methods defined in the fragment, the methods will just be overwritten. + # + # @param partial [String] the code fragment to eval against the class + # + def self.use(partial) + dirname = ::File.dirname(partial) + basename = ::File.basename(partial, ".rb") + basename = basename[1..-1] if basename.start_with?("_") + class_eval IO.read(::File.expand_path("#{dirname}/_#{basename}.rb", ::File.dirname(caller_locations.first.absolute_path))) + end + # The cookbook in which this Resource was defined (if any). # # @return Chef::CookbookVersion The cookbook in which this Resource was defined. diff --git a/lib/chef/resource/build_essential.rb b/lib/chef/resource/build_essential.rb index 5a41f3895b..ab1be3bcfd 100644 --- a/lib/chef/resource/build_essential.rb +++ b/lib/chef/resource/build_essential.rb @@ -161,7 +161,7 @@ class Chef # # @return [true, false] def xcode_cli_installed? - packages = Plist.parse_xml(::File.open("/Library/Receipts/InstallHistory.plist", "r")) + packages = ::Plist.parse_xml(::File.open("/Library/Receipts/InstallHistory.plist", "r")) packages.select! { |package| package["displayName"].match? "Command Line Tools" } !packages.empty? end diff --git a/lib/chef/resource/chef_client_cron.rb b/lib/chef/resource/chef_client_cron.rb index fb30bd0538..681327bcd3 100644 --- a/lib/chef/resource/chef_client_cron.rb +++ b/lib/chef/resource/chef_client_cron.rb @@ -52,6 +52,13 @@ class Chef extend Chef::ResourceHelpers::CronValidations + property :job_name, String, + default: Chef::Dist::CLIENT, + description: "The name of the cron job to create." + + property :comment, String, + description: "A comment to place in the cron.d file." + property :user, String, description: "The name of the user that #{Chef::Dist::PRODUCT} runs as.", default: "root" @@ -86,29 +93,18 @@ class Chef "should be a valid weekday spec" => method(:validate_dow), } - property :mailto, String, - description: "The e-mail address to e-mail any cron task failures to." - - property :accept_chef_license, [true, false], - description: "Accept the Chef Online Master License and Services Agreement. See https://www.chef.io/online-master-agreement/", - default: false - - property :job_name, String, - default: Chef::Dist::CLIENT, - description: "The name of the cron job to create." - property :splay, [Integer, String], default: 300, coerce: proc { |x| Integer(x) }, callbacks: { "should be a positive number" => proc { |v| v > 0 } }, description: "A random number of seconds between 0 and X to add to interval so that all #{Chef::Dist::CLIENT} commands don't execute at the same time." - property :environment, Hash, - default: lazy { {} }, - description: "A Hash containing additional arbitrary environment variables under which the cron job will be run in the form of ``({'ENV_VARIABLE' => 'VALUE'})``." + property :mailto, String, + description: "The e-mail address to e-mail any cron task failures to." - property :comment, String, - description: "A comment to place in the cron.d file." + property :accept_chef_license, [true, false], + description: "Accept the Chef Online Master License and Services Agreement. See https://www.chef.io/online-master-agreement/", + default: false property :config_directory, String, default: Chef::Dist::CONF_DIR, @@ -134,6 +130,10 @@ class Chef default: lazy { [] }, description: "An array of options to pass to the #{Chef::Dist::CLIENT} command." + property :environment, Hash, + default: lazy { {} }, + description: "A Hash containing additional arbitrary environment variables under which the cron job will be run in the form of ``({'ENV_VARIABLE' => 'VALUE'})``." + action :add do # TODO: Replace this with a :create_if_missing action on directory when that exists unless ::Dir.exist?(new_resource.log_directory) @@ -144,8 +144,8 @@ class Chef end end - cron_d new_resource.job_name do - minute new_resource.minute + declare_resource(cron_resource_type, new_resource.job_name) do + minute new_resource.minute hour new_resource.hour day new_resource.day weekday new_resource.weekday @@ -207,6 +207,17 @@ class Chef "> #{::File.join(new_resource.log_directory, new_resource.log_file_name)} 2>&1" end end + + # + # The type of cron resource to run. Linux systems all support the /etc/cron.d directory + # and can use the cron_d resource, but Solaris / AIX / FreeBSD need to use the crontab + # via the legacy cron resource. + # + # @return [Symbol] + # + def cron_resource_type + linux? ? :cron_d : :cron + end end end end diff --git a/lib/chef/resource/chef_client_scheduled_task.rb b/lib/chef/resource/chef_client_scheduled_task.rb index c4d2bc97c6..3d9deb6ca3 100644 --- a/lib/chef/resource/chef_client_scheduled_task.rb +++ b/lib/chef/resource/chef_client_scheduled_task.rb @@ -49,6 +49,10 @@ class Chef resource_name :chef_client_scheduled_task + property :task_name, String, + description: "The name of the scheduled task to create.", + default: Chef::Dist::CLIENT + property :user, String, description: "The name of the user that #{Chef::Dist::PRODUCT} runs as.", default: "System", sensitive: true @@ -109,10 +113,6 @@ class Chef description: "An array of options to pass to the #{Chef::Dist::CLIENT} command.", default: lazy { [] } - property :task_name, String, - description: "The name of the scheduled task to create.", - default: Chef::Dist::CLIENT - action :add do # TODO: Replace this with a :create_if_missing action on directory when that exists unless Dir.exist?(new_resource.log_directory) diff --git a/lib/chef/resource/chef_client_systemd_timer.rb b/lib/chef/resource/chef_client_systemd_timer.rb new file mode 100644 index 0000000000..359fe951a0 --- /dev/null +++ b/lib/chef/resource/chef_client_systemd_timer.rb @@ -0,0 +1,177 @@ +# +# Copyright:: 2020, Chef Software Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require_relative "../resource" +require_relative "../dist" + +class Chef + class Resource + class ChefClientSystemdTimer < Chef::Resource + unified_mode true + + provides :chef_client_systemd_timer + + description "Use the chef_client_systemd_timer resource to setup the #{Chef::Dist::PRODUCT} to run as a systemd timer." + introduced "16.0" + examples <<~DOC + Setup #{Chef::Dist::PRODUCT} to run using the default 30 minute cadence + ```ruby + chef_client_systemd_timer "Run chef-client as a systemd timer" + ``` + + Run #{Chef::Dist::PRODUCT} every 1 hour + ```ruby + chef_client_systemd_timer "Run chef-client every 1 hour" do + interval "1hr" + end + ``` + + Run #{Chef::Dist::PRODUCT} with extra options passed to the client + ```ruby + chef_client_systemd_timer "Run an override recipe" do + daemon_options ["--override-runlist mycorp_base::default"] + end + ``` + DOC + + property :job_name, String, + description: "The name of the system timer to create.", + default: Chef::Dist::CLIENT + + property :description, String, + description: "The description to add to the systemd timer. This will be displayed when running `systemctl status` for the timer.", + default: "#{Chef::Dist::PRODUCT} periodic execution" + + property :user, String, + description: "The name of the user that #{Chef::Dist::PRODUCT} runs as.", + default: "root" + + property :delay_after_boot, String, + description: "The time to wait after booting before the interval starts. This is expressed as a systemd time span such as `300seconds`, `1hr`, or `1m`. See <https://www.freedesktop.org/software/systemd/man/systemd.time.html> for a complete list of allowed time span values.", + default: "1min" + + property :interval, String, + description: "The interval to wait between executions. This is expressed as a systemd time span such as `300seconds`, `1hr`, or `1m`. See <https://www.freedesktop.org/software/systemd/man/systemd.time.html> for a complete list of allowed time span values.", + default: "30min" + + property :splay, String, + description: "A interval between 0 and X to add to the interval so that all #{Chef::Dist::CLIENT} commands don't execute at the same time. This is expressed as a systemd time span such as `300seconds`, `1hr`, or `1m`. See <https://www.freedesktop.org/software/systemd/man/systemd.time.html> for a complete list of allowed time span values.", + default: "5min" + + property :accept_chef_license, [true, false], + description: "Accept the Chef Online Master License and Services Agreement. See https://www.chef.io/online-master-agreement/", + default: false + + property :run_on_battery, [true, false], + description: "Run the timer for #{Chef::Dist::PRODUCT} if the system is on battery.", + default: true + + property :config_directory, String, + description: "The path of the config directory.", + default: Chef::Dist::CONF_DIR + + property :chef_binary_path, String, + description: "The path to the #{Chef::Dist::CLIENT} binary.", + default: "/opt/#{Chef::Dist::DIR_SUFFIX}/bin/#{Chef::Dist::CLIENT}" + + property :daemon_options, Array, + description: "An array of options to pass to the #{Chef::Dist::CLIENT} command.", + default: lazy { [] } + + property :environment, Hash, + description: "A Hash containing additional arbitrary environment variables under which the systemd timer will be run in the form of ``({'ENV_VARIABLE' => 'VALUE'})``.", + default: lazy { {} } + + action :add do + systemd_unit "#{new_resource.job_name}.service" do + content service_content + action :create + end + + systemd_unit "#{new_resource.job_name}.timer" do + content timer_content + action %i{create enable start} + end + end + + action :remove do + systemd_unit "#{new_resource.job_name}.service" do + action :remove + end + + systemd_unit "#{new_resource.job_name}.timer" do + action :remove + end + end + + action_class do + # + # The chef-client command to run in the systemd unit. + # + # @return [String] + # + def chef_client_cmd + cmd = "#{new_resource.chef_binary_path}" + cmd << " #{new_resource.daemon_options.join(" ")}" unless new_resource.daemon_options.empty? + cmd << " --chef-license accept" if new_resource.accept_chef_license + cmd << " -c #{::File.join(new_resource.config_directory, "client.rb")}" + cmd + end + + # + # The timer content to pass to the systemd_unit + # + # @return [Hash] + # + def timer_content + { + "Unit" => { "Description" => new_resource.description }, + "Timer" => { + "OnBootSec" => new_resource.delay_after_boot, + "OnUnitActiveSec" => new_resource.interval, + "RandomizedDelaySec" => new_resource.splay, + }, + "Install" => { "WantedBy" => "timers.target" }, + } + end + + # + # The service content to pass to the systemd_unit + # + # @return [Hash] + # + def service_content + unit = { + "Unit" => { + "Description" => new_resource.description, + "After" => "network.target auditd.service", + }, + "Service" => { + "Type" => "oneshot", + "ExecStart" => chef_client_cmd, + "SuccessExitStatus" => [3, 213, 35, 37, 41], + }, + "Install" => { "WantedBy" => "multi-user.target" }, + } + + unit["Service"]["ConditionACPower"] = "true" unless new_resource.run_on_battery + unit["Service"]["Environment"] = new_resource.environment.collect { |k, v| "\"#{k}=#{v}\"" } unless new_resource.environment.empty? + unit + end + end + end + end +end diff --git a/lib/chef/resource/plist.rb b/lib/chef/resource/plist.rb new file mode 100644 index 0000000000..d5d2ce3c6c --- /dev/null +++ b/lib/chef/resource/plist.rb @@ -0,0 +1,207 @@ +# +# Copyright:: Copyright 2017-2020, Microsoft Corporation +# Copyright:: Copyright 2020, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require_relative "../resource" +require "plist" + +class Chef + class Resource + + class PlistResource < Chef::Resource # we name this PlistResource to avoid confusion with Plist from the plist gem + unified_mode true + + provides :plist + + description "Use the plist resource to set config values in plist files on macOS systems." + introduced "16.0" + + property :path, String, name_property: true + property :entry, String + property :value, [TrueClass, FalseClass, String, Integer, Float, Hash] + property :encoding, String, default: "binary" + property :owner, String, default: "root" + property :group, String, default: "wheel" + property :mode, [String, Integer] + + PLISTBUDDY_EXECUTABLE = "/usr/libexec/PlistBuddy".freeze + DEFAULTS_EXECUTABLE = "/usr/bin/defaults".freeze + PLUTIL_EXECUTABLE = "/usr/bin/plutil".freeze + PLUTIL_FORMAT_MAP = { "us-ascii" => "xml1", + "text/xml" => "xml1", + "utf-8" => "xml1", + "binary" => "binary1" }.freeze + + load_current_value do |desired| + current_value_does_not_exist! unless ::File.exist? desired.path + entry desired.entry if entry_in_plist? desired.entry, desired.path + + setting = setting_from_plist desired.entry, desired.path + value convert_to_data_type_from_string(setting[:key_type], setting[:key_value]) + + file_type_cmd = shell_out "/usr/bin/file", "--brief", "--mime-encoding", "--preserve-date", desired.path + encoding file_type_cmd.stdout.chomp + + file_owner_cmd = shell_out("/usr/bin/stat", "-f", "%Su", desired.path) + owner file_owner_cmd.stdout.chomp + + file_group_cmd = shell_out("/usr/bin/stat", "-f", "%Sg", desired.path) + group file_group_cmd.stdout.chomp + end + + action :set do + converge_if_changed :path do + converge_by "create new plist: '#{new_resource.path}'" do + file new_resource.path do + content {}.to_plist + owner new_resource.owner + group new_resource.group + mode new_resource.mode if property_is_set?(:mode) + end + end + end + + plist_file_name = ::File.basename(new_resource.path) + + converge_if_changed :entry do + converge_by "add entry \"#{new_resource.entry}\" to #{plist_file_name}" do + shell_out!(plistbuddy_command(:add, new_resource.entry, new_resource.path, new_resource.value)) + end + end + + converge_if_changed :value do + converge_by "#{plist_file_name}: set #{new_resource.entry} to #{new_resource.value}" do + shell_out!(plistbuddy_command(:set, new_resource.entry, new_resource.path, new_resource.value)) + end + end + + converge_if_changed :encoding do + converge_by "change format" do + unless PLUTIL_FORMAT_MAP.key?(new_resource.encoding) + Chef::Application.fatal!( + "Option encoding must be equal to one of: #{PLUTIL_FORMAT_MAP.keys}! You passed \"#{new_resource.encoding}\"." + ) + end + shell_out!(PLUTIL_EXECUTABLE, "-convert", PLUTIL_FORMAT_MAP[new_resource.encoding], new_resource.path) + end + end + + converge_if_changed :owner do + converge_by "update owner to #{new_resource.owner}" do + file new_resource.path do + owner new_resource.owner + end + end + end + + converge_if_changed :group do + converge_by "update group to #{new_resource.group}" do + file new_resource.path do + group new_resource.group + end + end + end + end + + ### Question: Should I refactor these methods into an action_class? + ### Answer: NO + ### Why: We need them in both the action and in load_current_value. If you put them in the + ### action class then they're only in the Provider class and are not available to load_current_value + + def convert_to_data_type_from_string(type, value) + case type + when "boolean" + # Since we've determined this is a boolean data type, we can assume that: + # If the value as an int is 1, return true + # If the value as an int is 0 (not 1), return false + value.to_i == 1 + when "integer" + value.to_i + when "float" + value.to_f + when "string" + value + when "dictionary" + value + when nil + "" + else + raise "Unknown or unsupported data type: #{type.class}" + end + end + + def type_to_commandline_string(value) + case value + when Array + "array" + when Integer + "integer" + when FalseClass + "bool" + when TrueClass + "bool" + when Hash + "dict" + when String + "string" + when Float + "float" + else + raise "Unknown or unsupported data type: #{value} of #{value.class}" + end + end + + def entry_in_plist?(entry, path) + print_entry = plistbuddy_command :print, entry, path + cmd = shell_out print_entry + cmd.exitstatus == 0 + end + + def plistbuddy_command(subcommand, entry, path, value = nil) + sep = " " + arg = case subcommand.to_s + when "add" + type_to_commandline_string(value) + when "set" + if value.is_a?(Hash) + sep = ":" + value.map { |k, v| "#{k} #{v}" } + else + value + end + else + "" + end + entry_with_arg = ["\"#{entry}\"", arg].join(sep).strip + subcommand = "#{subcommand.capitalize} :#{entry_with_arg}" + [PLISTBUDDY_EXECUTABLE, "-c", "\'#{subcommand}\'", "\"#{path}\""].join(" ") + end + + def setting_from_plist(entry, path) + defaults_read_type_output = shell_out(DEFAULTS_EXECUTABLE, "read-type", path, entry).stdout + data_type = defaults_read_type_output.split.last + + if value.class == Hash + plutil_output = shell_out(PLUTIL_EXECUTABLE, "-extract", entry, "xml1", "-o", "-", path).stdout.chomp + { key_type: data_type, key_value: ::Plist.parse_xml(plutil_output) } + else + defaults_read_output = shell_out(DEFAULTS_EXECUTABLE, "read", path, entry).stdout + { key_type: data_type, key_value: defaults_read_output.strip } + end + end + end + end +end diff --git a/lib/chef/resource/sysctl.rb b/lib/chef/resource/sysctl.rb index 240f45e60f..82dbe61081 100644 --- a/lib/chef/resource/sysctl.rb +++ b/lib/chef/resource/sysctl.rb @@ -57,7 +57,6 @@ class Chef def after_created raise "The sysctl resource requires Linux as it needs sysctl and the sysctl.d directory functionality." unless node["os"] == "linux" - raise "The sysctl resource does not support SLES releases less than 12 as it requires a sysctl.d directory" if platform_family?("suse") && node["platform_version"].to_i < 12 end def coerce_value(v) diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 66bec89658..c7191ef69f 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -29,6 +29,7 @@ require_relative "resource/build_essential" require_relative "resource/cookbook_file" require_relative "resource/chef_client_cron" require_relative "resource/chef_client_scheduled_task" +require_relative "resource/chef_client_systemd_timer" require_relative "resource/chef_gem" require_relative "resource/chef_handler" require_relative "resource/chef_sleep" @@ -86,6 +87,7 @@ require_relative "resource/package" require_relative "resource/pacman_package" require_relative "resource/paludis_package" require_relative "resource/perl" +require_relative "resource/plist" require_relative "resource/portage_package" require_relative "resource/powershell_package_source" require_relative "resource/powershell_script" diff --git a/lib/chef/run_context/cookbook_compiler.rb b/lib/chef/run_context/cookbook_compiler.rb index 4dda6aeb2e..36ee34c62e 100644 --- a/lib/chef/run_context/cookbook_compiler.rb +++ b/lib/chef/run_context/cookbook_compiler.rb @@ -245,11 +245,13 @@ class Chef def load_lwrps_from_cookbook(cookbook_name) files_in_cookbook_by_segment(cookbook_name, :providers).each do |filename| next unless File.extname(filename) == ".rb" + next if File.basename(filename).match?(/^_/) load_lwrp_provider(cookbook_name, filename) end files_in_cookbook_by_segment(cookbook_name, :resources).each do |filename| next unless File.extname(filename) == ".rb" + next if File.basename(filename).match?(/^_/) load_lwrp_resource(cookbook_name, filename) end diff --git a/lib/chef/version.rb b/lib/chef/version.rb index c2477c32c9..40001c4dba 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -23,7 +23,7 @@ require_relative "version_string" class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = Chef::VersionString.new("16.0.194") + VERSION = Chef::VersionString.new("16.0.203") end # diff --git a/omnibus/omnibus-test.sh b/omnibus/omnibus-test.sh index 0499d03b0b..2a3b6e520e 100644 --- a/omnibus/omnibus-test.sh +++ b/omnibus/omnibus-test.sh @@ -131,7 +131,7 @@ FORCE_FFI_YAJL=ext export FORCE_FFI_YAJL # chef-shell smoke tests require "rb-readline" which requires "infocmp" -# most platforms provide "infocmp" by default via an "ncurses" package but SLES 11 and 12 provide it via "ncurses-devel" which +# most platforms provide "infocmp" by default via an "ncurses" package but SLES 12 provide it via "ncurses-devel" which # isn't typically installed. omnibus-toolchain has "infocmp" built-in so we add omnibus-toolchain to the PATH to ensure # tests will function properly. PATH="/opt/$TOOLCHAIN/bin:/usr/local/bin:/opt/$TOOLCHAIN/embedded/bin:$PATH" diff --git a/spec/functional/assets/inittest b/spec/functional/assets/inittest index dc542a965a..3284d27a1e 100644 --- a/spec/functional/assets/inittest +++ b/spec/functional/assets/inittest @@ -2,16 +2,17 @@ TMPDIR="${TMPDIR:-/tmp}" -function create_chef_txt { - touch $TMPDIR/inittest.txt +create_chef_txt() { + touch "$TMPDIR"/inittest.txt } -function delete_chef_txt { - rm $TMPDIR/inittest.txt + +delete_chef_txt() { + rm "$TMPDIR"/inittest.txt } -function rename_chef_txt { - mv $TMPDIR/inittest.txt $TMPDIR/$1 +rename_chef_txt() { + mv "$TMPDIR"/inittest.txt "$TMPDIR"/"$1" } case "$1" in @@ -22,7 +23,7 @@ stop ) delete_chef_txt ;; status ) - [ -f $TMPDIR/inittest.txt ] || [ -f $TMPDIR/inittest_reload.txt ] || [ -f $TMPDIR/inittest_restart.txt ] + [ -f "$TMPDIR"/inittest.txt ] || [ -f "$TMPDIR"/inittest_reload.txt ] || [ -f "$TMPDIR"/inittest_restart.txt ] ;; reload ) rename_chef_txt "inittest_reload.txt" diff --git a/spec/functional/resource/insserv_spec.rb b/spec/functional/resource/insserv_spec.rb index 55a803d431..1af33b434b 100644 --- a/spec/functional/resource/insserv_spec.rb +++ b/spec/functional/resource/insserv_spec.rb @@ -22,7 +22,7 @@ require "functional/resource/base" require "chef/mixin/shell_out" require "fileutils" -describe Chef::Resource::Service, :requires_root, :sles11 do +describe Chef::Resource::Service, :requires_root, :opensuse do include Chef::Mixin::ShellOut diff --git a/spec/integration/recipes/notifies_spec.rb b/spec/integration/recipes/notifies_spec.rb index 6d781922f5..9767cf5de7 100644 --- a/spec/integration/recipes/notifies_spec.rb +++ b/spec/integration/recipes/notifies_spec.rb @@ -1,3 +1,19 @@ +# +# Copyright:: Copyright Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + require "spec_helper" require "support/shared/integration/integration_helper" require "chef/mixin/shell_out" diff --git a/spec/integration/recipes/use_partial_spec.rb b/spec/integration/recipes/use_partial_spec.rb new file mode 100644 index 0000000000..b8e8a27635 --- /dev/null +++ b/spec/integration/recipes/use_partial_spec.rb @@ -0,0 +1,112 @@ +# +# Copyright:: Copyright Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "spec_helper" +require "support/shared/integration/integration_helper" +require "chef/mixin/shell_out" + +describe "notifying_block" do + include IntegrationSupport + include Chef::Mixin::ShellOut + + let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) } + let(:chef_client) { "bundle exec chef-client --minimal-ohai" } + + when_the_repository "has a cookbook with partial resources" do + before do + directory "cookbooks/x" do + file "resources/_shared_properties.rb", <<-EOM + property :content, String + EOM + file "resources/_action_helpers.rb", <<-EOM + def printit(string) + puts "DIDIT: \#{string}" + end + EOM + file "resources/thing.rb", <<-EOM + provides :thing + use "shared_properties" + action_class do + use "action_helpers" + end + action :run do + printit(new_resource.content) + end + EOM + file "recipes/default.rb", <<~EOM + thing "whatever" do + content "stuff" + end + EOM + end + file "config/client.rb", <<-EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + always_dump_stacktrace true + EOM + end + + it "should run cleanly and print the output" do + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + expect(result.stdout).to match(/DIDIT: stuff/) + result.error! + end + end + + when_the_repository "has a cookbook with partial resources done differently" do + before do + directory "cookbooks/x" do + file "partials/_shared_properties.rb", <<-EOM + property :content, String + EOM + file "partials/_action_partials.rb", <<-EOM + def printit(string) + puts "DIDIT: \#{string}" + end + EOM + # this tests relative pathing, including the underscore and including the trailing .rb all work + file "resources/thing.rb", <<-EOM + provides :thing + use "../partials/_shared_properties.rb" + action_class do + use "../partials/_action_partials.rb" + end + action :run do + printit(new_resource.content) + end + EOM + file "recipes/default.rb", <<~EOM + thing "whatever" do + content "stuff" + end + EOM + end + file "config/client.rb", <<-EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + always_dump_stacktrace true + EOM + end + + it "should run cleanly and print the output" do + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + expect(result.stdout).to match(/DIDIT: stuff/) + result.error! + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 49e8d2f8f5..c6fd27d271 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -169,7 +169,7 @@ RSpec.configure do |config| config.filter_run_excluding linux_only: true unless linux? config.filter_run_excluding aix_only: true unless aix? config.filter_run_excluding suse_only: true unless suse? - config.filter_run_excluding sles11: true unless sles11? + config.filter_run_excluding opensuse: true unless opensuse? config.filter_run_excluding debian_family_only: true unless debian_family? config.filter_run_excluding supports_cloexec: true unless supports_cloexec? config.filter_run_excluding selinux_only: true unless selinux_enabled? diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb index e92da37043..0e98fce621 100644 --- a/spec/support/platform_helpers.rb +++ b/spec/support/platform_helpers.rb @@ -174,8 +174,8 @@ def rhel6? rhel? && !!(ohai[:platform_version].to_i == 6) end -def sles11? - suse? && !!(ohai[:platform_version].to_i == 11) +def opensuse? + suse? && !!(ohai[:platform_version].to_i >= 15) end def rhel7? diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb index 99035eff4e..7ddb981aba 100644 --- a/spec/unit/knife/core/bootstrap_context_spec.rb +++ b/spec/unit/knife/core/bootstrap_context_spec.rb @@ -1,6 +1,6 @@ # # Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2011-2016, Chef Software Inc. +# Copyright:: Copyright 2011-2020, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -297,7 +297,7 @@ describe Chef::Knife::Core::BootstrapContext do describe "#version_to_install" do context "when bootstrap_version is provided" do - let(:chef_config) { { knife: { bootstrap_version: "awesome" } } } + let(:config) { { bootstrap_version: "awesome" } } it "returns bootstrap_version" do expect(bootstrap_context.version_to_install).to eq "awesome" diff --git a/spec/unit/resource/chef_client_systemd_timer_spec.rb b/spec/unit/resource/chef_client_systemd_timer_spec.rb new file mode 100644 index 0000000000..139d4c6ba3 --- /dev/null +++ b/spec/unit/resource/chef_client_systemd_timer_spec.rb @@ -0,0 +1,70 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: 2020, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::ChefClientSystemdTimer do + let(:node) { Chef::Node.new } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } + let(:resource) { Chef::Resource::ChefClientSystemdTimer.new("fakey_fakerton", run_context) } + let(:provider) { resource.provider_for_action(:add) } + + it "sets the default action as :add" do + expect(resource.action).to eql([:add]) + end + + it "user defaults to root" do + expect(resource.user).to eql("root") + end + + it "builds a default value for chef_binary_path dist values" do + expect(resource.chef_binary_path).to eql("/opt/chef/bin/chef-client") + end + + it "supports :add and :remove actions" do + expect { resource.action :add }.not_to raise_error + expect { resource.action :remove }.not_to raise_error + end + + describe "#chef_client_cmd" do + it "creates a valid command if using all default properties" do + expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client -c /etc/chef/client.rb") + end + + it "uses daemon_options if set" do + resource.daemon_options ["--foo 1", "--bar 2"] + expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client --foo 1 --bar 2 -c /etc/chef/client.rb") + end + + it "uses custom config dir if set" do + resource.config_directory "/etc/some_other_dir" + expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client -c /etc/some_other_dir/client.rb") + end + + it "uses custom chef-client binary if set" do + resource.chef_binary_path "/usr/local/bin/chef-client" + expect(provider.chef_client_cmd).to eql("/usr/local/bin/chef-client -c /etc/chef/client.rb") + end + + it "sets the license acceptance flag if set" do + resource.accept_chef_license true + expect(provider.chef_client_cmd).to eql("/opt/chef/bin/chef-client --chef-license accept -c /etc/chef/client.rb") + end + end +end diff --git a/spec/unit/resource/plist_spec.rb b/spec/unit/resource/plist_spec.rb new file mode 100644 index 0000000000..ab5b35b6c3 --- /dev/null +++ b/spec/unit/resource/plist_spec.rb @@ -0,0 +1,130 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: 2020, Chef Software Inc. +# Copyright:: 2017-2020, Microsoft Corporation +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::PlistResource do + let(:resource) { Chef::Resource::PlistResource.new("fakey_fakerton") } + + it "sets the default action as :set" do + expect(resource.action).to eql([:set]) + end + + it "path is the name property" do + expect(resource.path).to eql("fakey_fakerton") + end + + describe "#plistbuddy_command" do + it "the bool arguments contain the data type" do + expect(resource.plistbuddy_command(:add, "FooEntry", "path/to/file.plist", true)).to eq "/usr/libexec/PlistBuddy -c 'Add :\"FooEntry\" bool' \"path/to/file.plist\"" + end + + it "the add command only adds the data type" do + expect(resource.plistbuddy_command(:add, "QuuxEntry", "path/to/file.plist", 50)).to eq "/usr/libexec/PlistBuddy -c 'Add :\"QuuxEntry\" integer' \"path/to/file.plist\"" + end + + it "the delete command is formatted properly" do + expect(resource.plistbuddy_command(:delete, "BarEntry", "path/to/file.plist")).to eq "/usr/libexec/PlistBuddy -c 'Delete :\"BarEntry\"' \"path/to/file.plist\"" + end + + it "the set command is formatted properly" do + expect(resource.plistbuddy_command(:set, "BazEntry", "path/to/file.plist", false)).to eq "/usr/libexec/PlistBuddy -c 'Set :\"BazEntry\" false' \"path/to/file.plist\"" + end + + it "the print command is formatted properly" do + expect(resource.plistbuddy_command(:print, "QuxEntry", "path/to/file.plist")).to eq "/usr/libexec/PlistBuddy -c 'Print :\"QuxEntry\"' \"path/to/file.plist\"" + end + + it "the command to set a dictionary data type is formatted properly" do + expect(resource.plistbuddy_command(:set, "AppleFirstWeekday", "path/to/file.plist", gregorian: 4)).to eq "/usr/libexec/PlistBuddy -c 'Set :\"AppleFirstWeekday\":gregorian 4' \"path/to/file.plist\"" + end + + it "returns the value properly formatted with double quotes when the value has spaces" do + expect(resource.plistbuddy_command(:print, "Foo Bar Baz", "path/to/file.plist")).to eq "/usr/libexec/PlistBuddy -c 'Print :\"Foo Bar Baz\"' \"path/to/file.plist\"" + end + + it "returns the value properly formatted with double quotes when The value to be added contains spaces" do + expect(resource.plistbuddy_command(:add, "Foo Bar Baz", "path/to/file.plist", true)).to eq "/usr/libexec/PlistBuddy -c 'Add :\"Foo Bar Baz\" bool' \"path/to/file.plist\"" + end + + it "returns the value properly formatted with double quotes when the plist itself contains spaces" do + expect(resource.plistbuddy_command(:print, "Foo Bar Baz", "Library/Preferences/com.parallels.Parallels Desktop.plist")).to eq "/usr/libexec/PlistBuddy -c 'Print :\"Foo Bar Baz\"' \"Library/Preferences/com.parallels.Parallels Desktop.plist\"" + end + end + + describe "#convert_to_data_type_from_string" do + it "returns true if entry is 1 and the type is boolean" do + expect(resource.convert_to_data_type_from_string("boolean", "1")).to eq true + end + + it "returns false if entry is 0 and the type is boolean" do + expect(resource.convert_to_data_type_from_string("boolean", "0")).to eq false + end + + it "returns the value as an integer when the type is integer and the value is 1" do + expect(resource.convert_to_data_type_from_string("integer", "1")).to eq 1 + end + + it "returns the value as an integer when the type is integer and the value is 0" do + expect(resource.convert_to_data_type_from_string("integer", "0")).to eq 0 + end + + it "returns the correct value as an integer when the type is integer and the value is 950224" do + expect(resource.convert_to_data_type_from_string("integer", "950224")).to eq 950224 + end + + it "returns the correct value still as a string when the type is string and the value is also a string" do + expect(resource.convert_to_data_type_from_string("string", "corge")).to eq "corge" + end + + it "returns the correct value as a float when the type is float and the value is 3.14159265359" do + expect(resource.convert_to_data_type_from_string("float", "3.14159265359")).to eq 3.14159265359 + end + + it "returns an empty string when the type nor the value is given" do + expect(resource.convert_to_data_type_from_string(nil, "")).to eq "" + end + end + + describe "#type_to_commandline_string" do + it "returns the required boolean entry type as a string" do + expect(resource.type_to_commandline_string(true)).to eq "bool" + end + + it "returns the required array entry type as a string" do + expect(resource.type_to_commandline_string(%w{foo bar})).to eq "array" + end + + it "returns the required dictionary entry type as a string" do + expect(resource.type_to_commandline_string("baz" => "qux")).to eq "dict" + end + + it "returns the required string entry type as a string" do + expect(resource.type_to_commandline_string("quux")).to eq "string" + end + + it "returns the required integer entry type as a string" do + expect(resource.type_to_commandline_string(1)).to eq "integer" + end + + it "returns the required float entry type as a string" do + expect(resource.type_to_commandline_string(1.0)).to eq "float" + end + end +end |