summaryrefslogtreecommitdiff
path: root/spec/functional/resource
diff options
context:
space:
mode:
authorDaniel Schauenberg <d@unwiredcouch.com>2021-01-07 09:26:12 +0100
committerDaniel Schauenberg <d@unwiredcouch.com>2021-01-07 09:27:09 +0100
commite50d6e907d6bebec70271e9bcca50459dad88548 (patch)
tree57bef79bd565b536404761031eea39ebc90babdd /spec/functional/resource
parentd98533a4d677cfc5bf1a9a77ff9cdcc4e2543862 (diff)
parentf2a9569d23af039bd69d2dd3adb1251f7da044e3 (diff)
downloadchef-e50d6e907d6bebec70271e9bcca50459dad88548.tar.gz
Merge remote-tracking branch 'origin/master' into mrtazz/pkgng-exit-code-fix
Signed-off-by: Daniel Schauenberg <d@unwiredcouch.com>
Diffstat (limited to 'spec/functional/resource')
-rwxr-xr-xspec/functional/resource/aix_service_spec.rb19
-rwxr-xr-xspec/functional/resource/aixinit_service_spec.rb25
-rw-r--r--spec/functional/resource/apt_package_spec.rb (renamed from spec/functional/resource/package_spec.rb)53
-rw-r--r--spec/functional/resource/base.rb28
-rw-r--r--spec/functional/resource/bash_spec.rb63
-rw-r--r--spec/functional/resource/batch_spec.rb8
-rw-r--r--spec/functional/resource/bff_spec.rb14
-rw-r--r--spec/functional/resource/chocolatey_package_spec.rb70
-rw-r--r--spec/functional/resource/cookbook_file_spec.rb10
-rw-r--r--spec/functional/resource/cron_spec.rb72
-rw-r--r--spec/functional/resource/deploy_revision_spec.rb881
-rw-r--r--spec/functional/resource/directory_spec.rb2
-rw-r--r--spec/functional/resource/dnf_package_spec.rb1277
-rw-r--r--spec/functional/resource/dpkg_package_spec.rb10
-rw-r--r--spec/functional/resource/dsc_resource_spec.rb13
-rw-r--r--spec/functional/resource/dsc_script_spec.rb247
-rwxr-xr-xspec/functional/resource/env_spec.rb192
-rw-r--r--spec/functional/resource/execute_spec.rb33
-rw-r--r--spec/functional/resource/file_spec.rb4
-rw-r--r--spec/functional/resource/git_spec.rb344
-rw-r--r--spec/functional/resource/group_spec.rb151
-rw-r--r--spec/functional/resource/ifconfig_spec.rb33
-rw-r--r--spec/functional/resource/insserv_spec.rb206
-rw-r--r--spec/functional/resource/launchd_spec.rb232
-rw-r--r--spec/functional/resource/link_spec.rb132
-rw-r--r--spec/functional/resource/locale_spec.rb108
-rw-r--r--spec/functional/resource/mount_spec.rb44
-rw-r--r--spec/functional/resource/msu_package_spec.rb107
-rw-r--r--spec/functional/resource/ohai_spec.rb2
-rw-r--r--spec/functional/resource/powershell_package_source_spec.rb107
-rw-r--r--spec/functional/resource/powershell_script_spec.rb207
-rw-r--r--spec/functional/resource/reboot_spec.rb8
-rw-r--r--spec/functional/resource/registry_spec.rb276
-rw-r--r--spec/functional/resource/remote_directory_spec.rb2
-rw-r--r--spec/functional/resource/remote_file_spec.rb215
-rw-r--r--spec/functional/resource/rpm_spec.rb27
-rw-r--r--spec/functional/resource/template_spec.rb43
-rw-r--r--spec/functional/resource/timezone_spec.rb41
-rw-r--r--spec/functional/resource/user/dscl_spec.rb24
-rw-r--r--spec/functional/resource/user/mac_user_spec.rb207
-rw-r--r--spec/functional/resource/user/useradd_spec.rb698
-rw-r--r--spec/functional/resource/user/windows_spec.rb150
-rw-r--r--spec/functional/resource/windows_certificate_spec.rb316
-rw-r--r--spec/functional/resource/windows_env_spec.rb285
-rw-r--r--spec/functional/resource/windows_firewall_rule_spec.rb93
-rw-r--r--spec/functional/resource/windows_font_spec.rb49
-rw-r--r--spec/functional/resource/windows_package_spec.rb49
-rw-r--r--spec/functional/resource/windows_path_spec.rb68
-rw-r--r--spec/functional/resource/windows_security_policy_spec.rb86
-rw-r--r--spec/functional/resource/windows_service_spec.rb15
-rw-r--r--spec/functional/resource/windows_share_spec.rb103
-rw-r--r--spec/functional/resource/windows_task_spec.rb1969
-rw-r--r--spec/functional/resource/windows_user_privilege_spec.rb192
-rw-r--r--spec/functional/resource/yum_package_spec.rb981
-rw-r--r--spec/functional/resource/zypper_package_spec.rb247
55 files changed, 8160 insertions, 2678 deletions
diff --git a/spec/functional/resource/aix_service_spec.rb b/spec/functional/resource/aix_service_spec.rb
index 5fff3e00d7..16d830b88a 100755
--- a/spec/functional/resource/aix_service_spec.rb
+++ b/spec/functional/resource/aix_service_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, 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");
@@ -18,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
shared_examples "src service" do
@@ -77,12 +75,21 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
include Chef::Mixin::ShellOut
def get_user_id
- shell_out("id -u #{ENV['USER']}").stdout.chomp
+ shell_out("id -u #{ENV["USER"]}").stdout.chomp
+ end
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
end
describe "When service is a subsystem" do
before(:all) do
- script_dir = File.join(File.dirname(__FILE__), "/../assets/")
+ script_dir = File.join(__dir__, "/../assets/")
shell_out!("mkssys -s ctestsys -p #{script_dir}/testchefsubsys -u #{get_user_id} -S -n 15 -f 9 -R -Q")
end
@@ -110,7 +117,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
# Cannot run this test on a WPAR
describe "When service is a group", :not_wpar do
before(:all) do
- script_dir = File.join(File.dirname(__FILE__), "/../assets/")
+ script_dir = File.join(__dir__, "/../assets/")
shell_out!("mkssys -s ctestsys -p #{script_dir}/testchefsubsys -u #{get_user_id} -S -n 15 -f 9 -R -Q -G ctestgrp")
end
diff --git a/spec/functional/resource/aixinit_service_spec.rb b/spec/functional/resource/aixinit_service_spec.rb
index bf50046b03..c568d40a8d 100755
--- a/spec/functional/resource/aixinit_service_spec.rb
+++ b/spec/functional/resource/aixinit_service_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2014-2016, 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");
@@ -18,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
require "fileutils"
@@ -29,18 +27,18 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
# Platform specific validation routines.
def service_should_be_started(file_name)
# The existence of this file indicates that the service was started.
- expect(File.exists?("#{Dir.tmpdir}/#{file_name}")).to be_truthy
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_truthy
end
def service_should_be_stopped(file_name)
- expect(File.exists?("#{Dir.tmpdir}/#{file_name}")).to be_falsey
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_falsey
end
def valide_symlinks(expected_output, run_level = nil, status = nil, priority = nil)
directory = []
if priority.is_a? Hash
priority.each do |level, o|
- directory << "/etc/rc.d/rc#{level}.d/#{(o[0] == :start ? 'S' : 'K')}#{o[1]}#{new_resource.service_name}"
+ directory << "/etc/rc.d/rc#{level}.d/#{(o[0] == :start ? "S" : "K")}#{o[1]}#{new_resource.service_name}"
end
directory
else
@@ -57,9 +55,10 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
# Actual tests
let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
new_resource = Chef::Resource::Service.new("chefinittest", run_context)
new_resource.provider Chef::Provider::Service::AixInit
- new_resource.supports({ :status => true, :restart => true, :reload => true })
+ new_resource.supports({ status: true, restart: true, reload: true })
new_resource
end
@@ -69,12 +68,12 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
before(:all) do
- File.delete("/etc/rc.d/init.d/chefinittest") if File.exists?("/etc/rc.d/init.d/chefinittest")
- FileUtils.cp("#{File.join(File.dirname(__FILE__), "/../assets/chefinittest")}", "/etc/rc.d/init.d/chefinittest")
+ File.delete("/etc/rc.d/init.d/chefinittest") if File.exist?("/etc/rc.d/init.d/chefinittest")
+ FileUtils.cp((File.join(__dir__, "/../assets/chefinittest")).to_s, "/etc/rc.d/init.d/chefinittest")
end
after(:all) do
- File.delete("/etc/rc.d/init.d/chefinittest") if File.exists?("/etc/rc.d/init.d/chefinittest")
+ File.delete("/etc/rc.d/init.d/chefinittest") if File.exist?("/etc/rc.d/init.d/chefinittest")
end
before(:each) do
@@ -166,7 +165,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
after do
- File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest")
+ File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exist?("/etc/rc.d/rc2.d/chefinittest")
end
it "creates symlink with status K" do
@@ -182,7 +181,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
after do
- File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest")
+ File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exist?("/etc/rc.d/rc2.d/chefinittest")
end
it "creates a symlink with status K and a priority" do
@@ -199,7 +198,7 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
after do
- File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest")
+ File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exist?("/etc/rc.d/rc2.d/chefinittest")
end
it "create symlink with status stop (K) and a priority " do
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/apt_package_spec.rb
index 0f01a751ec..9f10e27731 100644
--- a/spec/functional/resource/package_spec.rb
+++ b/spec/functional/resource/apt_package_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -23,7 +22,7 @@ require "webrick"
module AptServer
def enable_testing_apt_source
File.open("/etc/apt/sources.list.d/chef-integration-test.list", "w+") do |f|
- f.puts "deb http://localhost:9000/ sid main"
+ f.puts "deb [trusted=yes] http://localhost:9000/ sid main"
end
# Magic to update apt cache for only our repo
shell_out!("apt-get update " +
@@ -40,9 +39,7 @@ module AptServer
def tcp_test_port(hostname, port)
tcp_socket = TCPSocket.new(hostname, port)
true
- rescue Errno::ETIMEDOUT
- false
- rescue Errno::ECONNREFUSED
+ rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED
false
ensure
tcp_socket && tcp_socket.close
@@ -50,11 +47,11 @@ module AptServer
def apt_server
@apt_server ||= WEBrick::HTTPServer.new(
- :Port => 9000,
- :DocumentRoot => apt_data_dir + "/var/www/apt",
+ Port: 9000,
+ DocumentRoot: apt_data_dir + "/var/www/apt",
# Make WEBrick quiet, comment out for debug.
- :Logger => Logger.new(StringIO.new),
- :AccessLog => [ StringIO.new, WEBrick::AccessLog::COMMON_LOG_FORMAT ]
+ Logger: Logger.new(StringIO.new),
+ AccessLog: [ StringIO.new, WEBrick::AccessLog::COMMON_LOG_FORMAT ]
)
end
@@ -86,13 +83,13 @@ module AptServer
end
end
-metadata = { :unix_only => true,
- :requires_root => true,
- :provider => { :package => Chef::Provider::Package::Apt },
- :arch => "x86_64" # test packages are 64bit
+metadata = { unix_only: true,
+ requires_root: true,
+ provider: { package: Chef::Provider::Package::Apt },
+ arch: "x86_64", # test packages are 64bit
}
-describe Chef::Resource::Package, metadata do
+describe Chef::Resource::AptPackage, metadata do
include Chef::Mixin::ShellOut
context "with a remote package source" do
@@ -143,7 +140,7 @@ describe Chef::Resource::Package, metadata do
end
def base_resource
- r = Chef::Resource::Package.new("chef-integration-test", run_context)
+ r = Chef::Resource::AptPackage.new("chef-integration-test", run_context)
# The apt repository in the spec data is not gpg signed, so we need to
# force apt to accept the package:
r.options("--force-yes")
@@ -169,13 +166,13 @@ describe Chef::Resource::Package, metadata do
it "does nothing for action :remove" do
package_resource.run_action(:remove)
- shell_out!("dpkg -l chef-integration-test", :returns => [1])
+ shell_out!("dpkg -l chef-integration-test", returns: [1])
expect(package_resource).not_to be_updated_by_last_action
end
it "does nothing for action :purge" do
package_resource.run_action(:purge)
- shell_out!("dpkg -l chef-integration-test", :returns => [1])
+ shell_out!("dpkg -l chef-integration-test", returns: [1])
expect(package_resource).not_to be_updated_by_last_action
end
@@ -275,7 +272,7 @@ describe Chef::Resource::Package, metadata do
r = base_resource
r.cookbook_name = "preseed"
r.response_file("preseed-template-variables.seed")
- r.response_file_variables({ :template_variable => "SUPPORTS VARIABLES" })
+ r.response_file_variables({ template_variable: "SUPPORTS VARIABLES" })
r
end
@@ -300,13 +297,13 @@ describe Chef::Resource::Package, metadata do
it "does nothing for action :install" do
package_resource.run_action(:install)
- shell_out!("dpkg -l chef-integration-test", :returns => [0])
+ shell_out!("dpkg -l chef-integration-test", returns: [0])
expect(package_resource).not_to be_updated_by_last_action
end
it "does nothing for action :upgrade" do
package_resource.run_action(:upgrade)
- shell_out!("dpkg -l chef-integration-test", :returns => [0])
+ shell_out!("dpkg -l chef-integration-test", returns: [0])
expect(package_resource).not_to be_updated_by_last_action
end
@@ -324,10 +321,10 @@ describe Chef::Resource::Package, metadata do
# un chef-integration-test <none> (no description available)
def pkg_should_be_removed
# will raise if exit code != 0,1
- pkg_check = shell_out!("dpkg -l chef-integration-test", :returns => [0, 1])
+ pkg_check = shell_out!("dpkg -l chef-integration-test", returns: [0, 1])
if pkg_check.exitstatus == 0
- expect(pkg_check.stdout).to match(/un[\s]+chef-integration-test/)
+ expect(pkg_check.stdout).to match(/un\s+chef-integration-test/)
end
end
@@ -353,14 +350,14 @@ describe Chef::Resource::Package, metadata do
it "does nothing for action :install" do
package_resource.run_action(:install)
- shell_out!("dpkg -l chef-integration-test", :returns => [0])
+ shell_out!("dpkg -l chef-integration-test", returns: [0])
expect(package_resource).not_to be_updated_by_last_action
end
it "upgrades the package for action :upgrade" do
package_resource.run_action(:upgrade)
- dpkg_l = shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(dpkg_l.stdout).to match(/chef\-integration\-test[\s]+1\.1\-1/)
+ dpkg_l = shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(dpkg_l.stdout).to match(/chef\-integration\-test\s+1\.1\-1/)
expect(package_resource).to be_updated_by_last_action
end
@@ -373,8 +370,8 @@ describe Chef::Resource::Package, metadata do
it "upgrades the package for action :install" do
package_resource.run_action(:install)
- dpkg_l = shell_out!("dpkg -l chef-integration-test", :returns => [0])
- expect(dpkg_l.stdout).to match(/chef\-integration\-test[\s]+1\.1\-1/)
+ dpkg_l = shell_out!("dpkg -l chef-integration-test", returns: [0])
+ expect(dpkg_l.stdout).to match(/chef\-integration\-test\s+1\.1\-1/)
expect(package_resource).to be_updated_by_last_action
end
end
diff --git a/spec/functional/resource/base.rb b/spec/functional/resource/base.rb
deleted file mode 100644
index 38175e81c0..0000000000
--- a/spec/functional/resource/base.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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.
-#
-
-def run_context
- @run_context ||= begin
- node = Chef::Node.new
- node.default[:platform] = ohai[:platform]
- node.default[:platform_version] = ohai[:platform_version]
- node.default[:os] = ohai[:os]
- events = Chef::EventDispatch::Dispatcher.new
- Chef::RunContext.new(node, {}, events)
- end
-end
diff --git a/spec/functional/resource/bash_spec.rb b/spec/functional/resource/bash_spec.rb
index a2e174d557..abb88f499f 100644
--- a/spec/functional/resource/bash_spec.rb
+++ b/spec/functional/resource/bash_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, 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");
@@ -17,65 +17,32 @@
#
require "spec_helper"
-require "functional/resource/base"
describe Chef::Resource::Bash, :unix_only do
let(:code) { "echo hello" }
let(:resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+
resource = Chef::Resource::Bash.new("foo_resource", run_context)
- resource.code(code)
+ resource.code(code) unless code.nil?
resource
end
- describe "when setting the command attribute" do
- let (:command) { "wizard racket" }
-
- # in Chef-12 the `command` attribute is largely useless, but does set the identity attribute
- # so that notifications need to target the value of the command. it will not run the `command`
- # and if it is given without a code block then it does nothing and always succeeds.
- describe "in Chef-12", chef: "< 13" do
- it "gets the commmand attribute from the name" do
- expect(resource.command).to eql("foo_resource")
- end
-
- it "sets the resource identity to the command name" do
- resource.command command
- expect(resource.identity).to eql(command)
- end
-
- it "warns when the code is not present and a useless `command` is present" do
- expect(Chef::Log).to receive(:warn).with(/coding error/)
- expect(Chef::Log).to receive(:warn).with(/deprecated/)
- resource.code nil
- resource.command command
- expect { resource.run_action(:run) }.not_to raise_error
- end
+ describe "when setting the command property" do
+ let(:command) { "wizard racket" }
- describe "when the code is not present" do
- let(:code) { nil }
- it "warns" do
- expect(Chef::Log).to receive(:warn)
- expect { resource.run_action(:run) }.not_to raise_error
- end
- end
+ it "should raise an exception when trying to set the command" do
+ expect { resource.command command }.to raise_error(Chef::Exceptions::Script)
end
- # in Chef-13 the `command` attribute needs to be for internal use only
- describe "in Chef-13", chef: ">= 13" do
- it "should raise an exception when trying to set the command" do
- expect { resource.command command }.to raise_error # FIXME: add a real error in Chef-13
- end
-
- it "should initialize the command to nil" do
- expect(resource.command).to be_nil
- end
+ it "should initialize the command to nil" do
+ expect(resource.command).to be_nil
+ end
- describe "when the code is not present" do
- let(:code) { nil }
- it "raises an exception" do
- expect { resource.run_action(:run) }.to raise_error # FIXME: add a real error in Chef-13
- expect { resource.run_action(:run) }.not_to raise_error
- end
+ describe "when the code is not present" do
+ let(:code) { nil }
+ it "raises an exception" do
+ expect { resource.run_action(:run) }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
end
diff --git a/spec/functional/resource/batch_spec.rb b/spec/functional/resource/batch_spec.rb
index e4fc6420c7..9ec1385175 100644
--- a/spec/functional/resource/batch_spec.rb
+++ b/spec/functional/resource/batch_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -23,7 +23,11 @@ describe Chef::Resource::WindowsScript::Batch, :windows_only do
let(:output_command) { " > " }
- let (:architecture_command) { "@echo %PROCESSOR_ARCHITECTURE%" }
+ let(:architecture_command) { "@echo %PROCESSOR_ARCHITECTURE%" }
+
+ let(:resource) do
+ Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", @run_context)
+ end
it_behaves_like "a Windows script running on Windows"
diff --git a/spec/functional/resource/bff_spec.rb b/spec/functional/resource/bff_spec.rb
index e7f7540e5a..cdcc086180 100644
--- a/spec/functional/resource/bff_spec.rb
+++ b/spec/functional/resource/bff_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require "functional/resource/base"
require "chef/mixin/shell_out"
# Run the test only for AIX platform.
-describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform] != "aix" do
+describe Chef::Resource::BffPackage, :requires_root, external: ohai[:platform] != "aix" do
include Chef::Mixin::ShellOut
let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
new_resource = Chef::Resource::BffPackage.new(@pkg_name, run_context)
new_resource.source @pkg_path
new_resource
@@ -31,12 +31,12 @@ describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform
def bff_pkg_should_be_installed(resource)
expect(shell_out("lslpp -L #{resource.name}").exitstatus).to eq(0)
- ::File.exists?("/usr/PkgA/bin/acommand")
+ ::File.exist?("/usr/PkgA/bin/acommand")
end
def bff_pkg_should_be_removed(resource)
expect(shell_out("lslpp -L #{resource.name}").exitstatus).to eq(1)
- !::File.exists?("/usr/PkgA/bin/acommand")
+ !::File.exist?("/usr/PkgA/bin/acommand")
end
before(:all) do
@@ -62,7 +62,7 @@ describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform
context "package install action with options" do
it "should install a package" do
- new_resource.options("-e/tmp/installp.log")
+ new_resource.options("-e#{Dir.tmpdir}/installp.log")
new_resource.run_action(:install)
bff_pkg_should_be_installed(new_resource)
end
@@ -108,7 +108,7 @@ describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform
end
it "should remove an installed package" do
- new_resource.options("-e/tmp/installp.log")
+ new_resource.options("-e#{Dir.tmpdir}/installp.log")
new_resource.run_action(:remove)
bff_pkg_should_be_removed(new_resource)
end
diff --git a/spec/functional/resource/chocolatey_package_spec.rb b/spec/functional/resource/chocolatey_package_spec.rb
index 7bb6698daf..e55c1a453c 100644
--- a/spec/functional/resource/chocolatey_package_spec.rb
+++ b/spec/functional/resource/chocolatey_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright (c) 2016 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");
@@ -16,22 +16,19 @@
# limitations under the License.
#
require "spec_helper"
-require "chef/mixin/powershell_out"
+require "chef/mixin/shell_out"
-describe Chef::Resource::ChocolateyPackage, :windows_only do
- include Chef::Mixin::PowershellOut
-
- before(:all) do
- powershell_out!("iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))")
- unless ENV["PATH"] =~ /chocolatey\\bin/
- ENV["PATH"] = "C:\\ProgramData\\chocolatey\\bin;#{ENV["PATH"]}"
- end
- end
+describe Chef::Resource::ChocolateyPackage, :windows_only, :choco_installed do
+ include Chef::Mixin::ShellOut
let(:package_name) { "test-A" }
- let(:package_list) { proc { powershell_out!("choco list -lo -r #{Array(package_name).join(' ')}").stdout.chomp } }
+ let(:package_list) { proc { shell_out!("choco list -lo -r #{Array(package_name).join(" ")}").stdout.chomp } }
let(:package_source) { File.join(CHEF_SPEC_ASSETS, "chocolatey_feed") }
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
subject do
new_resource = Chef::Resource::ChocolateyPackage.new("test choco package", run_context)
new_resource.package_name package_name
@@ -39,6 +36,11 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
new_resource
end
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
context "installing a package" do
after { remove_package }
@@ -70,7 +72,7 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
end
context "installing multiple packages" do
- let(:package_name) { [ "test-A", "test-B" ] }
+ let(:package_name) { %w{test-A test-B} }
it "installs both packages" do
subject.run_action(:install)
@@ -82,6 +84,48 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do
subject.package_name "blah"
expect { subject.run_action(:install) }.to raise_error Chef::Exceptions::Package
end
+
+ it "installs with an option as a string" do
+ subject.options "--force --confirm"
+ subject.run_action(:install)
+ expect(package_list.call).to eq("#{package_name}|2.0")
+ end
+
+ it "installs with multiple options as a string" do
+ subject.options "--force --confirm"
+ subject.run_action(:install)
+ expect(package_list.call).to eq("#{package_name}|2.0")
+ end
+
+ context "when multiple options passed as string" do
+ before do
+ subject.options "--force --confirm"
+ subject.source nil
+ end
+
+ it "splits a string into an array of options" do
+ expect(provider.send(:cmd_args)).to eq(["--force", "--confirm"])
+ end
+
+ it "calls command_line_to_argv_w_helper method" do
+ expect(provider).to receive(:command_line_to_argv_w_helper).with(subject.options).and_return(["--force", "--confirm"])
+ provider.send(:cmd_args)
+ end
+ end
+
+ context "when multiple options passed as array" do
+ it "Does not call command_line_to_argv_w_helper method" do
+ subject.options [ "--force", "--confirm" ]
+ expect(provider).not_to receive(:command_line_to_argv_w_helper)
+ provider.send(:cmd_args)
+ end
+ end
+
+ it "installs with multiple options as an array" do
+ subject.options [ "--force", "--confirm" ]
+ subject.run_action(:install)
+ expect(package_list.call).to eq("#{package_name}|2.0")
+ end
end
context "upgrading a package" do
diff --git a/spec/functional/resource/cookbook_file_spec.rb b/spec/functional/resource/cookbook_file_spec.rb
index d127413c73..8dbf22d611 100644
--- a/spec/functional/resource/cookbook_file_spec.rb
+++ b/spec/functional/resource/cookbook_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Tim Hinderliter (<tim@chef.io>)
-# Copyright:: Copyright 2012-2016, 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");
@@ -25,9 +25,7 @@ describe Chef::Resource::CookbookFile do
let(:source) { "java.response" }
let(:cookbook_name) { "java" }
let(:expected_content) do
- content = File.open(File.join(CHEF_SPEC_DATA, "cookbooks", "java", "files", "default", "java.response"), "rb") do |f|
- f.read
- end
+ content = File.open(File.join(CHEF_SPEC_DATA, "cookbooks", "java", "files", "default", "java.response"), "rb", &:read)
content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
content
end
@@ -70,11 +68,11 @@ describe Chef::Resource::CookbookFile do
let(:path) { File.join(windows_non_temp_dir, make_tmpname(file_base)) }
before do
- FileUtils.mkdir_p(windows_non_temp_dir) if Chef::Platform.windows?
+ FileUtils.mkdir_p(windows_non_temp_dir) if ChefUtils.windows?
end
after do
- FileUtils.rm_r(windows_non_temp_dir) if Chef::Platform.windows? && File.exists?(windows_non_temp_dir)
+ FileUtils.rm_r(windows_non_temp_dir) if ChefUtils.windows? && File.exist?(windows_non_temp_dir)
end
end
diff --git a/spec/functional/resource/cron_spec.rb b/spec/functional/resource/cron_spec.rb
index f5948191c5..fa53eb08a1 100644
--- a/spec/functional/resource/cron_spec.rb
+++ b/spec/functional/resource/cron_spec.rb
@@ -1,7 +1,6 @@
-# encoding: UTF-8
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -18,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
describe Chef::Resource::Cron, :requires_root, :unix_only do
@@ -28,7 +26,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
# Platform specific validation routines.
def cron_should_exists(cron_name, command)
case ohai[:platform]
- when "aix", "solaris", "opensolaris", "solaris2", "omnios"
+ when "aix", "solaris2", "omnios"
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(0)
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").stdout.lines.to_a.size).to eq(1)
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{command}\"").exitstatus).to eq(0)
@@ -43,7 +41,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
def cron_should_not_exists(cron_name)
case ohai[:platform]
- when "aix", "solaris", "opensolaris", "solaris2", "omnios"
+ when "aix", "solaris2", "omnios"
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(1)
expect(shell_out("crontab -l #{new_resource.user} | grep \"#{new_resource.command}\"").stdout.lines.to_a.size).to eq(0)
else
@@ -53,19 +51,20 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
end
# Actual tests
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
let(:new_resource) do
new_resource = Chef::Resource::Cron.new("Chef functional test cron", run_context)
- new_resource.user "root"
- # @hourly is not supported on solaris, aix
- if ohai[:platform] == "solaris" || ohai[:platform] == "solaris2" || ohai[:platform] == "aix"
- new_resource.minute "0 * * * *"
- else
- new_resource.minute "@hourly"
- end
- new_resource.hour ""
- new_resource.day ""
- new_resource.month ""
- new_resource.weekday ""
+ new_resource.user "root"
+ new_resource.minute "0"
new_resource.command "/bin/true"
new_resource
end
@@ -89,6 +88,16 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
5.times { new_resource.run_action(:create) }
cron_should_exists(new_resource.name, new_resource.command)
end
+
+ # Test cron for day of week
+ weekdays = { Mon: 1, tuesday: 2, '3': 3, 'thursday': 4, 'Fri': 5, 6 => 6 }
+ weekdays.each do |key, value|
+ it "should create crontab entry and set #{value} for #{key} as weekday" do
+ new_resource.weekday key
+ expect { new_resource.run_action(:create) }.not_to raise_error
+ cron_should_exists(new_resource.name, new_resource.command)
+ end
+ end
end
describe "delete action" do
@@ -104,9 +113,9 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
end
end
- exclude_solaris = %w{solaris opensolaris solaris2 omnios}.include?(ohai[:platform])
- describe "create action with various attributes", :external => exclude_solaris do
- def create_and_validate_with_attribute(resource, attribute, value)
+ exclude_solaris = %w{solaris solaris2 omnios}.include?(ohai[:platform])
+ describe "create action with various attributes", external: exclude_solaris do
+ def create_and_validate_with_property(resource, attribute, value)
if ohai[:platform] == "aix"
expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron, /Aix cron entry does not support environment variables. Please set them in script and use script in cron./)
else
@@ -118,6 +127,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
def cron_attribute_should_exists(cron_name, attribute, value)
return if %w{aix solaris}.include?(ohai[:platform])
+
# Test if the attribute exists on newly created cron
cron_should_exists(cron_name, "")
expect(shell_out("crontab -l -u #{new_resource.user} | grep '#{attribute.upcase}=\"#{value}\"'").exitstatus).to eq(0)
@@ -129,28 +139,28 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
it "should create a crontab entry for mailto attribute" do
new_resource.mailto "cheftest@example.com"
- create_and_validate_with_attribute(new_resource, "mailto", "cheftest@example.com")
+ create_and_validate_with_property(new_resource, "mailto", "cheftest@example.com")
end
it "should create a crontab entry for path attribute" do
new_resource.path "/usr/local/bin"
- create_and_validate_with_attribute(new_resource, "path", "/usr/local/bin")
+ create_and_validate_with_property(new_resource, "path", "/usr/local/bin")
end
it "should create a crontab entry for shell attribute" do
new_resource.shell "/bin/bash"
- create_and_validate_with_attribute(new_resource, "shell", "/bin/bash")
+ create_and_validate_with_property(new_resource, "shell", "/bin/bash")
end
it "should create a crontab entry for home attribute" do
new_resource.home "/home/opscode"
- create_and_validate_with_attribute(new_resource, "home", "/home/opscode")
+ create_and_validate_with_property(new_resource, "home", "/home/opscode")
end
%i{ home mailto path shell }.each do |attr|
it "supports an empty string for #{attr} attribute" do
new_resource.send(attr, "")
- create_and_validate_with_attribute(new_resource, attr.to_s, "")
+ create_and_validate_with_property(new_resource, attr.to_s, "")
end
end
end
@@ -160,20 +170,10 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
new_resource.run_action(:delete)
end
- def cron_create_should_raise_exception
- expect { new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/)
- cron_should_not_exists(new_resource.name)
- end
-
- it "should not create cron with invalid minute" do
- new_resource.minute "invalid"
- cron_create_should_raise_exception
- end
-
it "should not create cron with invalid user" do
new_resource.user "1-really-really-invalid-user-name"
- cron_create_should_raise_exception
+ expect { new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron)
+ cron_should_not_exists(new_resource.name)
end
-
end
end
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
deleted file mode 100644
index 572609d8ff..0000000000
--- a/spec/functional/resource/deploy_revision_spec.rb
+++ /dev/null
@@ -1,881 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2012-2016, 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 "tmpdir"
-
-# Deploy relies heavily on symlinks, so it doesn't work on windows.
-describe Chef::Resource::DeployRevision, :unix_only => true, :requires_git => true do
-
- let(:file_cache_path) { Dir.mktmpdir }
- let(:deploy_directory) { Dir.mktmpdir }
-
- # By making restart or other operations write to this file, we can externally
- # track the order in which those operations happened.
- let(:observe_order_file) { Tempfile.new("deploy-resource-observe-operations") }
-
- before do
- Chef::Log.level = :info
- @old_file_cache_path = Chef::Config[:file_cache_path]
- Chef::Config[:file_cache_path] = file_cache_path
- end
-
- after do
- Chef::Config[:file_cache_path] = @old_file_cache_path
- FileUtils.remove_entry_secure deploy_directory if File.exist?(deploy_directory)
- FileUtils.remove_entry_secure file_cache_path
- observe_order_file.close
- FileUtils.remove_entry_secure observe_order_file.path
- end
-
- before(:all) do
- @ohai = Ohai::System.new
- @ohai.all_plugins(%w{platform os})
- end
-
- let(:node) do
- Chef::Node.new.tap do |n|
- n.name "rspec-test"
- n.consume_external_attrs(@ohai.data, {})
- end
- end
-
- let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
-
- # These tests use git's bundle feature, which is a way to export an entire
- # git repo (or subset of commits) as a single file.
- #
- # Generally you can treat a git bundle as a regular git remote.
- #
- # See also: http://git-scm.com/2010/03/10/bundles.html
- let(:git_bundle_repo) { File.expand_path("git_bundles/sinatra-test-app.gitbundle", CHEF_SPEC_DATA) }
-
- let(:git_bundle_with_in_repo_callbacks) { File.expand_path("git_bundles/sinatra-test-app-with-callback-files.gitbundle", CHEF_SPEC_DATA) }
-
- let(:git_bundle_with_in_repo_symlinks) { File.expand_path("git_bundles/sinatra-test-app-with-symlinks.gitbundle", CHEF_SPEC_DATA) }
-
- # This is the fourth version
- let(:latest_rev) { "3eb5ca6c353c83d9179dd3b29347539829b401f3" }
-
- # This is the third version
- let(:previous_rev) { "6d19a6dbecc8e37f5b2277345885c0c783eb8fb1" }
-
- # This is the second version
- let(:second_rev) { "0827e1b0e5043608ac0a824da5c558e252154ad0" }
-
- # This is the sixth version, it is on the "with-deploy-scripts" branch
- let(:rev_with_in_repo_callbacks) { "2404d015882659754bdb93ad6e4b4d3d02691a82" }
-
- # This is the fifth version in the "with-symlinks" branch
- let(:rev_with_in_repo_symlinks) { "5a4748c52aaea8250b4346a9b8ede95ee3755e28" }
-
- # Read values from the +observe_order_file+ and split each line. This way you
- # can see in which order things really happened.
- def actual_operations_order
- IO.read(observe_order_file.path).split("\n").map(&:strip)
- end
-
- # 1. touch `restart.txt` in cwd so we know that the command is run with the
- # right cwd.
- # 2. Append +tag+ to the `observe_order_file` so we can check the order in
- # which operations happen later in the test.
- def shell_restart_command(tag)
- "touch restart.txt && echo '#{tag}' >> #{observe_order_file.path}"
- end
-
- let(:basic_deploy_resource) do
- Chef::Resource::DeployRevision.new(deploy_directory, run_context).tap do |r|
- r.name "deploy-revision-unit-test"
- r.repo git_bundle_repo
- r.symlink_before_migrate({})
- r.symlinks({})
- end
- end
-
- let(:deploy_to_latest_rev) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(latest_rev)
- r.restart_command shell_restart_command(:deploy_to_latest_rev)
- end
- end
-
- let(:deploy_to_previous_rev) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(previous_rev)
- r.restart_command shell_restart_command(:deploy_to_previous_rev)
- end
- end
-
- let(:deploy_to_latest_rev_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(latest_rev)
- r.restart_command shell_restart_command(:deploy_to_latest_rev_again)
- end
- end
-
- let(:deploy_to_previous_rev_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(previous_rev)
- r.restart_command shell_restart_command(:deploy_to_previous_rev_again)
- end
- end
-
- let(:deploy_to_second_rev) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(second_rev)
- r.restart_command shell_restart_command(:deploy_to_second_rev)
- end
- end
-
- let(:deploy_to_second_rev_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(second_rev)
- r.restart_command shell_restart_command(:deploy_to_second_rev_again)
- end
- end
-
- let(:deploy_to_second_rev_again_again) do
- basic_deploy_resource.dup.tap do |r|
- r.revision(second_rev)
- r.restart_command shell_restart_command(:deploy_to_second_rev_again_again)
- end
- end
-
- # Computes the full path for +path+ relative to the deploy directory
- def rel_path(path)
- File.expand_path(path, deploy_directory)
- end
-
- def actual_current_rev
- Dir.chdir(rel_path("current")) do
- `git rev-parse HEAD`.strip
- end
- end
-
- def self.the_app_is_deployed_at_revision(target_rev_spec)
- it "deploys the app to the target revision (#{target_rev_spec})" do
- target_rev = send(target_rev_spec)
-
- expect(File).to exist(rel_path("current"))
-
- expect(actual_current_rev).to eq(target_rev)
-
- # Is the app code actually there?
- expect(File).to exist(rel_path("current/app/app.rb"))
- end
- end
-
- context "when deploying a simple app" do
- describe "for the first time, with the required directory layout precreated" do
- before do
- FileUtils.mkdir_p(rel_path("releases"))
- FileUtils.mkdir_p(rel_path("shared"))
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application" do
- expect(File).to exist(rel_path("current/restart.txt"))
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev})
- end
-
- it "is marked as updated" do
- expect(deploy_to_latest_rev).to be_updated_by_last_action
- end
- end
-
- describe "back to a previously deployed revision, with the directory structure precreated" do
- before do
- FileUtils.mkdir_p(rel_path("releases"))
- FileUtils.mkdir_p(rel_path("shared"))
-
- deploy_to_latest_rev.run_action(:deploy)
- deploy_to_previous_rev.run_action(:deploy)
- deploy_to_latest_rev_again.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev deploy_to_previous_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the fourth version of the app")
- end
- end
-
- describe "for the first time, with no existing directory layout" do
- before do
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- it "creates the required directory tree" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application" do
- expect(File).to exist(rel_path("current/restart.txt"))
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev})
- end
-
- it "is marked as updated" do
- expect(deploy_to_latest_rev).to be_updated_by_last_action
- end
- end
-
- describe "again to the current revision" do
- before do
- deploy_to_latest_rev.run_action(:deploy)
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "does not restart the app" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev})
- end
-
- it "is not marked updated" do
- expect(deploy_to_latest_rev).not_to be_updated_by_last_action
- end
-
- end
-
- describe "again with force_deploy" do
- before do
- deploy_to_latest_rev.run_action(:force_deploy)
- deploy_to_latest_rev_again.run_action(:force_deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the app" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev).to be_updated_by_last_action
- end
-
- end
-
- describe "again to a new revision" do
- before do
- deploy_to_previous_rev.run_action(:deploy)
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application after the new deploy" do
- expect(actual_operations_order).to eq(%w{deploy_to_previous_rev deploy_to_latest_rev})
- end
-
- it "is marked updated" do
- expect(deploy_to_previous_rev).to be_updated_by_last_action
- end
-
- it "leaves the old copy of the app around for rollback" do
- expect(File).to exist(File.join(deploy_directory, "releases", previous_rev))
- end
-
- end
-
- describe "back to a previously deployed revision (implicit rollback)" do
- before do
- deploy_to_latest_rev.run_action(:deploy)
- deploy_to_previous_rev.run_action(:deploy)
- deploy_to_latest_rev_again.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_latest_rev deploy_to_previous_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the fourth version of the app")
- end
- end
-
- describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do
- before do
- deploy_to_previous_rev.run_action(:deploy)
- @previous_rev_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @latest_rev_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev_again.run_action(:rollback)
- @previous_rev_again_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:previous_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_previous_rev deploy_to_latest_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the third version of the app")
- end
-
- it "all_releases after first deploy should have one entry" do
- expect(@previous_rev_all_releases.length).to eq(1)
- end
-
- it "all_releases after second deploy should have two entries" do
- expect(@latest_rev_all_releases.length).to eq(2)
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@previous_rev_again_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@previous_rev_again_all_releases).to eq(@previous_rev_all_releases)
- end
-
- end
-
- describe "back to a previously deployed revision where resource rev == previous revision (explicit rollback)" do
- before do
- deploy_to_previous_rev.run_action(:deploy)
- @previous_rev_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @latest_rev_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev_again.run_action(:rollback)
- # FIXME: only difference with previous test is using latest_rev_again insetad of previous_rev_again
- @previous_rev_again_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:previous_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_previous_rev deploy_to_latest_rev deploy_to_previous_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_previous_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the third version of the app")
- end
-
- it "all_releases after first deploy should have one entry" do
- expect(@previous_rev_all_releases.length).to eq(1)
- end
-
- it "all_releases after second deploy should have two entries" do
- expect(@latest_rev_all_releases.length).to eq(2)
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@previous_rev_again_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@previous_rev_again_all_releases).to eq(@previous_rev_all_releases)
- end
- end
-
- describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do
- before do
- deploy_to_second_rev.run_action(:deploy)
- @first_deploy_all_releases = deploy_to_second_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev.run_action(:deploy)
- @second_deploy_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev_again.run_action(:rollback)
- @third_deploy_all_releases = deploy_to_previous_rev_again.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @fourth_deploy_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev_again.run_action(:rollback)
- @fifth_deploy_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:second_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_second_rev deploy_to_previous_rev deploy_to_previous_rev_again deploy_to_latest_rev deploy_to_latest_rev_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_latest_rev_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the second version of the app")
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@fifth_deploy_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@fifth_deploy_all_releases).to eq(@first_deploy_all_releases)
- end
- end
-
- describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do
- before do
- deploy_to_second_rev.run_action(:deploy)
- @first_deploy_all_releases = deploy_to_second_rev.provider_for_action(:deploy).all_releases
- deploy_to_previous_rev.run_action(:deploy)
- @second_deploy_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases
- deploy_to_second_rev_again.run_action(:rollback)
- @third_deploy_all_releases = deploy_to_second_rev_again.provider_for_action(:deploy).all_releases
- deploy_to_latest_rev.run_action(:deploy)
- @fourth_deploy_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases
- deploy_to_second_rev_again_again.run_action(:rollback)
- @fifth_deploy_all_releases = deploy_to_second_rev_again_again.provider_for_action(:deploy).all_releases
- end
-
- the_app_is_deployed_at_revision(:second_rev)
-
- it "restarts the application after rolling back" do
- expect(actual_operations_order).to eq(%w{deploy_to_second_rev deploy_to_previous_rev deploy_to_second_rev_again deploy_to_latest_rev deploy_to_second_rev_again_again})
- end
-
- it "is marked updated" do
- expect(deploy_to_second_rev_again_again).to be_updated_by_last_action
- end
-
- it "deploys the right code" do
- expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the second version of the app")
- end
-
- it "all_releases after rollback should have one entry" do
- expect(@fifth_deploy_all_releases.length).to eq(1)
- end
-
- it "all_releases after rollback should be the same as after the first deploy" do
- expect(@fifth_deploy_all_releases).to eq(@first_deploy_all_releases)
- end
-
- end
-
- # CHEF-3435
- describe "to a deploy_to path that does not yet exist" do
-
- let(:top_level_tmpdir) { Dir.mktmpdir }
-
- # override top level deploy_directory let block with one that is two
- # directories deeper
- let(:deploy_directory) { File.expand_path("nested/deeper", top_level_tmpdir) }
-
- after do
- FileUtils.remove_entry_secure top_level_tmpdir
- end
-
- before do
- expect(File).not_to exist(deploy_directory)
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- it "creates the required directory tree" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- end
- end
-
- context "when deploying an app with inline recipe callbacks" do
-
- # Use closures to capture and mutate this variable. This allows us to track
- # ordering of operations.
- callback_order = []
-
- let(:deploy_to_latest_with_inline_recipes) do
- deploy_to_latest_rev.dup.tap do |r|
- r.symlink_before_migrate "config/config.ru" => "config.ru"
- r.before_migrate do
- callback_order << :before_migrate
-
- file "#{release_path}/before_migrate.txt" do
- # The content here isn't relevant, but it gets printed when running
- # the tests. Could be handy for debugging.
- content callback_order.inspect
- end
- end
- r.before_symlink do
- callback_order << :before_symlink
-
- current_release_path = release_path
- ruby_block "ensure before symlink" do
- block do
- if ::File.exist?(::File.join(current_release_path, "/tmp"))
- raise "Ordering issue with provider, expected symlinks to not have been created"
- end
- end
- end
-
- file "#{release_path}/before_symlink.txt" do
- content callback_order.inspect
- end
- end
- r.before_restart do
- callback_order << :before_restart
-
- current_release_path = release_path
- ruby_block "ensure after symlink" do
- block do
- unless ::File.exist?(::File.join(current_release_path, "/tmp"))
- raise "Ordering issue with provider, expected symlinks to have been created"
- end
- end
- end
-
- file "#{release_path}/tmp/before_restart.txt" do
- content callback_order.inspect
- end
- end
- r.after_restart do
- callback_order << :after_restart
- file "#{release_path}/tmp/after_restart.txt" do
- content callback_order.inspect
- end
- end
- end
- end
-
- before do
- callback_order.clear # callback_order variable is global for this context group
- deploy_to_latest_with_inline_recipes.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:latest_rev)
-
- it "is marked updated" do
- expect(deploy_to_latest_with_inline_recipes).to be_updated_by_last_action
- end
-
- it "calls the callbacks in order" do
- expect(callback_order).to eq([:before_migrate, :before_symlink, :before_restart, :after_restart])
- end
-
- it "runs chef resources in the callbacks" do
- expect(File).to exist(rel_path("current/before_migrate.txt"))
- expect(File).to exist(rel_path("current/before_symlink.txt"))
- expect(File).to exist(rel_path("current/tmp/before_restart.txt"))
- expect(File).to exist(rel_path("current/tmp/after_restart.txt"))
- end
- end
-
- context "when deploying an app with in-repo callback scripts" do
- let(:deploy_with_in_repo_callbacks) do
- basic_deploy_resource.dup.tap do |r|
- r.repo git_bundle_with_in_repo_callbacks
- r.revision rev_with_in_repo_callbacks
- end
- end
-
- before do
- deploy_with_in_repo_callbacks.run_action(:deploy)
- end
-
- the_app_is_deployed_at_revision(:rev_with_in_repo_callbacks)
-
- it "runs chef resources in the callbacks" do
- expect(File).to exist(rel_path("current/before_migrate.txt"))
- expect(File).to exist(rel_path("current/before_symlink.txt"))
- expect(File).to exist(rel_path("current/tmp/before_restart.txt"))
- expect(File).to exist(rel_path("current/tmp/after_restart.txt"))
- end
-
- end
-
- context "when deploying an app with migrations" do
- let(:deploy_with_migration) do
- basic_deploy_resource.dup.tap do |r|
-
- # Need this so we can call methods from this test inside the inline
- # recipe callbacks
- spec_context = self
-
- r.revision latest_rev
-
- # enable migrations
- r.migrate true
- # abuse `shell_restart_command` so we can observe order of when the
- # miration command gets run
- r.migration_command shell_restart_command("migration")
- r.before_migrate do
-
- # inline recipe callbacks don't cwd, so you have to get the release
- # directory as a local and "capture" it in the closure.
- current_release = release_path
- execute spec_context.shell_restart_command("before_migrate") do
- cwd current_release
- end
- end
- r.before_symlink do
- current_release = release_path
- execute spec_context.shell_restart_command("before_symlink") do
- cwd current_release
- end
- end
-
- r.before_restart do
- current_release = release_path
- execute spec_context.shell_restart_command("before_restart") do
- cwd current_release
- end
- end
-
- r.after_restart do
- current_release = release_path
- execute spec_context.shell_restart_command("after_restart") do
- cwd current_release
- end
- end
-
- end
- end
-
- before do
- deploy_with_migration.run_action(:deploy)
- end
-
- it "runs migrations in between the before_migrate and before_symlink steps" do
- expect(actual_operations_order).to eq(%w{before_migrate migration before_symlink before_restart after_restart})
- end
- end
-
- context "when deploying an app with in-repo symlinks" do
- let(:deploy_with_in_repo_symlinks) do
- basic_deploy_resource.dup.tap do |r|
- r.repo git_bundle_with_in_repo_symlinks
- r.revision rev_with_in_repo_symlinks
- end
- end
-
- it "should not raise an exception calling File.utime on symlinks" do
- expect { deploy_with_in_repo_symlinks.run_action(:deploy) }.not_to raise_error
- end
- end
-
- context "when a previously deployed application has been nuked" do
-
- shared_examples_for "a redeployed application" do
-
- it "should redeploy the application" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
- end
- end
-
- # background: If a deployment is hosed and the user decides to rm -rf the
- # deployment dir, deploy resource should detect that and nullify its cache.
-
- context "by removing the entire deploy directory" do
-
- before do
- deploy_to_latest_rev.dup.run_action(:deploy)
- FileUtils.rm_rf(deploy_directory)
- deploy_to_latest_rev.dup.run_action(:deploy)
- end
-
- include_examples "a redeployed application"
-
- end
-
- context "by removing the current/ directory" do
-
- before do
- deploy_to_latest_rev.dup.run_action(:deploy)
- FileUtils.rm(rel_path("current"))
- deploy_to_latest_rev.dup.run_action(:deploy)
- end
-
- include_examples "a redeployed application"
-
- end
- end
-
- context "when a deployment fails" do
-
- shared_examples_for "a recovered deployment" do
-
- it "should redeploy the application" do
- expect(File).to be_directory(rel_path("releases"))
- expect(File).to be_directory(rel_path("shared"))
- expect(File).to be_directory(rel_path("releases/#{latest_rev}"))
-
- expect(File).to be_directory(rel_path("current/tmp"))
- expect(File).to be_directory(rel_path("current/config"))
- expect(File).to be_directory(rel_path("current/public"))
-
- expect(File).to be_symlink(rel_path("current"))
- expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}"))
-
- # if callbacks ran, we know the app was deployed and not merely rolled
- # back to a (busted) prior deployment.
- expect(callback_order).to eq([:before_migrate,
- :before_symlink,
- :before_restart,
- :after_restart ])
- end
- end
-
- let!(:callback_order) { [] }
-
- let(:deploy_to_latest_with_callback_tracking) do
- resource = deploy_to_latest_rev.dup
- tracker = callback_order
- resource.before_migrate { tracker << :before_migrate }
- resource.before_symlink { tracker << :before_symlink }
- resource.before_restart { tracker << :before_restart }
- resource.after_restart { tracker << :after_restart }
- resource
- end
-
- [:before_migrate, :before_symlink, :before_restart, :after_restart].each do |callback|
-
- context "in the `#{callback}' callback" do
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Exception, %r{I am a failed deploy})
- deploy_to_latest_with_callback_tracking.run_action(:deploy)
- end
-
- let(:deploy_that_fails) do
- resource = deploy_to_latest_rev.dup
- errant_callback = lambda { |x| raise Exception, "I am a failed deploy" }
- resource.send(callback, &errant_callback)
- resource
- end
-
- include_examples "a recovered deployment"
-
- end
-
- end
-
- context "in the service restart step" do
-
- let(:deploy_that_fails) do
- resource = deploy_to_latest_rev.dup
- resource.restart_command("RUBYOPT=\"\" ruby -e 'exit 1'")
- resource
- end
-
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
- deploy_to_latest_with_callback_tracking.run_action(:deploy)
- end
-
- include_examples "a recovered deployment"
- end
-
- context "when cloning the app code" do
-
- class BadTimeScmProvider
- def initialize(new_resource, run_context)
- end
-
- def load_current_resource
- end
-
- def revision_slug
- "5"
- end
-
- def run_action(action)
- raise "network error"
- end
- end
-
- let(:deploy_that_fails) do
- resource = deploy_to_latest_rev.dup
- resource.scm_provider(BadTimeScmProvider)
- resource
- end
-
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(RuntimeError, /network error/)
- deploy_to_latest_with_callback_tracking.run_action(:deploy)
- end
-
- include_examples "a recovered deployment"
- end
-
- context "and then is deployed to a different revision" do
-
- let(:deploy_that_fails) do
- resource = deploy_to_previous_rev.dup
- resource.after_restart { |x| raise Exception, "I am a failed deploy" }
- resource
- end
-
- before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Exception, %r{I am a failed deploy})
- deploy_to_latest_rev.run_action(:deploy)
- end
-
- it "removes the unsuccessful deploy after a later successful deploy" do
- expect(::File).not_to exist(File.join(deploy_directory, "releases", previous_rev))
- end
-
- end
-
- end
-end
diff --git a/spec/functional/resource/directory_spec.rb b/spec/functional/resource/directory_spec.rb
index 0c1345d57f..b4791226f8 100644
--- a/spec/functional/resource/directory_spec.rb
+++ b/spec/functional/resource/directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
diff --git a/spec/functional/resource/dnf_package_spec.rb b/spec/functional/resource/dnf_package_spec.rb
new file mode 100644
index 0000000000..e0a69da4f9
--- /dev/null
+++ b/spec/functional/resource/dnf_package_spec.rb
@@ -0,0 +1,1277 @@
+#
+# Copyright:: Copyright (c) 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 "chef/mixin/shell_out"
+
+# run this test only for following platforms.
+exclude_test = !(%w{rhel fedora amazon}.include?(ohai[:platform_family]) && File.exist?("/usr/bin/dnf"))
+describe Chef::Resource::DnfPackage, :requires_root, external: exclude_test do
+ include Chef::Mixin::ShellOut
+
+ # NOTE: every single test here either needs to explicitly call flush_cache or needs to explicitly
+ # call preinstall (which explicitly calls flush_cache). It is your responsibility to do one or the
+ # other in order to minimize calling flush_cache a half dozen times per test.
+
+ def flush_cache
+ # needed on at least fc23/fc24 sometimes to deal with the dnf cache getting out of sync with the rpm db
+ FileUtils.rm_f "/var/cache/dnf/@System.solv"
+ Chef::Resource::DnfPackage.new("shouldnt-matter", run_context).run_action(:flush_cache)
+ end
+
+ def preinstall(*rpms)
+ rpms.each do |rpm|
+ shell_out!("rpm -ivh #{CHEF_SPEC_ASSETS}/yumrepo/#{rpm}")
+ end
+ flush_cache
+ end
+
+ before(:all) do
+ shell_out!("dnf -y install dnf-plugins-core")
+ end
+
+ before(:each) do
+ File.open("/etc/yum.repos.d/chef-dnf-localtesting.repo", "w+") do |f|
+ f.write <<~EOF
+ [chef-dnf-localtesting]
+ name=Chef DNF spec testing repo
+ baseurl=file://#{CHEF_SPEC_ASSETS}/yumrepo
+ enable=1
+ gpgcheck=0
+ EOF
+ end
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | grep chef_rpm | xargs -r rpm -e")
+ # next line is useful cleanup if you happen to have been testing both yum + dnf func tests on the same box and
+ # have some yum garbage left around
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ end
+
+ after(:all) do
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | grep chef_rpm | xargs -r rpm -e")
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:dnf_package) { Chef::Resource::DnfPackage.new(package_name, run_context) }
+
+ def pkg_arch
+ ohai[:kernel][:machine]
+ end
+
+ describe ":install" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+
+ it "installs if the package is not installed" do
+ flush_cache
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not install twice" do
+ flush_cache
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the i686 package is installed", :intel_64bit do
+ skip "FIXME: do nothing, or install the #{pkg_arch} version?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "does not install if the prior version i686 package is installed", :intel_64bit do
+ skip "FIXME: do nothing, or install the #{pkg_arch} version?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.i686$")
+ end
+ end
+
+ context "expanded idempotency checks with version variants" do
+ %w{1.10 1* 1.10-1 1*-1 1.10-* 1*-* 0:1.10 0:1* 0:1.10-1 0:1*-1 *:1.10-* *:1*-*}.each do |vstring|
+ it "installs the rpm when #{vstring} is in the package_name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the package_name" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the rpm when #{vstring} is in the version property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the version property" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades the rpm when #{vstring} is in the package_name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the package_name" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-#{vstring}")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades the rpm when #{vstring} is in the version property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ end
+
+ it "is idempotent when #{vstring} is in the version property" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ %w{1.2 1* 1.2-1 1*-1 1.2-* 1*-* 0:1.2 0:1* 0:1.2-1 0:1*-1 *:1.2-* *:1*-*}.each do |vstring|
+ it "is idempotent when #{vstring} is in the version property and there is a candidate version" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ %w{1.2 1.2-1 1.2-* 0:1.2 0:1.2-1 *:1.2-*}.each do |vstring|
+ it "is idempotent when #{vstring} is in the version property on upgrade and it doesn't match the candidate version" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ %w{1* 1*-1 1*-* 0:1* 0:1*-1 *:1*-*}.each do |vstring|
+ it "upgrades when #{vstring} is in the version property on upgrade and it matches the candidate version" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version(vstring)
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+ end
+
+ context "with versions or globs in the name" do
+ it "works with a version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with an older version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with an evra" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-0:1.2-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with version and release" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1.2-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with a version glob" do
+ flush_cache
+ dnf_package.package_name("chef_rpm-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "works with a name glob + version glob" do
+ flush_cache
+ dnf_package.package_name("chef_rp*-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades when the installed version does not match the version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "downgrades when the installed version is higher than the package_name version" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ # version only matches the actual dnf version, does not work with epoch or release or combined evr
+ context "with version property" do
+ it "matches the full version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches with a glob" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches the vr" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches the evr" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("0:1.10-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches with a vr glob", :rhel_gte_8 do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "matches with an evr glob", :rhel_gte_8 do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("0:1.10-1*")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "downgrades" do
+ it "downgrades the package when allow_downgrade" do
+ flush_cache
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.2-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with arches", :intel_64bit do
+ it "installs with 64-bit arch in the name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs with 32-bit arch in the name" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.i686")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs with 64-bit arch in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.arch((pkg_arch).to_s)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs with 32-bit arch in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.arch("i686")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs when the 32-bit arch is in the name and the version is in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.i686")
+ dnf_package.version("1.10-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs when the 64-bit arch is in the name and the version is in the property" do
+ flush_cache
+ dnf_package.package_name("chef_rpm.#{pkg_arch}")
+ dnf_package.version("1.10-1")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with constraints" do
+ it "with nothing installed, it installs the latest version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm >= 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm >= 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm >= 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with nothing installed, it installs the latest version" do
+ flush_cache
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with an equality constraint, when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm = 1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with an equality constraint, when it is met by an installed rpm, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm = 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "when it is met by an installed rpm, it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when there is no solution to the contraint" do
+ flush_cache
+ dnf_package.package_name("chef_rpm > 2.0")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "when there is no solution to the contraint but an rpm is preinstalled" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 2.0")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "with a less than constraint, when nothing is installed, it installs" do
+ flush_cache
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a less than constraint, when the install version matches, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a less than constraint, when the install version fails, it should downgrade" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with source arguments" do
+ it "raises an exception when the package does not exist" do
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "does not raise a hard exception in why-run mode when the package does not exist" do
+ Chef::Config[:why_run] = true
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ dnf_package.run_action(:install)
+ expect { dnf_package.run_action(:install) }.not_to raise_error
+ end
+
+ it "installs the package when using the source argument" do
+ flush_cache
+ dnf_package.name "something"
+ dnf_package.package_name "somethingelse"
+ dnf_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "downgrade on a local file with allow_downgrade true works" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.version "1.2-1"
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not downgrade the package with :install" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not upgrade the package with :install" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed and there is a version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.version "1.2-1"
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "multipackage with arches", :intel_64bit do
+ it "installs two rpms" do
+ flush_cache
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does nothing if both are installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ flush_cache
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the second rpm if the first is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the first rpm if the second is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs two rpms with multi-arch" do
+ flush_cache
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the second rpm if the first is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the first rpm if the second is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "does nothing if both are installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ dnf_package.package_name(%w{chef_rpm chef_rpm} )
+ dnf_package.arch([pkg_arch, "i686"])
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "repo controls" do
+ it "should fail with the repo disabled" do
+ flush_cache
+ dnf_package.options("--disablerepo=chef-dnf-localtesting")
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "should work with disablerepo first" do
+ flush_cache
+ dnf_package.options(["--disablerepo=*", "--enablerepo=chef-dnf-localtesting"])
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "should work to enable a disabled repo" do
+ shell_out!("dnf config-manager --set-disabled chef-dnf-localtesting")
+ flush_cache
+ expect { dnf_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ flush_cache
+ dnf_package.options("--enablerepo=chef-dnf-localtesting")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "when an idempotent install action is run, does not leave repos disabled" do
+ flush_cache
+ # this is a bit tricky -- we need this action to be idempotent, so that it doesn't recycle any
+ # caches, but need it to hit whatavailable with the repo disabled. using :upgrade like this
+ # accomplishes both those goals (it would be easier if we had other rpms in this repo, but with
+ # one rpm we need to do this).
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.options("--disablerepo=chef-dnf-localtesting")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ # now we're still using the same cache in the dnf_helper.py cache and we test to see if the
+ # repo that we temporarily disabled is enabled on this pass.
+ dnf_package.package_name("chef_rpm-1.10-1.#{pkg_arch}")
+ dnf_package.options(nil)
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+ end
+
+ describe ":upgrade" do
+ context "downgrades" do
+ it "just work with DNF" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "throws a deprecation warning with allow_downgrade" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect(Chef).to receive(:deprecated).with(:dnf_package_allow_downgrade, /^the allow_downgrade property on the dnf_package provider is not used/)
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.version("1.2")
+ dnf_package.allow_downgrade true
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}")
+ dnf_package.run_action(:install)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with source arguments" do
+ it "installs the package when using the source argument" do
+ flush_cache
+ dnf_package.name "something"
+ dnf_package.package_name "somethingelse"
+ dnf_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "downgrades the package when allow_downgrade is true" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "upgrades the package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ flush_cache
+ dnf_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "version pinning" do
+ it "with a full version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a full version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.2-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.version("1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a full version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.10-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a full version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.2-1")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin it installs a later package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a partial (no release) version pin in the name it downgrades the package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm-1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a prco equality pin in the name it upgrades a prior package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm = 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a prco equality pin in the name it downgrades a later package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm = 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a > pin in the name and no rpm installed it installs" do
+ flush_cache
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a < pin in the name and no rpm installed it installs" do
+ flush_cache
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a > pin in the name and matching rpm installed it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and no rpm installed it installs" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and non-matching rpm installed it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm > 1.2")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "with a < pin in the name and non-matching rpm installed it downgrades" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.package_name("chef_rpm < 1.10")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ dnf_package.run_action(:upgrade)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+ end
+
+ describe ":remove" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "does not remove the package twice" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "removes the package if the i686 package is installed", :intel_64bit do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "removes the package if the prior version i686 package is installed", :intel_64bit do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with 64-bit arch", :intel_64bit do
+ let(:package_name) { "chef_rpm.#{pkg_arch}" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does nothing if the i686 package is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "does nothing if the prior version i686 package is installed" do
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.i686$")
+ end
+ end
+
+ context "with 32-bit arch", :intel_64bit do
+ let(:package_name) { "chef_rpm.i686" }
+ it "removes only the 32-bit arch if both are installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ dnf_package.run_action(:remove)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+ end
+
+ describe ":lock and :unlock" do
+ before(:all) do
+ shell_out("dnf -y install python3-dnf-plugin-versionlock")
+ end
+
+ before(:each) do
+ shell_out("dnf versionlock delete 'chef_rpm-*'") # will exit with error when nothing is locked, we don't care
+ end
+
+ it "locks an rpm" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).to match("^chef_rpm-0:")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not lock if its already locked" do
+ flush_cache
+ shell_out!("dnf versionlock add chef_rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("dnf versionlock list").stdout.chomp).to match("^chef_rpm-0:")
+ end
+
+ it "unlocks an rpm" do
+ flush_cache
+ shell_out!("dnf versionlock add chef_rpm")
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).not_to match("^chef_rpm-0:")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "does not unlock an already locked rpm" do
+ flush_cache
+ dnf_package.package_name("chef_rpm")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ expect(shell_out("dnf versionlock list").stdout.chomp).not_to match("^chef_rpm-0:")
+ end
+
+ it "check that we can lock based on provides" do
+ flush_cache
+ dnf_package.package_name("chef_rpm_provides")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).to match("^chef_rpm-0:")
+ dnf_package.run_action(:lock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+
+ it "check that we can unlock based on provides" do
+ flush_cache
+ shell_out!("dnf versionlock add chef_rpm")
+ dnf_package.package_name("chef_rpm_provides")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be true
+ expect(shell_out("dnf versionlock list").stdout.chomp).not_to match("^chef_rpm-0:")
+ dnf_package.run_action(:unlock)
+ expect(dnf_package.updated_by_last_action?).to be false
+ end
+ end
+end
diff --git a/spec/functional/resource/dpkg_package_spec.rb b/spec/functional/resource/dpkg_package_spec.rb
index 1988fd0c7d..0a8202127c 100644
--- a/spec/functional/resource/dpkg_package_spec.rb
+++ b/spec/functional/resource/dpkg_package_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, 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");
@@ -273,7 +273,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
it "should remove both packages when called with two" do
shell_out!("dpkg -i #{test1_0} #{test2_0}")
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
@@ -282,7 +282,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
it "should remove a package when only the first one is installed" do
shell_out!("dpkg -i #{test1_0}")
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
@@ -291,7 +291,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
it "should remove a package when only the second one is installed" do
shell_out!("dpkg -i #{test2_0}")
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
@@ -299,7 +299,7 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
end
it "should do nothing when both packages are not installed" do
- set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ set_dpkg_package_name %w{chef-integration-test chef-integration-test2}
dpkg_package.run_action(action)
expect(dpkg_package).not_to be_updated_by_last_action
should_be_purged_or_removed("chef-integration-test")
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
index bb3cf2157d..227811a5ef 100644
--- a/spec/functional/resource/dsc_resource_spec.rb
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, 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");
@@ -33,11 +33,11 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
Chef::Resource::DscResource.new("dsc_resource_test", run_context)
end
- context "when Powershell does not support Invoke-DscResource"
- context "when Powershell supports Invoke-DscResource" do
+ context "when PowerShell does not support Invoke-DscResource"
+ context "when PowerShell supports Invoke-DscResource" do
before do
if !Chef::Platform.supports_dsc_invoke_resource?(node)
- skip "Requires Powershell >= 5.0.10018.0"
+ skip "Requires PowerShell >= 5.0.10018.0"
elsif !Chef::Platform.supports_refresh_mode_enabled?(node) && !Chef::Platform.dsc_refresh_mode_disabled?(node)
skip "Requires LCM RefreshMode is Disabled"
end
@@ -46,7 +46,8 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
it "raises an exception if the resource is not found" do
new_resource.resource "thisdoesnotexist"
expect { new_resource.run_action(:run) }.to raise_error(
- Chef::Exceptions::ResourceNotFound)
+ Chef::Exceptions::ResourceNotFound
+ )
end
end
@@ -61,7 +62,7 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
end
after do
- File.delete(tmp_file_name) if File.exists? tmp_file_name
+ File.delete(tmp_file_name) if File.exist? tmp_file_name
end
it "converges the resource if it is not converged" do
diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb
index e2b58f6432..b22599266b 100644
--- a/spec/functional/resource/dsc_script_spec.rb
+++ b/spec/functional/resource/dsc_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, 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");
@@ -17,14 +17,13 @@
#
require "spec_helper"
-require "chef/mixin/powershell_out"
-require "chef/mixin/shell_out"
+require "chef/mixin/powershell_exec"
require "chef/mixin/windows_architecture_helper"
require "support/shared/integration/integration_helper"
-describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
+describe Chef::Resource::DscScript, :windows_powershell_dsc_only, :ruby64_only do
include Chef::Mixin::WindowsArchitectureHelper
- include Chef::Mixin::PowershellOut
+ include Chef::Mixin::PowershellExec
before(:all) do
@temp_dir = ::Dir.mktmpdir("dsc-functional-test")
# enable the HTTP listener if it is not already enabled needed by underlying DSC engine
@@ -34,7 +33,7 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
winrm create winrm/config/Listener?Address=*+Transport=HTTP
}
CODE
- powershell_out!(winrm_code)
+ powershell_exec!(winrm_code)
end
after(:all) do
@@ -65,10 +64,8 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
end
def delete_user(target_user)
- begin
- shell_out!("net user #{target_user} /delete")
- rescue Mixlib::ShellOut::ShellCommandFailed
- end
+ shell_out!("net user #{target_user} /delete")
+ rescue Mixlib::ShellOut::ShellCommandFailed
end
let(:dsc_env_variable) { "chefenvtest" }
@@ -76,10 +73,11 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
let(:env_value2) { "value2" }
let(:dsc_test_run_context) do
node = Chef::Node.new
+ node.consume_external_attrs(OHAI_SYSTEM.data, {}) # node[:languages][:powershell][:version]
+ node.automatic["os"] = "windows"
node.automatic["platform"] = "windows"
node.automatic["platform_version"] = "6.1"
node.automatic["kernel"][:machine] = :x86_64 # Only 64-bit architecture is supported
- node.automatic[:languages][:powershell][:version] = "4.0"
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
end
@@ -104,7 +102,7 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
ValueData = '#{test_registry_data}'
Ensure = 'Present'
}
-EOH
+ EOH
end
let(:dsc_code) { dsc_reg_code }
@@ -112,7 +110,7 @@ EOH
<<-EOH
param($testregkeyname, $testregvaluename)
#{dsc_reg_code}
-EOH
+ EOH
end
let(:dsc_user_prefix) { "dsc" }
@@ -139,7 +137,7 @@ EOH
$#{dsc_user_prefix_param_name},
$#{dsc_user_suffix_param_name}
)
-EOH
+ EOH
end
let(:config_param_section) { "" }
@@ -148,59 +146,59 @@ EOH
let(:dsc_user_suffix_code) { dsc_user_suffix }
let(:dsc_script_environment_attribute) { nil }
let(:dsc_user_resources_code) do
- <<-EOH
- #{config_param_section}
-node localhost
-{
-$testuser = #{dsc_user_code}
-$testpassword = ConvertTo-SecureString -String "jf9a8m49jrajf4#" -AsPlainText -Force
-$testcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testuser, $testpassword
-
-User dsctestusercreate
-{
- UserName = $testuser
- Password = $testcred
- Description = "DSC test user"
- Ensure = "Present"
- Disabled = $false
- PasswordNeverExpires = $true
- PasswordChangeRequired = $false
-}
-}
-EOH
+ <<~EOH
+ #{config_param_section}
+ node localhost
+ {
+ $testuser = #{dsc_user_code}
+ $testpassword = ConvertTo-SecureString -String "jf9a8m49jrajf4#" -AsPlainText -Force
+ $testcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testuser, $testpassword
+
+ User dsctestusercreate
+ {
+ UserName = $testuser
+ Password = $testcred
+ Description = "DSC test user"
+ Ensure = "Present"
+ Disabled = $false
+ PasswordNeverExpires = $true
+ PasswordChangeRequired = $false
+ }
+ }
+ EOH
end
let(:dsc_user_config_data) do
- <<-EOH
-@{
- AllNodes = @(
- @{
- NodeName = "localhost";
- PSDscAllowPlainTextPassword = $true
- }
- )
-}
+ <<~EOH
+ @{
+ AllNodes = @(
+ @{
+ NodeName = "localhost";
+ PSDscAllowPlainTextPassword = $true
+ }
+ )
+ }
-EOH
+ EOH
end
let(:dsc_environment_env_var_name) { "dsc_test_cwd" }
- let(:dsc_environment_no_fail_not_etc_directory) { "#{ENV['systemroot']}\\system32" }
- let(:dsc_environment_fail_etc_directory) { "#{ENV['systemroot']}\\system32\\drivers\\etc" }
+ let(:dsc_environment_no_fail_not_etc_directory) { "#{ENV["systemroot"]}\\system32" }
+ let(:dsc_environment_fail_etc_directory) { "#{ENV["systemroot"]}\\system32\\drivers\\etc" }
let(:exception_message_signature) { "LL927-LL928" }
let(:dsc_environment_config) do
- <<-EOH
-if (($pwd.path -eq '#{dsc_environment_fail_etc_directory}') -and (test-path('#{dsc_environment_fail_etc_directory}')))
-{
- throw 'Signature #{exception_message_signature}: Purposefully failing because cwd == #{dsc_environment_fail_etc_directory}'
-}
-environment "whatsmydir"
-{
- Name = '#{dsc_environment_env_var_name}'
- Value = $pwd.path
- Ensure = 'Present'
-}
-EOH
+ <<~EOH
+ if (($pwd.path -eq '#{dsc_environment_fail_etc_directory}') -and (test-path('#{dsc_environment_fail_etc_directory}')))
+ {
+ throw 'Signature #{exception_message_signature}: Purposefully failing because cwd == #{dsc_environment_fail_etc_directory}'
+ }
+ environment "whatsmydir"
+ {
+ Name = '#{dsc_environment_env_var_name}'
+ Value = $pwd.path
+ Ensure = 'Present'
+ }
+ EOH
end
let(:dsc_config_name) do
@@ -235,7 +233,7 @@ EOH
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(false)
dsc_test_resource.run_action(:run)
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(true)
- expect(dsc_test_resource.registry_value_exists?(test_registry_key, { :name => test_registry_value, :type => :string, :data => test_registry_data })).to eq(true)
+ expect(dsc_test_resource.registry_value_exists?(test_registry_key, { name: test_registry_value, type: :string, data: test_registry_data })).to eq(true)
end
it_should_behave_like "a dsc_script resource with configuration affected by cwd"
@@ -244,13 +242,13 @@ EOH
shared_examples_for "a dsc_script resource with configuration affected by cwd" do
after(:each) do
removal_resource = Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
- removal_resource.code <<-EOH
-environment 'removethis'
-{
- Name = '#{dsc_environment_env_var_name}'
- Ensure = 'Absent'
-}
-EOH
+ removal_resource.code <<~EOH
+ environment 'removethis'
+ {
+ Name = '#{dsc_environment_env_var_name}'
+ Ensure = 'Absent'
+ }
+ EOH
removal_resource.run_action(:run)
end
@@ -263,12 +261,9 @@ EOH
it "should raise an exception if the cwd is etc" do
dsc_test_resource.cwd(dsc_environment_fail_etc_directory)
- expect { dsc_test_resource.run_action(:run) }.to raise_error(Chef::Exceptions::PowershellCmdletException)
- begin
+ expect {
dsc_test_resource.run_action(:run)
- rescue Chef::Exceptions::PowershellCmdletException => e
- expect(e.message).to match(exception_message_signature)
- end
+ }.to raise_error(Chef::PowerShell::CommandFailed, /#{exception_message_signature}/)
end
end
end
@@ -311,11 +306,11 @@ EOH
it "should set a registry key according to parameters passed to the configuration" do
dsc_test_resource.configuration_name(config_name_value)
- dsc_test_resource.flags({ :"#{reg_key_name_param_name}" => test_registry_key, :"#{reg_key_value_param_name}" => test_registry_value })
+ dsc_test_resource.flags({ "#{reg_key_name_param_name}": test_registry_key, "#{reg_key_value_param_name}": test_registry_value })
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(false)
dsc_test_resource.run_action(:run)
expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(true)
- expect(dsc_test_resource.registry_value_exists?(test_registry_key, { :name => test_registry_value, :type => :string, :data => test_registry_data })).to eq(true)
+ expect(dsc_test_resource.registry_value_exists?(test_registry_key, { name: test_registry_value, type: :string, data: test_registry_data })).to eq(true)
end
end
end
@@ -348,11 +343,9 @@ EOH
shared_examples_for "a dsc_script with configuration data that takes parameters" do
let(:dsc_user_code) { dsc_user_param_code }
let(:config_param_section) { config_params }
- let(:config_flags) { { :"#{dsc_user_prefix_param_name}" => "#{dsc_user_prefix}", :"#{dsc_user_suffix_param_name}" => "#{dsc_user_suffix}" } }
+ let(:config_flags) { { "#{dsc_user_prefix_param_name}": (dsc_user_prefix).to_s, "#{dsc_user_suffix_param_name}": (dsc_user_suffix).to_s } }
it "does not directly contain the user name" do
- configuration_script_content = ::File.open(dsc_test_resource.command) do |file|
- file.read
- end
+ configuration_script_content = ::File.open(dsc_test_resource.command, &:read)
expect(configuration_script_content.include?(dsc_user)).to be(false)
end
it_behaves_like "a dsc_script with configuration data"
@@ -362,9 +355,7 @@ EOH
let(:dsc_user_code) { dsc_user_env_code }
it "does not directly contain the user name" do
- configuration_script_content = ::File.open(dsc_test_resource.command) do |file|
- file.read
- end
+ configuration_script_content = ::File.open(dsc_test_resource.command, &:read)
expect(configuration_script_content.include?(dsc_user)).to be(false)
end
it_behaves_like "a dsc_script with configuration data"
@@ -410,46 +401,46 @@ EOH
end
let(:dsc_configuration_script) do
- <<-MYCODE
-cd c:\\
-configuration LCM
-{
- param ($thumbprint)
- localconfigurationmanager
- {
- RebootNodeIfNeeded = $false
- ConfigurationMode = 'ApplyOnly'
- CertificateID = $thumbprint
- }
-}
-$cert = ls Cert:\\LocalMachine\\My\\ |
- Where-Object {$_.Subject -match "ChefTest"} |
- Select -first 1
-
-if($cert -eq $null) {
- $pfxpath = '#{self_signed_cert_path}'
- $password = ''
- $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxpath, $password, ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset))
- $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "My", ([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
- $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
- $store.Add($cert)
- $store.Close()
-}
-
-lcm -thumbprint $cert.thumbprint
-set-dsclocalconfigurationmanager -path ./LCM
-$ConfigurationData = @"
-@{
-AllNodes = @(
- @{
- NodeName = "localhost";
- CertificateID = '$($cert.thumbprint)';
- };
-);
-}
-"@
-$ConfigurationData | out-file '#{configuration_data_path}' -force
- MYCODE
+ <<~MYCODE
+ cd c:\\
+ configuration LCM
+ {
+ param ($thumbprint)
+ localconfigurationmanager
+ {
+ RebootNodeIfNeeded = $false
+ ConfigurationMode = 'ApplyOnly'
+ CertificateID = $thumbprint
+ }
+ }
+ $cert = ls Cert:\\LocalMachine\\My\\ |
+ Where-Object {$_.Subject -match "ChefTest"} |
+ Select -first 1
+
+ if($cert -eq $null) {
+ $pfxpath = '#{self_signed_cert_path}'
+ $password = ''
+ $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxpath, $password, ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset))
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "My", ([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ $store.Add($cert)
+ $store.Close()
+ }
+
+ lcm -thumbprint $cert.thumbprint
+ set-dsclocalconfigurationmanager -path ./LCM
+ $ConfigurationData = @"
+ @{
+ AllNodes = @(
+ @{
+ NodeName = "localhost";
+ CertificateID = '$($cert.thumbprint)';
+ };
+ );
+ }
+ "@
+ $ConfigurationData | out-file '#{configuration_data_path}' -force
+ MYCODE
end
let(:powershell_script_resource) do
@@ -461,14 +452,14 @@ $ConfigurationData | out-file '#{configuration_data_path}' -force
let(:dsc_script_resource) do
dsc_test_resource_base.tap do |r|
- r.code <<-EOF
-User dsctestusercreate
-{
- UserName = '#{dsc_user}'
- Password = #{r.ps_credential('jf9a8m49jrajf4#')}
- Ensure = "Present"
-}
-EOF
+ r.code <<~EOF
+ User dsctestusercreate
+ {
+ UserName = '#{dsc_user}'
+ Password = #{r.ps_credential("jf9a8m49jrajf4#")}
+ Ensure = "Present"
+ }
+ EOF
r.configuration_data_script(configuration_data_path)
end
end
diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb
deleted file mode 100755
index 4b0ff70c0b..0000000000
--- a/spec/functional/resource/env_spec.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2014-2016, 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::Env, :windows_only do
- context "when running on Windows" do
- let(:chef_env_test_lower_case) { "chefenvtest" }
- let(:chef_env_test_mixed_case) { "chefENVtest" }
- let(:env_dne_key) { "env_dne_key" }
- let(:env_value1) { "value1" }
- let(:env_value2) { "value2" }
-
- let(:env_value_expandable) { "%SystemRoot%" }
- let(:test_run_context) do
- node = Chef::Node.new
- node.default["os"] = "windows"
- node.default["platform"] = "windows"
- node.default["platform_version"] = "6.1"
- empty_events = Chef::EventDispatch::Dispatcher.new
- Chef::RunContext.new(node, {}, empty_events)
- end
- let(:test_resource) do
- Chef::Resource::Env.new("unknown", test_run_context)
- end
-
- before(:each) do
- resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context)
- resource_lower.run_action(:delete)
- resource_mixed = Chef::Resource::Env.new(chef_env_test_mixed_case, test_run_context)
- resource_mixed.run_action(:delete)
- end
-
- context "when the create action is invoked" do
- it "should create an environment variable for action create" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- end
-
- it "should modify an existing variable's value to a new value" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.value(env_value2)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.key_name(chef_env_test_mixed_case)
- test_resource.value(env_value2)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- it "should not expand environment variables if the variable is not PATH" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value_expandable)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
- end
- end
-
- context "when the modify action is invoked" do
- it "should raise an exception for modify if the variable doesn't exist" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- expect { test_resource.run_action(:modify) }.to raise_error(Chef::Exceptions::Env)
- end
-
- it "should modify an existing variable's value to a new value" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.value(env_value2)
- test_resource.run_action(:modify)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- # This examlpe covers Chef Issue #1754
- it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.key_name(chef_env_test_mixed_case)
- test_resource.value(env_value2)
- test_resource.run_action(:modify)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
- end
-
- it "should not expand environment variables if the variable is not PATH" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.value(env_value_expandable)
- test_resource.run_action(:modify)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
- end
-
- context "when using PATH" do
- let(:random_name) { Time.now.to_i }
- let(:env_val) { "#{env_value_expandable}_#{random_name}" }
- let!(:path_before) { test_resource.provider_for_action(test_resource.action).env_value("PATH") || "" }
- let!(:env_path_before) { ENV["PATH"] }
-
- it "should expand PATH" do
- expect(path_before).not_to include(env_val)
- test_resource.key_name("PATH")
- test_resource.value("#{path_before};#{env_val}")
- test_resource.run_action(:create)
- expect(ENV["PATH"]).not_to include(env_val)
- expect(ENV["PATH"]).to include("#{random_name}")
- end
-
- after(:each) do
- # cleanup so we don't flood the path
- test_resource.key_name("PATH")
- test_resource.value(path_before)
- test_resource.run_action(:create)
- ENV["PATH"] = env_path_before
- end
- end
-
- end
-
- context "when the delete action is invoked" do
- it "should delete an environment variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.run_action(:delete)
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- end
-
- it "should not raise an exception when a non-existent environment variable is deleted" do
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- expect { test_resource.run_action(:delete) }.not_to raise_error
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- end
-
- it "should delete an existing variable's value to a new value if the specified variable name case differs from the existing variable" do
- test_resource.key_name(chef_env_test_lower_case)
- test_resource.value(env_value1)
- test_resource.run_action(:create)
- expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
- test_resource.key_name(chef_env_test_mixed_case)
- test_resource.run_action(:delete)
- expect(ENV[chef_env_test_lower_case]).to eq(nil)
- expect(ENV[chef_env_test_mixed_case]).to eq(nil)
- end
-
- it "should delete a value from the current process even if it is not in the registry" do
- expect(ENV[env_dne_key]).to eq(nil)
- ENV[env_dne_key] = env_value1
- test_resource.key_name(env_dne_key)
- test_resource.run_action(:delete)
- expect(ENV[env_dne_key]).to eq(nil)
- end
- end
- end
-end
diff --git a/spec/functional/resource/execute_spec.rb b/spec/functional/resource/execute_spec.rb
index 3c31537ebe..3d7e185e17 100644
--- a/spec/functional/resource/execute_spec.rb
+++ b/spec/functional/resource/execute_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, 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");
@@ -17,11 +17,11 @@
#
require "spec_helper"
-require "functional/resource/base"
require "timeout"
describe Chef::Resource::Execute do
let(:resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
resource = Chef::Resource::Execute.new("foo_resource", run_context)
resource.command("echo hello")
resource
@@ -87,8 +87,8 @@ describe Chef::Resource::Execute do
describe "when parent resource sets :environment" do
before do
resource.environment({
- "SAWS_SECRET" => "supersecret",
- "SAWS_KEY" => "qwerty",
+ "SAWS_SECRET" => "supersecret",
+ "SAWS_KEY" => "qwerty",
})
end
@@ -106,7 +106,7 @@ describe Chef::Resource::Execute do
it "guard adds additional values in its :environment and runs" do
resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] != "regularsecret"'}, {
- :environment => { "SGCE_SECRET" => "regularsecret" },
+ environment: { "SGCE_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).to be_updated_by_last_action
@@ -114,7 +114,7 @@ describe Chef::Resource::Execute do
it "guard adds additional values in its :environment and does not run" do
resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] == "regularsecret"'}, {
- :environment => { "SGCE_SECRET" => "regularsecret" },
+ environment: { "SGCE_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).not_to be_updated_by_last_action
@@ -122,7 +122,7 @@ describe Chef::Resource::Execute do
it "guard overwrites value with its :environment and runs" do
resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "regularsecret"'}, {
- :environment => { "SAWS_SECRET" => "regularsecret" },
+ environment: { "SAWS_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).to be_updated_by_last_action
@@ -130,13 +130,25 @@ describe Chef::Resource::Execute do
it "guard overwrites value with its :environment and does not runs" do
resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] == "regularsecret"'}, {
- :environment => { "SAWS_SECRET" => "regularsecret" },
+ environment: { "SAWS_SECRET" => "regularsecret" },
}
resource.run_action(:run)
expect(resource).not_to be_updated_by_last_action
end
end
+ describe "when a guard is specified" do
+ describe "when using the default guard interpreter" do
+ let(:guard_interpreter_resource) { nil }
+ it_behaves_like "a resource with a guard specifying an alternate user identity"
+ end
+
+ describe "when using the execute resource as the guard interpreter" do
+ let(:guard_interpreter_resource) { :execute }
+ it_behaves_like "a resource with a guard specifying an alternate user identity"
+ end
+ end
+
# Ensure that CommandTimeout is raised, and is caused by resource.timeout really expiring.
# https://github.com/chef/chef/issues/2985
#
@@ -151,4 +163,9 @@ describe Chef::Resource::Execute do
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout)
end
end
+
+ describe "when running with an alternate user identity" do
+ let(:resource_command_property) { :command }
+ it_behaves_like "an execute resource that supports alternate user identity"
+ end
end
diff --git a/spec/functional/resource/file_spec.rb b/spec/functional/resource/file_spec.rb
index 0fa1317032..c0d68e1762 100644
--- a/spec/functional/resource/file_spec.rb
+++ b/spec/functional/resource/file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
@@ -51,7 +51,7 @@ describe Chef::Resource::File do
end
let(:resource_with_relative_path) do
- create_resource(:use_relative_path => true)
+ create_resource(use_relative_path: true)
end
let(:unmanaged_content) do
diff --git a/spec/functional/resource/git_spec.rb b/spec/functional/resource/git_spec.rb
index 6808898c29..ab05947d29 100644
--- a/spec/functional/resource/git_spec.rb
+++ b/spec/functional/resource/git_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@chef.io>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -17,30 +17,18 @@
#
require "spec_helper"
-require "chef/mixin/shell_out"
require "tmpdir"
-require "shellwords"
# Deploy relies heavily on symlinks, so it doesn't work on windows.
-describe Chef::Resource::Git, :requires_git => true do
- include Chef::Mixin::ShellOut
- let(:file_cache_path) { Dir.mktmpdir }
+describe Chef::Resource::Git do
+ include RecipeDSLHelper
+
# Some versions of git complains when the deploy directory is
# already created. Here we intentionally don't create the deploy
# directory beforehand.
let(:base_dir_path) { Dir.mktmpdir }
let(:deploy_directory) { File.join(base_dir_path, make_tmpname("git_base")) }
- let(:node) do
- Chef::Node.new.tap do |n|
- n.name "rspec-test"
- n.consume_external_attrs(@ohai.data, {})
- end
- end
-
- let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
- let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
-
# These tests use git's bundle feature, which is a way to export an entire
# git repo (or subset of commits) as a single file.
#
@@ -64,33 +52,31 @@ describe Chef::Resource::Git, :requires_git => true do
let(:rev_testing) { "972d153654503bccec29f630c5dd369854a561e8" }
let(:rev_head) { "d294fbfd05aa7709ad9a9b8ef6343b17d355bf5f" }
- let(:git_user_config) do
- <<-E
-[user]
- name = frodoTbaggins
- email = frodo@shire.org
-E
- end
-
before(:each) do
- Chef::Log.level = :warn # silence git command live streams
- @old_file_cache_path = Chef::Config[:file_cache_path]
- shell_out!("git clone \"#{git_bundle_repo}\" example", :cwd => origin_repo_dir)
- File.open("#{origin_repo}/.git/config", "a+") { |f| f.print(git_user_config) }
- Chef::Config[:file_cache_path] = file_cache_path
+ shell_out!("git", "clone", git_bundle_repo, "example", cwd: origin_repo_dir)
+ File.open("#{origin_repo}/.git/config", "a+") do |f|
+ f.print <<~EOF
+ [user]
+ name = frodoTbaggins
+ email = frodo@shire.org
+ EOF
+ end
end
after(:each) do
- Chef::Config[:file_cache_path] = @old_file_cache_path
FileUtils.remove_entry_secure deploy_directory if File.exist?(deploy_directory)
FileUtils.remove_entry_secure base_dir_path
- FileUtils.remove_entry_secure file_cache_path
FileUtils.remove_entry_secure origin_repo_dir
end
- before(:all) do
- @ohai = Ohai::System.new
- @ohai.all_plugins(%w{platform os})
+ def expect_revision_to_be(revision, version)
+ rev_ver = shell_out!("git", "rev-parse", revision, cwd: deploy_directory).stdout.strip
+ expect(rev_ver).to eq(version)
+ end
+
+ def expect_branch_to_be(branch)
+ head_branch = shell_out!("git name-rev --name-only HEAD", cwd: deploy_directory).stdout.strip
+ expect(head_branch).to eq(branch)
end
context "working with pathes with special characters" do
@@ -102,156 +88,242 @@ E
end
it "clones a repository with a space in the path" do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository "#{path_with_spaces}/example-repo.gitbundle"
- end.run_action(:sync)
+ repo = "#{path_with_spaces}/example-repo.gitbundle"
+ git(deploy_directory) do
+ repository repo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
end
end
context "when deploying from an annotated tag" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "v1.0.0"
- end
- end
-
- # We create a copy of the basic_git_resource so that we can run
- # the resource again and verify that it doesn't update.
- let(:copy_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "v1.0.0"
- end
- end
-
it "checks out the revision pointed to by the tag commit, not the tag commit itself" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(v1_commit)
+ git deploy_directory do
+ repository origin_repo
+ revision "v1.0.0"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", v1_commit)
+ expect_branch_to_be("tags/v1.0.0^0") # detached
# also verify the tag commit itself is what we expect as an extra sanity check
- rev = shell_out!("git rev-parse v1.0.0", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(rev).to eq(v1_tag)
+ expect_revision_to_be("v1.0.0", v1_tag)
end
it "doesn't update if up-to-date" do
- # this used to fail because we didn't resolve the annotated tag
- # properly to the pointed to commit.
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(v1_commit)
-
- copy_git_resource.run_action(:sync)
- expect(copy_git_resource).not_to be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "v1.0.0"
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "v1.0.0"
+ expect_branch_to_be("tags/v1.0.0^0") # detached
+ end.should_not_be_updated
end
end
context "when deploying from a SHA revision" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository git_bundle_repo
- end
- end
-
- # We create a copy of the basic_git_resource so that we can run
- # the resource again and verify that it doesn't update.
- let(:copy_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- end
+ it "checks out the expected revision ed18" do
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_foo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_foo)
+ expect_branch_to_be("master~1") # detached
end
- it "checks out the expected revision ed18" do
- basic_git_resource.revision rev_foo
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_foo)
+ it "checks out the expected revision ed18 to a local branch" do
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_foo
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_foo)
+ expect_branch_to_be("deploy") # detached
end
it "doesn't update if up-to-date" do
- basic_git_resource.revision rev_foo
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_foo)
-
- copy_git_resource.revision rev_foo
- copy_git_resource.run_action(:sync)
- expect(copy_git_resource).not_to be_updated
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_foo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_foo)
+
+ git deploy_directory do
+ repository origin_repo
+ revision rev_foo
+ end.should_not_be_updated
+ expect_branch_to_be("master~1") # detached
end
it "checks out the expected revision 972d" do
- basic_git_resource.revision rev_testing
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_testing)
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_testing
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_testing)
+ expect_branch_to_be("master~2") # detached
+ end
+
+ it "checks out the expected revision 972d to a local branch" do
+ git deploy_directory do
+ repository git_bundle_repo
+ revision rev_testing
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_testing)
+ expect_branch_to_be("deploy")
end
end
context "when deploying from a revision named 'HEAD'" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "HEAD"
- end
+ it "checks out the expected revision" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- it "checks out the expected revision" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ it "checks out the expected revision, and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
+ end
+
+ it "checks out the expected revision to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
end
end
context "when deploying from the default revision" do
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- # use default
- end
+ it "checks out HEAD as the default revision" do
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- it "checks out HEAD as the default revision" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD", :cwd => deploy_directory, :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ it "checks out HEAD as the default revision, and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
+ end
+
+ it "checks out HEAD as the default revision to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
+ end
+ end
+
+ context "when updating a branch that's already checked out out" do
+ it "checks out master, commits to the repo, and checks out the latest changes" do
+ git deploy_directory do
+ repository origin_repo
+ revision "master"
+ action :sync
+ end.should_be_updated
+
+ # We don't have a way to test a commit in the git bundle
+ # Revert to a previous commit in the same branch and make sure we can still sync.
+ shell_out!("git", "reset", "--hard", rev_foo, cwd: deploy_directory)
+
+ git deploy_directory do
+ repository origin_repo
+ revision "master"
+ action :sync
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
end
context "when dealing with a repo with a degenerate tag named 'HEAD'" do
before do
- shell_out!("git tag -m\"degenerate tag\" HEAD ed181b3419b6f489bedab282348162a110d6d3a1",
- :cwd => origin_repo)
+ shell_out!("git", "tag", "-m\"degenerate tag\"", "HEAD", "ed181b3419b6f489bedab282348162a110d6d3a1", cwd: origin_repo)
end
- let(:basic_git_resource) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- r.revision "HEAD"
- end
+ it "checks out the (master) HEAD revision and ignores the tag" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- let(:git_resource_default_rev) do
- Chef::Resource::Git.new(deploy_directory, run_context).tap do |r|
- r.repository origin_repo
- # use default of revision
- end
+ it "checks out the (master) HEAD revision and ignores the tag, and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
- it "checks out the (master) HEAD revision and ignores the tag" do
- basic_git_resource.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD",
- :cwd => deploy_directory,
- :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ it "checks out the (master) HEAD revision and ignores the tag to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ revision "HEAD"
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
end
it "checks out the (master) HEAD revision when no revision is specified (ignores tag)" do
- git_resource_default_rev.run_action(:sync)
- head_rev = shell_out!("git rev-parse HEAD",
- :cwd => deploy_directory,
- :returns => [0]).stdout.strip
- expect(head_rev).to eq(rev_head)
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
+ end
+
+ it "checks out the (master) HEAD revision when no revision is specified (ignores tag), and is idempotent" do
+ git deploy_directory do
+ repository origin_repo
+ end.should_be_updated
+ git deploy_directory do
+ repository origin_repo
+ end.should_not_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("master")
end
+ it "checks out the (master) HEAD revision when no revision is specified (ignores tag) to a local branch" do
+ git deploy_directory do
+ repository origin_repo
+ checkout_branch "deploy"
+ end.should_be_updated
+ expect_revision_to_be("HEAD", rev_head)
+ expect_branch_to_be("deploy")
+ end
end
end
diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb
index aa5a29f92c..a682e9c0c7 100644
--- a/spec/functional/resource/group_spec.rb
+++ b/spec/functional/resource/group_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Chirag Jog (<chirag@clogeny.com>)
# Author:: Siddheshwar More (<siddheshwar.more@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -18,19 +18,14 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
-# Chef::Resource::Group are turned off on Mac OS X 10.6 due to caching
-# issues around Etc.getgrnam() not picking up the group membership
-# changes that are done on the system. Etc.endgrent is not functioning
-# correctly on certain 10.6 boxes.
-describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supported_on_mac_osx_106 do
+describe Chef::Resource::Group, :requires_root_or_running_windows do
include Chef::Mixin::ShellOut
def group_should_exist(group)
- case ohai[:platform_family]
- when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch"
+ case ohai[:os]
+ when "linux"
expect { Etc.getgrnam(group) }.not_to raise_error
expect(group).to eq(Etc.getgrnam(group).name)
when "windows"
@@ -54,8 +49,8 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
def group_should_not_exist(group)
- case ohai[:platform_family]
- when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch"
+ case ohai[:os]
+ when "linux"
expect { Etc.getgrnam(group) }.to raise_error(ArgumentError, "can't find group for #{group}")
when "windows"
expect { Chef::Util::Windows::NetGroup.new(group).local_get_members }.to raise_error(ArgumentError, /The group name could not be found./)
@@ -81,7 +76,7 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
if user && domain != "."
computer_name = ENV["computername"]
- !domain.casecmp(computer_name.downcase).zero?
+ !domain.casecmp(computer_name.downcase) == 0
end
end
@@ -99,13 +94,17 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
usr
end
- def create_user(username)
- user(username).run_action(:create) if ! windows_domain_user?(username)
+ def create_user(username, uid = nil)
+ unless windows_domain_user?(username)
+ user_to_create = user(username)
+ user_to_create.uid(uid) if uid
+ user_to_create.run_action(:create)
+ end
# TODO: User should exist
end
def remove_user(username)
- if ! windows_domain_user?(username)
+ unless windows_domain_user?(username)
u = user(username)
u.manage_home false # jekins hosts throw mail spool file not owned by user if we use manage_home true
u.run_action(:remove)
@@ -113,6 +112,15 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
# TODO: User shouldn't exist
end
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
shared_examples_for "correct group management" do
def add_members_to_group(members)
temp_resource = group_resource.dup
@@ -146,13 +154,15 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
# dscl doesn't perform any error checking and will let you add users that don't exist.
- describe "when no users exist", :not_supported_on_mac_osx do
+ describe "when no users exist", :not_supported_on_macos do
describe "when append is not set" do
# excluded_members can only be used when append is set. It is ignored otherwise.
let(:excluded_members) { [] }
+ let(:expected_error_class) { windows? ? ArgumentError : Mixlib::ShellOut::ShellCommandFailed }
+
it "should raise an error" do
- expect { group_resource.run_action(tested_action) }.to raise_error()
+ expect { group_resource.run_action(tested_action) }.to raise_error(expected_error_class)
end
end
@@ -161,16 +171,21 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
group_resource.append(true)
end
+ let(:expected_error_class) { windows? ? Chef::Exceptions::Win32APIError : Mixlib::ShellOut::ShellCommandFailed }
+
it "should raise an error" do
- expect { group_resource.run_action(tested_action) }.to raise_error()
+ expect { group_resource.run_action(tested_action) }.to raise_error(expected_error_class)
end
end
end
describe "when the users exist" do
before do
+ high_uid = 30000
(spec_members).each do |member|
- create_user(member)
+ remove_user(member)
+ create_user(member, high_uid)
+ high_uid += 1
end
end
@@ -289,13 +304,27 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
end
- let(:group_name) { "group#{SecureRandom.random_number(9999)}" }
- let(:included_members) { nil }
- let(:excluded_members) { nil }
+ let(:number) do
+ # Loop until we pick a gid that is not in use.
+ loop do
+
+ gid = rand(2000..9999) # avoid low group numbers
+ return nil if Etc.getgrgid(gid).nil? # returns nil on windows
+ rescue ArgumentError # group does not exist
+ return gid
+
+ end
+ end
+
+ let(:group_name) { "grp#{number}" } # group name should be 8 characters or less for Solaris, and possibly others
+ # https://community.aegirproject.org/developing/architecture/unix-group-limitations/index.html#Group_name_length_limits
+ let(:included_members) { [] }
+ let(:excluded_members) { [] }
let(:group_resource) do
group = Chef::Resource::Group.new(group_name, run_context)
group.members(included_members)
group.excluded_members(excluded_members)
+ group.gid(number) unless ohai[:platform_family] == "mac_os_x"
group
end
@@ -316,10 +345,11 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
describe "when group name is length 256", :windows_only do
let!(:group_name) do
- "theoldmanwalkingdownthestreetalwayshadagood\
-smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
-theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" end
+ "theoldmanwalkingdownthestreetalwayshadagood"\
+ "smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface"\
+ "theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking"\
+ "downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree"
+ end
it "should create a group" do
group_resource.run_action(:create)
@@ -327,19 +357,6 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" end
end
end
- describe "when group name length is more than 256", :windows_only do
- let!(:group_name) do
- "theoldmanwalkingdownthestreetalwayshadagood\
-smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
-theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
-
- it "should not create a group" do
- expect { group_resource.run_action(:create) }.to raise_error(ArgumentError)
- group_should_not_exist(group_name)
- end
- end
-
# not_supported_on_solaris because of the use of excluded_members
describe "should raise an error when same member is included in the members and excluded_members", :not_supported_on_solaris do
it "should raise an error" do
@@ -351,6 +368,26 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
end
+ # Note:This testcase is written separately from the `group create action` defined above because
+ # for group name > 256, Windows 2016 returns "The parameter is incorrect"
+ context "group create action: when group name length is more than 256", :windows_only do
+ let!(:group_name) do
+ "theoldmanwalkingdownthestreetalwayshadagood"\
+ "smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface"\
+ "theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking"\
+ "downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ"
+ end
+
+ it "should not create a group" do
+ expect { group_resource.run_action(:create) }.to raise_error(ArgumentError)
+ if windows_gte_10?
+ expect { Chef::Util::Windows::NetGroup.new(group_name).local_get_members }.to raise_error(ArgumentError, /The parameter is incorrect./)
+ else
+ group_should_not_exist(group_name)
+ end
+ end
+ end
+
describe "group remove action" do
describe "when there is a group" do
before do
@@ -400,7 +437,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
end
- describe "group manage action", :not_supported_on_solaris do
+ describe "group manage action" do
let(:spec_members) { %w{mnou5sdz htulrvwq x4c3g1lu} }
let(:included_members) { [spec_members[0], spec_members[1]] }
let(:excluded_members) { [spec_members[2]] }
@@ -417,6 +454,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
it "does not raise an error on manage" do
+ allow(Etc).to receive(:getpwnam).and_return(double("User"))
expect { group_resource.run_action(:manage) }.not_to raise_error
end
end
@@ -437,37 +475,4 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
end
end
- describe "group resource with Usermod provider", :solaris_only do
- describe "when excluded_members is set" do
- let(:excluded_members) { ["x4c3g1lu"] }
-
- it ":manage should raise an error" do
- expect { group_resource.run_action(:manage) }.to raise_error
- end
-
- it ":modify should raise an error" do
- expect { group_resource.run_action(:modify) }.to raise_error
- end
-
- it ":create should raise an error" do
- expect { group_resource.run_action(:create) }.to raise_error
- end
- end
-
- describe "when append is not set" do
- let(:included_members) { %w{gordon eric} }
-
- before(:each) do
- group_resource.append(false)
- end
-
- it ":manage should raise an error" do
- expect { group_resource.run_action(:manage) }.to raise_error
- end
-
- it ":modify should raise an error" do
- expect { group_resource.run_action(:modify) }.to raise_error
- end
- end
- end
end
diff --git a/spec/functional/resource/ifconfig_spec.rb b/spec/functional/resource/ifconfig_spec.rb
index a30dcff641..127e4e6331 100644
--- a/spec/functional/resource/ifconfig_spec.rb
+++ b/spec/functional/resource/ifconfig_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -16,17 +16,24 @@
# limitations under the License.
#
-require "functional/resource/base"
+require "spec_helper"
require "chef/mixin/shell_out"
# run this test only for following platforms.
-include_flag = !(%w{ubuntu centos aix}.include?(ohai[:platform]))
-
-describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => include_flag do
- # This test does not work in travis because there is no eth0
+include_flag = !(%w{amazon debian aix}.include?(ohai[:platform_family]) || (ohai[:platform_family] == "rhel" && ohai[:platform_version].to_i < 7))
+describe Chef::Resource::Ifconfig, :requires_root, :requires_ifconfig, external: include_flag do
include Chef::Mixin::ShellOut
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
let(:new_resource) do
new_resource = Chef::Resource::Ifconfig.new("10.10.0.1", run_context)
new_resource
@@ -51,11 +58,17 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
+ def fetch_first_interface_name
+ shell_out("ip link list |grep UP|grep -vi loop|head -1|cut -d':' -f 2 |cut -d'@' -f 1").stdout.strip
+ end
+
# **Caution: any updates to core interfaces can be risky.
def en0_interface_for_test
case ohai[:platform]
when "aix"
"en0"
+ when "ubuntu"
+ fetch_first_interface_name
else
"eth0"
end
@@ -115,7 +128,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
exclude_test = ohai[:platform] != "ubuntu"
- describe "#action_add", :external => exclude_test do
+ describe "#action_add", external: exclude_test do
after do
new_resource.run_action(:delete)
end
@@ -127,7 +140,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
- describe "#action_enable", :external => exclude_test do
+ describe "#action_enable", external: exclude_test do
after do
new_resource.run_action(:disable)
end
@@ -138,7 +151,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
- describe "#action_disable", :external => exclude_test do
+ describe "#action_disable", external: exclude_test do
before do
setup_enable_interface(new_resource)
new_resource.run_action(:enable)
@@ -150,7 +163,7 @@ describe Chef::Resource::Ifconfig, :requires_root, :skip_travis, :external => in
end
end
- describe "#action_delete", :external => exclude_test do
+ describe "#action_delete", external: exclude_test do
before do
setup_add_interface(new_resource)
new_resource.run_action(:add)
diff --git a/spec/functional/resource/insserv_spec.rb b/spec/functional/resource/insserv_spec.rb
new file mode 100644
index 0000000000..e5c74c68ac
--- /dev/null
+++ b/spec/functional/resource/insserv_spec.rb
@@ -0,0 +1,206 @@
+#
+# Author:: Dheeraj Dubey (<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "chef/mixin/shell_out"
+require "fileutils"
+
+describe Chef::Resource::Service, :requires_root, :opensuse do
+
+ include Chef::Mixin::ShellOut
+
+ def service_should_be_enabled
+ expect(shell_out!("/sbin/insserv -r -f #{new_resource.service_name}").exitstatus).to eq(0)
+ expect(shell_out!("/sbin/insserv -d -f #{new_resource.service_name}").exitstatus).to eq(0)
+ !Dir.glob("/etc/rc*/**/S*#{service_name}").empty?
+ end
+
+ def service_should_be_disabled
+ expect(shell_out!("/sbin/insserv -r -f #{new_resource.service_name}").exitstatus).to eq(0)
+ Dir.glob("/etc/rc*/**/S*#{service_name}").empty?
+ end
+
+ # Platform specific validation routines.
+ def service_should_be_started(file_name)
+ # The existence of this file indicates that the service was started.
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_truthy
+ end
+
+ def service_should_be_stopped(file_name)
+ expect(File.exist?("#{Dir.tmpdir}/#{file_name}")).to be_falsey
+ end
+
+ def delete_test_files
+ files = Dir.glob("#{Dir.tmpdir}/init[a-z_]*.txt")
+ File.delete(*files)
+ end
+
+ # Actual tests
+ let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+
+ new_resource = Chef::Resource::Service.new("inittest", run_context)
+ new_resource.provider Chef::Provider::Service::Insserv
+ new_resource.supports({ status: true, restart: true, reload: true })
+ new_resource
+ end
+
+ let(:provider) do
+ provider = new_resource.provider_for_action(new_resource.action)
+ provider
+ end
+
+ let(:service_name) { "Chef::Util::PathHelper.escape_glob_dir(current_resource.service_name)" }
+
+ let(:current_resource) do
+ provider.load_current_resource
+ provider.current_resource
+ end
+
+ before(:all) do
+ File.delete("/etc/init.d/inittest") if File.exist?("/etc/init.d/inittest")
+ FileUtils.cp((File.join(__dir__, "/../assets/inittest")).to_s, "/etc/init.d/inittest")
+ FileUtils.chmod(0755, "/etc/init.d/inittest")
+ end
+
+ after(:all) do
+ File.delete("/etc/init.d/inittest") if File.exist?("/etc/init.d/inittest")
+ end
+
+ before(:each) do
+ delete_test_files
+ end
+
+ after(:each) do
+ delete_test_files
+ end
+
+ describe "start service" do
+ it "should start the service" do
+ new_resource.run_action(:start)
+ service_should_be_started("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:start)
+ service_should_be_started("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:start)
+ service_should_be_started("inittest.txt")
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "stop service" do
+ before do
+ new_resource.run_action(:start)
+ end
+
+ it "should stop the service" do
+ new_resource.run_action(:stop)
+ service_should_be_stopped("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:stop)
+ service_should_be_stopped("inittest.txt")
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:stop)
+ service_should_be_stopped("inittest.txt")
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "restart service" do
+ before do
+ new_resource.run_action(:start)
+ end
+
+ it "should restart the service" do
+ new_resource.run_action(:restart)
+ service_should_be_started("inittest_restart.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ skip "FIXME: restart is not idempotent"
+ new_resource.run_action(:restart)
+ service_should_be_disabled
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:restart)
+ service_should_be_disabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "reload service" do
+ before do
+ new_resource.run_action(:start)
+ end
+
+ it "should reload the service" do
+ new_resource.run_action(:reload)
+ service_should_be_started("inittest_reload.txt")
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ skip "FIXME: reload is not idempotent"
+ new_resource.run_action(:reload)
+ service_should_be_disabled
+ expect(new_resource).to be_updated_by_last_action
+ new_resource.run_action(:reload)
+ service_should_be_disabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "enable service" do
+ it "should enable the service" do
+ new_resource.run_action(:enable)
+ service_should_be_enabled
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:enable)
+ service_should_be_enabled
+ new_resource.run_action(:enable)
+ service_should_be_enabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "disable_service" do
+ it "should disable the service" do
+ new_resource.run_action(:disable)
+ service_should_be_disabled
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ new_resource.run_action(:disable)
+ service_should_be_disabled
+ new_resource.run_action(:disable)
+ service_should_be_disabled
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+end
diff --git a/spec/functional/resource/launchd_spec.rb b/spec/functional/resource/launchd_spec.rb
new file mode 100644
index 0000000000..70399d310d
--- /dev/null
+++ b/spec/functional/resource/launchd_spec.rb
@@ -0,0 +1,232 @@
+#
+# Copyright:: Copyright (c) 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"
+
+# Deploy relies heavily on symlinks, so it doesn't work on windows.
+describe Chef::Resource::Launchd, :macos_only, requires_root: true do
+ include RecipeDSLHelper
+
+ before(:each) do
+ shell_out("launchctl unload -wF /Library/LaunchDaemons/io.chef.testing.fake.plist")
+ FileUtils.rm_f "/Library/LaunchDaemons/io.chef.testing.fake.plist"
+ end
+
+ after(:each) do
+ shell_out("launchctl unload -wF /Library/LaunchDaemons/io.chef.testing.fake.plist")
+ FileUtils.rm_f "/Library/LaunchDaemons/io.chef.testing.fake.plist"
+ end
+
+ context ":enable" do
+ it "enables a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out!("launchctl list io.chef.testing.fake").stdout).to match('"PID" = \d+')
+ expect(shell_out!("launchctl list io.chef.testing.fake").stdout).not_to match('"PID" = 0')
+ end
+
+ it "should be idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end.should_not_be_updated
+ end
+ end
+
+ context ":create" do
+ it "creates a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+
+ it "should be idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create
+ end.should_not_be_updated
+ end
+ end
+
+ context ":create_if_missing" do
+ it "creates a service if it is missing" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create_if_missing
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+ it "is idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create_if_missing
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :create_if_missing
+ end.should_not_be_updated
+ end
+ end
+
+ context ":delete" do
+ it "deletes a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be false
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+ it "is idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "60",
+ ]
+ run_at_load true
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_not_be_updated
+ end
+ it "works if the file does not exist" do
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :delete
+ end.should_not_be_updated
+ end
+ end
+
+ context ":disable" do
+ it "deletes a service" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "1",
+ ]
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_be_updated
+ expect(File.exist?("/Library/LaunchDaemons/io.chef.testing.fake.plist")).to be true
+ expect(shell_out("launchctl list io.chef.testing.fake").exitstatus).not_to eql(0)
+ end
+ it "is idempotent" do
+ launchd "io.chef.testing.fake" do
+ program_arguments [
+ "/bin/sleep",
+ "1",
+ ]
+ type "daemon"
+ action :enable
+ end
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_be_updated
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_not_be_updated
+ end
+ it "should work if the plist does not exist" do
+ launchd "io.chef.testing.fake" do
+ type "daemon"
+ action :disable
+ end.should_not_be_updated
+ end
+ end
+end
diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb
index de6976448e..c1de3bf99d 100644
--- a/spec/functional/resource/link_spec.rb
+++ b/spec/functional/resource/link_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: John Keiser (<jkeiser@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
@@ -19,13 +19,15 @@
require "spec_helper"
if windows?
- require "chef/win32/file" #probably need this in spec_helper
+ require "chef/win32/file" # probably need this in spec_helper
+ require "chef/win32/security"
end
describe Chef::Resource::Link do
let(:file_base) { "file_spec" }
let(:expect_updated?) { true }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
# We create the files in a different directory than tmp to exercise
# different file deployment strategies more completely.
@@ -53,13 +55,22 @@ describe Chef::Resource::Link do
end
after(:each) do
- begin
- cleanup_link(to) if File.exists?(to)
- cleanup_link(target_file) if File.exists?(target_file)
- cleanup_link(CHEF_SPEC_BACKUP_PATH) if File.exists?(CHEF_SPEC_BACKUP_PATH)
- rescue
- puts "Could not remove a file: #{$!}"
- end
+
+ cleanup_link(to) if File.exist?(to)
+ cleanup_link(target_file) if File.exist?(target_file)
+ cleanup_link(CHEF_SPEC_BACKUP_PATH) if File.exist?(CHEF_SPEC_BACKUP_PATH)
+ rescue
+ puts "Could not remove a file: #{$!}"
+
+ end
+
+ def user(user)
+ node = Chef::Node.new
+ node.consume_external_attrs(ohai.data, {})
+ run_context = Chef::RunContext.new(node, {}, Chef::EventDispatch::Dispatcher.new)
+ usr = Chef::Resource.resource_for_node(:user, node).new(user, run_context)
+ usr.password("ComplexPass11!") if windows?
+ usr
end
def cleanup_link(path)
@@ -108,12 +119,49 @@ describe Chef::Resource::Link do
end
end
+ let(:test_user) { windows? ? nil : ENV["USER"] }
+
+ def expected_owner
+ if windows?
+ get_sid(test_user)
+ else
+ test_user
+ end
+ end
+
+ def get_sid(value)
+ if value.is_a?(String)
+ Chef::ReservedNames::Win32::Security::SID.from_account(value)
+ elsif value.is_a?(Chef::ReservedNames::Win32::Security::SID)
+ value
+ else
+ raise "Must specify username or SID: #{value}"
+ end
+ end
+
+ def chown(file, user)
+ if windows?
+ Chef::ReservedNames::Win32::Security::SecurableObject.new(file).owner = get_sid(user)
+ else
+ File.lchown(Etc.getpwnam(user).uid, Etc.getpwnam(user).gid, file)
+ end
+ end
+
+ def owner(file)
+ if windows?
+ Chef::ReservedNames::Win32::Security::SecurableObject.new(file).security_descriptor.owner
+ else
+ Etc.getpwuid(File.lstat(file).uid).name
+ end
+ end
+
def create_resource
node = Chef::Node.new
events = Chef::EventDispatch::Dispatcher.new
cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(cookbook_repo))
run_context = Chef::RunContext.new(node, cookbook_collection, events)
+ allow(run_context).to receive(:logger).and_return(logger)
resource = Chef::Resource::Link.new(target_file, run_context)
resource.to(to)
resource
@@ -123,7 +171,7 @@ describe Chef::Resource::Link do
create_resource
end
- describe "when supported on platform", :not_supported_on_win2k3 do
+ describe "when supported on platform" do
shared_examples_for "delete errors out" do
it "delete errors out" do
expect { resource.run_action(:delete) }.to raise_error(Chef::Exceptions::Link)
@@ -135,7 +183,7 @@ describe Chef::Resource::Link do
describe "the :delete action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:delete)
end
@@ -156,7 +204,7 @@ describe Chef::Resource::Link do
describe "the :delete action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:delete)
end
@@ -177,13 +225,14 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "links to the target file" do
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(to))
+ expect(owner(target_file)).to eq(expected_owner) unless test_user.nil?
end
it "marks the resource updated" do
expect(resource).to be_updated
@@ -198,13 +247,14 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "leaves the file linked" do
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(to))
+ expect(owner(target_file)).to eq(expected_owner) unless test_user.nil?
end
it "does not mark the resource updated" do
expect(resource).not_to be_updated
@@ -219,11 +269,11 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "preserves the hard link" do
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
# Writing to one hardlinked file should cause both
# to have the new value.
@@ -244,11 +294,11 @@ describe Chef::Resource::Link do
describe "the :create action" do
before(:each) do
@info = []
- allow(Chef::Log).to receive(:info) { |msg| @info << msg }
+ allow(logger).to receive(:info) { |msg| @info << msg }
resource.run_action(:create)
end
it "links to the target file" do
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
# Writing to one hardlinked file should cause both
# to have the new value.
@@ -288,16 +338,26 @@ describe Chef::Resource::Link do
include_context "delete succeeds"
it "the :delete action does not delete the target file" do
resource.run_action(:delete)
- expect(File.exists?(to)).to be_truthy
+ expect(File.exist?(to)).to be_truthy
end
end
- context "pointing somewhere else" do
+ context "pointing somewhere else", :requires_root_or_running_windows do
+ let(:test_user) { "test-link-user" }
+ before do
+ user(test_user).run_action(:create)
+ end
+ after do
+ user(test_user).run_action(:remove)
+ end
before(:each) do
+ resource.owner(test_user)
@other_target = File.join(test_file_dir, make_tmpname("other_spec"))
File.open(@other_target, "w") { |file| file.write("eek") }
symlink(@other_target, target_file)
+ chown(target_file, test_user)
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
+ expect(owner(target_file)).to eq(expected_owner)
end
after(:each) do
File.delete(@other_target)
@@ -306,7 +366,7 @@ describe Chef::Resource::Link do
include_context "delete succeeds"
it "the :delete action does not delete the target file" do
resource.run_action(:delete)
- expect(File.exists?(to)).to be_truthy
+ expect(File.exist?(to)).to be_truthy
end
end
context "pointing nowhere" do
@@ -323,7 +383,7 @@ describe Chef::Resource::Link do
context "and the link already exists and is a hard link to the file" do
before(:each) do
link(to, target_file)
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
end
include_context "create symbolic link succeeds"
@@ -343,7 +403,7 @@ describe Chef::Resource::Link do
it "create errors out" do
if windows?
expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
- elsif os_x? || solaris? || freebsd? || aix?
+ elsif macos? || solaris? || freebsd? || aix?
expect { resource.run_action(:create) }.to raise_error(Errno::EPERM)
else
expect { resource.run_action(:create) }.to raise_error(Errno::EISDIR)
@@ -354,11 +414,11 @@ describe Chef::Resource::Link do
it_behaves_like "a securable resource without existing target" do
let(:path) { target_file }
- def allowed_acl(sid, expected_perms)
+ def allowed_acl(sid, expected_perms, _flags = 0)
[ ACE.access_allowed(sid, expected_perms[:specific]) ]
end
- def denied_acl(sid, expected_perms)
+ def denied_acl(sid, expected_perms, _flags = 0)
[ ACE.access_denied(sid, expected_perms[:specific]) ]
end
@@ -522,14 +582,14 @@ describe Chef::Resource::Link do
context "and the link already exists and is a hard link to the file" do
before(:each) do
link(to, target_file)
- expect(File.exists?(target_file)).to be_truthy
+ expect(File.exist?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_falsey
end
include_context "create hard link is noop"
include_context "delete succeeds"
it "the :delete action does not delete the target file" do
resource.run_action(:delete)
- expect(File.exists?(to)).to be_truthy
+ expect(File.exist?(to)).to be_truthy
end
end
context "and the link already exists and is a file" do
@@ -546,7 +606,7 @@ describe Chef::Resource::Link do
it "errors out" do
if windows?
expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
- elsif os_x? || solaris? || freebsd? || aix?
+ elsif macos? || solaris? || freebsd? || aix?
expect { resource.run_action(:create) }.to raise_error(Errno::EPERM)
else
expect { resource.run_action(:create) }.to raise_error(Errno::EISDIR)
@@ -593,10 +653,10 @@ describe Chef::Resource::Link do
end
context "and the link does not yet exist" do
it "links to the target file" do
- skip("OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks") if os_x? || freebsd? || aix?
+ skip("macOS/FreeBSD/AIX/Solaris symlink? and readlink working on hard links to symlinks") if macos? || freebsd? || aix? || solaris?
resource.run_action(:create)
- expect(File.exists?(target_file)).to be_truthy
- # OS X gets angry about this sort of link. Bug in OS X, IMO.
+ expect(File.exist?(target_file)).to be_truthy
+ # macOS gets angry about this sort of link. Bug in macOS, IMO.
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
@@ -612,9 +672,9 @@ describe Chef::Resource::Link do
end
context "and the link does not yet exist" do
it "links to the target file" do
- skip("OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks") if os_x? || freebsd? || aix?
+ skip("macOS/FreeBSD/AIX/Solaris fails to create hardlinks to broken symlinks") if macos? || freebsd? || aix? || solaris?
resource.run_action(:create)
- expect(File.exists?(target_file) || File.symlink?(target_file)).to be_truthy
+ expect(File.exist?(target_file) || File.symlink?(target_file)).to be_truthy
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
@@ -633,10 +693,4 @@ describe Chef::Resource::Link do
end
end
end
-
- describe "when not supported on platform", :win2k3_only do
- it "raises error" do
- expect { resource }.to raise_error(Chef::Exceptions::Win32APIFunctionNotImplemented)
- end
- end
end
diff --git a/spec/functional/resource/locale_spec.rb b/spec/functional/resource/locale_spec.rb
new file mode 100644
index 0000000000..5258f08696
--- /dev/null
+++ b/spec/functional/resource/locale_spec.rb
@@ -0,0 +1,108 @@
+#
+# Author:: Nimesh Patni (<nimesh.patni@msystechnologies.com>)
+# Copyright:: Copyright (c) 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::Locale, :requires_root do
+ let(:node) do
+ n = Chef::Node.new
+ n.consume_external_attrs(OHAI_SYSTEM.data, {})
+ n
+ end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::Locale.new("fakey_fakerton", run_context) }
+
+ context "on debian/ubuntu", :debian_family_only do
+ def sets_system_locale(*locales)
+ system_locales = File.readlines("/etc/locale.conf")
+ expect(system_locales.map(&:strip)).to eq(locales)
+ end
+
+ def unsets_system_locale(*locales)
+ system_locales = File.readlines("/etc/locale.conf")
+ expect(system_locales.map(&:strip)).not_to eq(locales)
+ end
+
+ describe "action: update" do
+ context "Sets system variable" do
+ it "when LC var is given" do
+ resource.lc_env({ "LC_MESSAGES" => "en_US" })
+ resource.run_action(:update)
+ sets_system_locale("LC_MESSAGES=en_US")
+ end
+ it "when lang is given" do
+ resource.lang("en_US")
+ resource.run_action(:update)
+ sets_system_locale("LANG=en_US")
+ end
+ it "when both lang & LC vars are given" do
+ resource.lang("en_US")
+ resource.lc_env({ "LC_TIME" => "en_IN" })
+ resource.run_action(:update)
+ sets_system_locale("LANG=en_US", "LC_TIME=en_IN")
+ end
+ end
+
+ context "Unsets system variable" do
+ it "when LC var is not given" do
+ resource.lc_env
+ resource.run_action(:update)
+ unsets_system_locale("LC_MESSAGES=en_US")
+ end
+ it "when lang is not given" do
+ resource.lang
+ resource.run_action(:update)
+ unsets_system_locale("LANG=en_US")
+ end
+ it "when both lang & LC vars are not given" do
+ resource.lang
+ resource.lc_env
+ resource.run_action(:update)
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+ end
+ end
+
+ context "on rhel", :rhel do
+ it "raises an exception due lacking the locale-gen tool" do
+ resource.lang("en_US")
+ expect { resource.run_action(:update) }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ end
+ end
+
+ context "on macos", :macos_only do
+ it "raises an exception due to being an unsupported platform" do
+ resource.lang("en_US")
+ expect { resource.run_action(:update) }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ end
+ end
+
+ # @TODO we need to enable these again
+ # context "on windows", :windows_only, requires_root: false do
+ # describe "action: update" do
+ # context "Sets system locale" do
+ # it "when lang is given" do
+ # resource.lang("en-US")
+ # resource.run_action(:update)
+ # end
+ # end
+ # end
+ # end
+end
diff --git a/spec/functional/resource/mount_spec.rb b/spec/functional/resource/mount_spec.rb
index c756b0d3d4..f11f97d6c7 100644
--- a/spec/functional/resource/mount_spec.rb
+++ b/spec/functional/resource/mount_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -17,33 +17,32 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
require "tmpdir"
# run this test only for following platforms.
-include_flag = !(%w{ubuntu centos aix solaris2}.include?(ohai[:platform]))
-
-describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => include_flag do
- # Disabled in travis because it refuses to let us mount a ramdisk. /dev/ramX does not
- # exist even after loading the kernel module
+include_flag = !(%w{debian rhel amazon aix solaris2}.include?(ohai[:platform_family]))
+describe Chef::Resource::Mount, :requires_root, external: include_flag do
include Chef::Mixin::ShellOut
# Platform specific setup, cleanup and validation helpers.
-
def setup_device_for_mount
# use ramdisk for creating a test device for mount.
# This can cleaner if we have chef resource/provider for ramdisk.
- case ohai[:platform]
+ case ohai[:platform_family]
when "aix"
# On AIX, we can't create a ramdisk inside a WPAR, so we use
# a "namefs" mount against / to test
# https://www-304.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.performance/namefs_file_sys.htm
device = "/"
fstype = "namefs"
- when "ubuntu", "centos"
+ when "debian", "rhel", "amazon"
device = "/dev/ram1"
+ unless File.exist?(device)
+ shell_out("mknod -m 660 #{device} b 1 0")
+ shell_out("chown root:disk #{device}")
+ end
shell_out("ls -1 /dev/ram*").stdout.each_line do |d|
if shell_out("mount | grep #{d}").exitstatus == "1"
# this device is not mounted, so use it.
@@ -69,7 +68,7 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
def mount_should_exist(mount_point, device, fstype = nil, options = nil)
validation_cmd = "mount | grep #{mount_point} | grep #{device} "
validation_cmd << " | grep #{fstype} " unless fstype.nil?
- validation_cmd << " | grep #{options.join(',')} " unless options.nil? || options.empty?
+ validation_cmd << " | grep #{options.join(",")} " unless options.nil? || options.empty?
expect(shell_out(validation_cmd).exitstatus).to eq(0)
end
@@ -101,6 +100,15 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
expect(shell_out("cat #{unix_mount_config_file}").stdout).not_to include("#{mount_point}:")
end
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
let(:new_resource) do
new_resource = Chef::Resource::Mount.new(@mount_point, run_context)
new_resource.device @device
@@ -121,8 +129,9 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
end
# Actual tests begin here.
- before(:all) do
+ before do |test|
@device, @fstype = setup_device_for_mount
+ @device = "/" if test.metadata[:skip_before]
@mount_point = Dir.mktmpdir("testmount")
@@ -137,13 +146,20 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
end
after(:all) do
- Dir.rmdir(@mount_point)
+ Dir.rmdir(@mount_point) if @mount_point
end
after(:each) do
cleanup_mount(new_resource.mount_point)
end
+ describe "when device is '/'" do
+ it "should mount the filesystem if device is '/'", :skip_before do
+ new_resource.run_action(:mount)
+ mount_should_exist(new_resource.mount_point, new_resource.device)
+ end
+ end
+
describe "when the target state is a mounted filesystem" do
it "should mount the filesystem if it isn't mounted" do
expect(current_resource.enabled).to be_falsey
@@ -157,7 +173,7 @@ describe Chef::Resource::Mount, :requires_root, :skip_travis, :external => inclu
# don't run the remount tests on solaris2 (tmpfs does not support remount)
# Need to make sure the platforms we've already excluded are considered:
skip_remount = include_flag || (ohai[:platform] == "solaris2")
- describe "when the filesystem should be remounted and the resource supports remounting", :external => skip_remount do
+ describe "when the filesystem should be remounted and the resource supports remounting", external: skip_remount do
it "should remount the filesystem if it is mounted" do
new_resource.run_action(:mount)
mount_should_exist(new_resource.mount_point, new_resource.device)
diff --git a/spec/functional/resource/msu_package_spec.rb b/spec/functional/resource/msu_package_spec.rb
new file mode 100644
index 0000000000..2e6fcdcbca
--- /dev/null
+++ b/spec/functional/resource/msu_package_spec.rb
@@ -0,0 +1,107 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "chef/provider/package/cab"
+
+describe Chef::Resource::MsuPackage, :win2012r2_only do
+
+ let(:package_name) { "Package_for_KB2959977" }
+ let(:package_source) { "https://download.microsoft.com/download/3/B/3/3B320C07-B7B1-41E5-81F4-79EBC02DF7D3/Windows8.1-KB2959977-x64.msu" }
+ let(:package_identity) { "Package_for_KB2959977~31bf3856ad364e35~amd64~~6.3.1.1" }
+ let(:timeout) { 3600 }
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
+ let(:new_resource) { Chef::Resource::CabPackage.new("windows_test_pkg") }
+ let(:cab_provider) do
+ Chef::Provider::Package::Cab.new(new_resource, run_context)
+ end
+
+ subject do
+ new_resource = Chef::Resource::MsuPackage.new("test msu package", run_context)
+ new_resource.package_name package_name
+ new_resource.source package_source
+ new_resource.timeout timeout
+ new_resource
+ end
+
+ context "installing package" do
+ after { remove_package }
+
+ it "installs the package successfully" do
+ subject.run_action(:install)
+ found_packages = cab_provider.installed_packages.select { |p| p["package_identity"] == package_identity }
+ expect(found_packages.length).to be == 1
+ end
+ end
+
+ context "removing a package" do
+ it "removes an installed package" do
+ subject.run_action(:install)
+ remove_package
+ found_packages = cab_provider.installed_packages.select { |p| p["package_identity"] == package_identity }
+ expect(found_packages.length).to be == 0
+ end
+ end
+
+ context "when an invalid msu package is given" do
+ def package_name
+ "Package_for_KB2859903"
+ end
+
+ def package_source
+ "https://download.microsoft.com/download/5/2/B/52BE95BF-22D8-4415-B644-0FDF398F6D96/IE10-Windows6.1-KB2859903-x86.msu"
+ end
+
+ it "raises error while installing" do
+ expect { subject.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "raises error while removing" do
+ expect { subject.run_action(:remove) }.to raise_error(Chef::Exceptions::Package)
+ end
+ end
+
+ context "when an msu package is not applicable to the image." do
+ def package_name
+ "Package_for_KB4019990"
+ end
+
+ def package_source
+ "http://download.windowsupdate.com/c/msdownload/update/software/updt/2017/05/windows8-rt-kb4019990-x64_a77f4e3e1f2d47205824763e7121bb11979c2716.msu"
+ end
+
+ it "raises error while installing" do
+ expect { subject.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /The specified package is not applicable to this image./)
+ end
+ end
+
+ def remove_package
+ pkg_to_remove = Chef::Resource::MsuPackage.new(package_name, run_context)
+ pkg_to_remove.source = package_source
+ pkg_to_remove.run_action(:remove)
+ end
+end
diff --git a/spec/functional/resource/ohai_spec.rb b/spec/functional/resource/ohai_spec.rb
index 06bccfc398..836a4f6da3 100644
--- a/spec/functional/resource/ohai_spec.rb
+++ b/spec/functional/resource/ohai_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright 2014-2016, 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");
diff --git a/spec/functional/resource/powershell_package_source_spec.rb b/spec/functional/resource/powershell_package_source_spec.rb
new file mode 100644
index 0000000000..abc9dcabd1
--- /dev/null
+++ b/spec/functional/resource/powershell_package_source_spec.rb
@@ -0,0 +1,107 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 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 "chef/mixin/powershell_exec"
+
+describe Chef::Resource::PowershellPackageSource, :windows_gte_10 do
+ include Chef::Mixin::PowershellExec
+
+ let(:source_name) { "fake" }
+ let(:url) { "https://www.nuget.org/api/v2" }
+ let(:trusted) { true }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ subject do
+ new_resource = Chef::Resource::PowershellPackageSource.new("test powershell package source", run_context)
+ new_resource.source_name source_name
+ new_resource.url url
+ new_resource.trusted trusted
+ new_resource.provider_name provider_name
+ new_resource
+ end
+
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
+ shared_examples "package_source" do
+ context "register a package source" do
+ after { remove_package_source }
+
+ it "registers the package source" do
+ subject.run_action(:register)
+ expect(get_installed_package_source_name).to eq(source_name)
+ end
+
+ it "does not register the package source if already installed" do
+ subject.run_action(:register)
+ subject.run_action(:register)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates an existing package source if changed" do
+ subject.run_action(:register)
+ subject.trusted !trusted
+ subject.run_action(:register)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ context "unregister a package source" do
+ it "unregisters the package source" do
+ subject.run_action(:register)
+ subject.run_action(:unregister)
+ expect(get_installed_package_source_name).to be_empty
+ end
+
+ it "does not unregister the package source if not already installed" do
+ subject.run_action(:unregister)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "with NuGet provider" do
+ let(:provider_name) { "NuGet" }
+
+ before(:all) do
+ powershell_exec!("[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;Install-PackageProvider -Name NuGet -Force")
+ end
+
+ it_behaves_like "package_source"
+ end
+
+ context "with PowerShellGet provider" do
+ let(:provider_name) { "PowerShellGet" }
+
+ it_behaves_like "package_source"
+ end
+
+ def get_installed_package_source_name
+ powershell_exec!("(Get-PackageSource -Name #{source_name} -ErrorAction SilentlyContinue).Name").result
+ end
+
+ def remove_package_source
+ pkg_to_remove = Chef::Resource::PowershellPackageSource.new(source_name, run_context)
+ pkg_to_remove.run_action(:unregister)
+ end
+end \ No newline at end of file
diff --git a/spec/functional/resource/powershell_script_spec.rb b/spec/functional/resource/powershell_script_spec.rb
index af345b0ea4..68fa94afe9 100644
--- a/spec/functional/resource/powershell_script_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Edwards (<adamed@chef.io>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -23,33 +23,31 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
include_context Chef::Resource::WindowsScript
- let (:architecture_command) { "echo $env:PROCESSOR_ARCHITECTURE" }
- let (:output_command) { " | out-file -encoding ASCII " }
+ let(:architecture_command) { "echo $env:PROCESSOR_ARCHITECTURE" }
+ let(:output_command) { " | out-file -encoding ASCII " }
it_behaves_like "a Windows script running on Windows"
- let(:successful_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" }
- let(:failed_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe /badargument" }
+ let(:successful_executable_script_content) { "#{ENV["SystemRoot"]}\\system32\\attrib.exe $env:systemroot" }
+ let(:failed_executable_script_content) { "#{ENV["SystemRoot"]}\\system32\\attrib.exe /badargument" }
let(:processor_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTURE" }
let(:native_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTUREW6432" }
let(:cmdlet_exit_code_not_found_content) { "get-item '.\\thisdoesnotexist'" }
let(:cmdlet_exit_code_success_content) { "get-item ." }
- let(:windows_process_exit_code_success_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" }
+ let(:windows_process_exit_code_success_content) { "#{ENV["SystemRoot"]}\\system32\\attrib.exe $env:systemroot" }
let(:windows_process_exit_code_not_found_content) { "findstr /notavalidswitch" }
- # Note that process exit codes on 32-bit Win2k3 cannot
- # exceed maximum value of signed integer
let(:arbitrary_nonzero_process_exit_code) { 4193 }
let(:arbitrary_nonzero_process_exit_code_content) { "exit #{arbitrary_nonzero_process_exit_code}" }
let(:invalid_powershell_interpreter_flag) { "/thisflagisinvalid" }
let(:valid_powershell_interpreter_flag) { "-Sta" }
let!(:resource) do
- r = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context)
+ r = Chef::Resource::WindowsScript::PowershellScript.new("PowerShell resource functional test", @run_context)
r.code(successful_executable_script_content)
r
end
- describe "when the run action is invoked on Windows" do
+ shared_examples_for "a running powershell script" do
it "successfully executes a non-cmdlet Windows binary as the last command of the script" do
resource.code(successful_executable_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.returns(0)
@@ -57,8 +55,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns the exit status 27 for a powershell script that exits with 27" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
file = Tempfile.new(["foo", ".ps1"])
begin
file.write "exit 27"
@@ -72,10 +68,9 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
end
- let (:negative_exit_status) { -27 }
- let (:unsigned_exit_status) { (-negative_exit_status ^ 65535) + 1 }
+ let(:negative_exit_status) { -27 }
+ let(:unsigned_exit_status) { (-negative_exit_status ^ 65535) + 1 }
it "returns the exit status -27 as a signed integer or an unsigned 16-bit 2's complement value of 65509 for a powershell script that exits with -27" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
# Versions of PowerShell prior to 4.0 return a 16-bit unsigned value --
# PowerShell 4.0 and later versions return a 32-bit signed value.
@@ -100,8 +95,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns the process exit code" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code(arbitrary_nonzero_process_exit_code_content)
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
@@ -120,34 +113,24 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 if the last command was a cmdlet that failed" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code(cmdlet_exit_code_not_found_content)
resource.returns(1)
resource.run_action(:run)
end
it "returns 1 if the last command was a cmdlet that failed and was preceded by a successfully executed non-cmdlet Windows binary" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([windows_process_exit_code_success_content, cmdlet_exit_code_not_found_content].join(";"))
resource.returns(1)
expect { resource.run_action(:run) }.not_to raise_error
end
it "raises a Mixlib::ShellOut::ShellCommandFailed error if the script is not syntactically correct" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code("if({)")
resource.returns(0)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
it "raises an error if the script is not syntactically correct even if returns is set to 1 which is what powershell.exe returns for syntactically invalid scripts" do
- # This test fails because shell_out expects the exit status to be 1, but it is actually 0
- # The error is a false-positive.
- skip "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code("if({)")
resource.returns(1)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
@@ -156,38 +139,30 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
# This somewhat ambiguous case, two failures of different types,
# seems to violate the principle of returning the status of the
# last line executed -- in this case, we return the status of the
- # second to last line. This happens because Powershell gives no
+ # second to last line. This happens because PowerShell gives no
# way for us to determine whether the last operation was a cmdlet
# or Windows process. Because the latter gives more specific
# errors than 0 or 1, we return that instead, which is acceptable
# since callers can test for nonzero rather than testing for 1.
it "returns 1 if the last command was a cmdlet that failed and was preceded by an unsuccessfully executed non-cmdlet Windows binary" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([arbitrary_nonzero_process_exit_code_content, cmdlet_exit_code_not_found_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns 0 if the last command was a non-cmdlet Windows binary that succeeded and was preceded by a failed cmdlet" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that succeeded" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that failed" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.code([cmdlet_exit_code_not_found_content, arbitrary_nonzero_process_exit_code_content].join(";"))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
@@ -206,8 +181,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 for $false as the last line of the script when convert_boolean_return is true" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.convert_boolean_return true
resource.code "$false"
resource.returns(1)
@@ -233,43 +206,79 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
expect(is_64_bit).to eq(detected_64_bit)
end
- it "returns 1 if an invalid flag is passed to the interpreter" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
+ it "returns 0 if a valid flag is passed to the interpreter" do
resource.code(cmdlet_exit_code_success_content)
- resource.flags(invalid_powershell_interpreter_flag)
- resource.returns(1)
+ resource.flags(valid_powershell_interpreter_flag)
+ resource.returns(0)
resource.run_action(:run)
end
- it "returns 0 if a valid flag is passed to the interpreter" do
+ it "returns 0 if no flag is passed to the interpreter" do
resource.code(cmdlet_exit_code_success_content)
- resource.flags(valid_powershell_interpreter_flag)
resource.returns(0)
resource.run_action(:run)
end
+ it "returns 1 if an invalid flag is passed to the interpreter" do
+ resource.code(cmdlet_exit_code_success_content)
+ resource.flags(invalid_powershell_interpreter_flag)
+ resource.returns(1)
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
it "raises an error when given a block and a guard_interpreter" do
resource.guard_interpreter :sh
resource.only_if { true }
expect { resource.should_skip?(:run) }.to raise_error(ArgumentError, /guard_interpreter does not support blocks/)
end
+ end
- context "when dsc is supported", :windows_powershell_dsc_only do
- it "can execute LCM configuration code" do
- resource.code <<-EOF
-configuration LCM
-{
- param ($thumbprint)
- localconfigurationmanager
- {
- RebootNodeIfNeeded = $false
- ConfigurationMode = 'ApplyOnly'
- }
-}
- EOF
- expect { resource.run_action(:run) }.not_to raise_error
- end
+ context "when using the powershell interpreter" do
+ before do
+ resource.interpreter "powershell"
+ end
+
+ it_behaves_like "a running powershell script"
+
+ it "runs Windows Powershell" do
+ resource.code("$PSVersionTable.PSVersion.Major | out-file -encoding ASCII #{script_output_path}")
+ resource.returns(0)
+ resource.run_action(:run)
+
+ expect(get_script_output.to_i).to be < 6
+ end
+ end
+
+ context "when using the pwsh interpreter", :pwsh_installed do
+ before do
+ resource.interpreter "pwsh"
+ end
+
+ it_behaves_like "a running powershell script"
+
+ it "runs a version of powershell greater than 6" do
+ resource.code("$PSVersionTable.PSVersion.Major | out-file -encoding ASCII #{script_output_path}")
+ resource.returns(0)
+ resource.run_action(:run)
+
+ expect(get_script_output.to_i).to be > 6
+ end
+ end
+
+ context "when dsc is supported", :windows_powershell_dsc_only do
+ it "can execute LCM configuration code" do
+ resource.code <<~EOF
+ configuration LCM
+ {
+ param ($thumbprint)
+ localconfigurationmanager
+ {
+ RebootNodeIfNeeded = $false
+ ConfigurationMode = 'ApplyOnly'
+ }
+ }
+ EOF
+ expect { resource.run_action(:run) }.not_to raise_error
end
end
@@ -296,10 +305,10 @@ configuration LCM
context "when running on a 32-bit version of Windows", :windows32_only do
it "raises an exception if :x86_64 process architecture is specified" do
- begin
- expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
- rescue Chef::Exceptions::Win32ArchitectureIncorrect
- end
+
+ expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
+ rescue Chef::Exceptions::Win32ArchitectureIncorrect
+
end
end
end
@@ -314,7 +323,7 @@ configuration LCM
expect(source_contains_case_insensitive_content?( get_script_output, "AMD64" )).to eq(true)
end
- it "executes a script with a 32-bit process if :i386 arch is specified", :not_supported_on_nano do
+ it "executes a script with a 32-bit process if :i386 arch is specified" do
resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.architecture(:i386)
resource.returns(0)
@@ -322,12 +331,6 @@ configuration LCM
expect(source_contains_case_insensitive_content?( get_script_output, "x86" )).to eq(true)
end
-
- it "raises an error when executing a script with a 32-bit process on Windows Nano Server", :windows_nano_only do
- resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
- expect { resource.architecture(:i386) }.to raise_error(Chef::Exceptions::Win32ArchitectureIncorrect,
- "cannot execute script with requested architecture 'i386' on Windows Nano Server")
- end
end
describe "when executing guards" do
@@ -376,13 +379,22 @@ configuration LCM
context "with powershell_script as the guard_interpreter" do
+ context "when pwsh is the interpreter", :pwsh_installed do
+ before do
+ resource.interpreter "pwsh"
+ end
+
+ it "uses powershell core to evaluate the guard" do
+ resource.not_if "$PSVersionTable.PSEdition -eq 'Core'"
+ expect(resource.should_skip?(:run)).to be_truthy
+ end
+ end
+
it "has a guard_interpreter attribute set to :powershell_script" do
expect(resource.guard_interpreter).to eq(:powershell_script)
end
it "evaluates a powershell $false for a not_if block as true" do
- pending "powershell.exe always exits with $true on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if "$false"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -393,8 +405,6 @@ configuration LCM
end
it "evaluates a powershell $false for an only_if block as false" do
- pending "powershell.exe always exits with $true on nano" if Chef::Platform.windows_nano_server?
-
resource.only_if "$false"
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -415,8 +425,6 @@ configuration LCM
end
it "evaluates a non-zero powershell exit status for not_if as true" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if "exit 37"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -427,8 +435,6 @@ configuration LCM
end
it "evaluates a failed executable exit status for not_if as false" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if windows_process_exit_code_not_found_content
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -439,8 +445,6 @@ configuration LCM
end
it "evaluates a failed executable exit status for only_if as false" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.only_if windows_process_exit_code_not_found_content
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -451,8 +455,6 @@ configuration LCM
end
it "evaluates a failed cmdlet exit status for not_if as true" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.not_if "throw 'up'"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -463,8 +465,6 @@ configuration LCM
end
it "evaluates a failed cmdlet exit status for only_if as false" do
- pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
-
resource.only_if "throw 'up'"
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -475,26 +475,26 @@ configuration LCM
end
it "evaluates a not_if block using the cwd guard parameter" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
- resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
+ resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", cwd: custom_cwd
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates an only_if block using the cwd guard parameter" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
- resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
+ resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", cwd: custom_cwd
expect(resource.should_skip?(:run)).to be_falsey
end
it "inherits cwd from the parent resource for only_if" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
resource.cwd custom_cwd
resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')"
expect(resource.should_skip?(:run)).to be_falsey
end
it "inherits cwd from the parent resource for not_if" do
- custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc"
+ custom_cwd = "#{ENV["SystemRoot"]}\\system32\\drivers\\etc"
resource.cwd custom_cwd
resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')"
expect(resource.should_skip?(:run)).to be_truthy
@@ -507,36 +507,30 @@ configuration LCM
end
it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean true as nonzero status code", :windows64_only do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.architecture :x86_64
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'AMD64')"
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code" do
resource.architecture :i386
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'X86')"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code" do
resource.architecture :i386
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'X86')"
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for only_if" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.convert_boolean_return true
resource.only_if "$false"
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for not_if" do
- pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
-
resource.convert_boolean_return true
resource.not_if "$false"
expect(resource.should_skip?(:run)).to be_falsey
@@ -554,40 +548,33 @@ configuration LCM
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.only_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.not_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.only_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if", :not_supported_on_nano do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if" do
resource.convert_boolean_return true
resource.architecture :i386
resource.not_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
expect(resource.should_skip?(:run)).to be_truthy
end
-
- it "raises an error when a 32-bit guard is used on Windows Nano Server", :windows_nano_only do
- resource.only_if "$true", :architecture => :i386
- expect { resource.run_action(:run) }.to raise_error(
- Chef::Exceptions::Win32ArchitectureIncorrect,
- /cannot execute script with requested architecture 'i386' on Windows Nano Server/)
- end
end
end
diff --git a/spec/functional/resource/reboot_spec.rb b/spec/functional/resource/reboot_spec.rb
index c264b122a7..5489dc1c72 100644
--- a/spec/functional/resource/reboot_spec.rb
+++ b/spec/functional/resource/reboot_spec.rb
@@ -22,9 +22,9 @@ describe Chef::Resource::Reboot do
let(:expected) do
{
- :delay_mins => 5,
- :requested_by => "reboot resource functional test",
- :reason => "reboot resource spec test",
+ delay_mins: 5,
+ requested_by: "reboot resource functional test",
+ reason: "reboot resource spec test",
}
end
@@ -45,7 +45,7 @@ describe Chef::Resource::Reboot do
shared_context "testing run context modification" do
def test_reboot_action(resource)
reboot_info = resource.run_context.reboot_info
- expect(reboot_info.keys.sort).to eq([:delay_mins, :reason, :requested_by, :timestamp])
+ expect(reboot_info.keys.sort).to eq(%i{delay_mins reason requested_by timestamp})
expect(reboot_info[:delay_mins]).to eq(expected[:delay_mins])
expect(reboot_info[:reason]).to eq(expected[:reason])
expect(reboot_info[:requested_by]).to eq(expected[:requested_by])
diff --git a/spec/functional/resource/registry_spec.rb b/spec/functional/resource/registry_spec.rb
index 8393e0a074..f3ad946f0e 100644
--- a/spec/functional/resource/registry_spec.rb
+++ b/spec/functional/resource/registry_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Author:: Lamont Granquist (<lamont@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
@@ -34,13 +34,13 @@ describe Chef::Resource::RegistryKey, :unix_only do
context "when load_current_resource is run on a non-windows node" do
it "throws an exception because you don't have a windows registry (derp)" do
@resource.key("HKCU\\Software\\Opscode")
- @resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @resource.values([{ name: "Color", type: :string, data: "Orange" }])
expect { @resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32NotWindows)
end
end
end
-describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
+describe Chef::Resource::RegistryKey, :windows_only, broken: true do
# parent and key must be single keys, not paths
let(:parent) { "Opscode" }
@@ -107,14 +107,14 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
reset_registry
end
- #Reporting setup
+ # Reporting setup
before do
@node.name("windowsbox")
@rest_client = double("Chef::ServerAPI (mock)")
- allow(@rest_client).to receive(:create_url).and_return("reports/nodes/windowsbox/runs/#{@run_id}");
- allow(@rest_client).to receive(:raw_http_request).and_return({ "result" => "ok" });
- allow(@rest_client).to receive(:post_rest).and_return({ "uri" => "https://example.com/reports/nodes/windowsbox/runs/#{@run_id}" });
+ allow(@rest_client).to receive(:create_url).and_return("reports/nodes/windowsbox/runs/#{@run_id}")
+ allow(@rest_client).to receive(:raw_http_request).and_return({ "result" => "ok" })
+ allow(@rest_client).to receive(:post_rest).and_return({ "uri" => "https://example.com/reports/nodes/windowsbox/runs/#{@run_id}" })
@resource_reporter = Chef::ResourceReporter.new(@rest_client)
@events.register(@resource_reporter)
@@ -123,104 +123,128 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
@run_id = @resource_reporter.run_id
@new_resource.cookbook_name = "monkey"
- @cookbook_version = double("Cookbook::Version", :version => "1.2.3")
- allow(@new_resource).to receive(:cookbook_version).and_return(@cookbook_version)
+ @cookbook_version = double("Cookbook::Version", version: "1.2.3")
+ @new_resource.cookbook_version(@cookbook_version)
end
- after (:all) do
+ after(:all) do
clean_registry
end
context "when action is create" do
- before (:all) do
+ before(:all) do
reset_registry
end
it "creates registry key, value if the key is missing" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Orange" })).to eq(true)
end
it "does not create the key if it already exists with same value, type and data" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Orange" })).to eq(true)
end
it "does not create the key if it already exists with same value and type but datatype of data differs" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "number", :type => :dword, :data => "12345" }])
+ @new_resource.values([{ name: "number", type: :dword, data: "12345" }])
@new_resource.run_action(:create)
expect(@new_resource).not_to be_updated_by_last_action
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "number", :type => :dword, :data => 12344 })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "number", type: :dword, data: 12344 })).to eq(true)
end
it "creates a value if it does not exist" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Mango", :type => :string, :data => "Yellow" }])
+ @new_resource.values([{ name: "Mango", type: :string, data: "Yellow" }])
@new_resource.run_action(:create)
- expect(@registry.data_exists?(reg_child, { :name => "Mango", :type => :string, :data => "Yellow" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Mango", type: :string, data: "Yellow" })).to eq(true)
end
it "modifies the data if the key and value exist and type matches" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Not just Orange - OpscodeOrange!" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Not just Orange - OpscodeOrange!" }])
@new_resource.run_action(:create)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Not just Orange - OpscodeOrange!" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Not just Orange - OpscodeOrange!" })).to eq(true)
end
it "modifys the type if the key and value exist and the type does not match" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :multi_string, :data => ["Not just Orange - OpscodeOrange!"] }])
+ @new_resource.values([{ name: "Color", type: :multi_string, data: ["Not just Orange - OpscodeOrange!"] }])
@new_resource.run_action(:create)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :multi_string, :data => ["Not just Orange - OpscodeOrange!"] })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :multi_string, data: ["Not just Orange - OpscodeOrange!"] })).to eq(true)
end
it "creates subkey if parent exists" do
@new_resource.key(reg_child + '\OpscodeTest')
- @new_resource.values([{ :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} }])
+ @new_resource.values([{ name: "Chef", type: :multi_string, data: %w{OpscodeOrange Rules} }])
@new_resource.recursive(false)
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child + '\OpscodeTest')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\OpscodeTest', { :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} })).to eq(true)
+ expect(@registry.value_exists?(reg_child + '\OpscodeTest', { name: "Chef", type: :multi_string, data: %w{OpscodeOrange Rules} })).to eq(true)
end
- it "gives error if action create and parent does not exist and recursive is set to false" do
+ it "raises an error if action create and parent does not exist and recursive is set to false" do
@new_resource.key(reg_child + '\Missing1\Missing2')
- @new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "MissingData" }])
@new_resource.recursive(false)
expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
end
+ it "raises an error if action create and type key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC", data: "my_data" }])
+ expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "raises an error if action create and data key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC", type: :string }])
+ expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::RegKeyValuesDataMissing)
+ end
+
+ it "raises an error if action create and only name key present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC" }])
+ expect { @new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "does not raise an error if action create and all keys are present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC", type: :string, data: "my_data" }])
+ expect { @new_resource.run_action(:create) }.to_not raise_error
+ end
+
it "creates missing keys if action create and parent does not exist and recursive is set to true" do
@new_resource.key(reg_child + '\Missing1\Missing2')
- @new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "MissingData" }])
@new_resource.recursive(true)
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child + '\Missing1\Missing2')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\Missing1\Missing2', { :name => "OC", :type => :string, :data => "MissingData" })).to eq(true)
+ expect(@registry.value_exists?(reg_child + '\Missing1\Missing2', { name: "OC", type: :string, data: "MissingData" })).to eq(true)
end
it "creates key with multiple value as specified" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "one", :type => :string, :data => "1" }, { :name => "two", :type => :string, :data => "2" }, { :name => "three", :type => :string, :data => "3" }])
+ @new_resource.values([{ name: "one", type: :string, data: "1" }, { name: "two", type: :string, data: "2" }, { name: "three", type: :string, data: "3" }])
@new_resource.recursive(true)
@new_resource.run_action(:create)
- @new_resource.values.each do |value|
+ @new_resource.each_value do |value|
expect(@registry.value_exists?(reg_child, value)).to eq(true)
end
end
@@ -235,12 +259,12 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
it "creates a key in a 32-bit registry that is not viewable in 64-bit" do
@new_resource.key(reg_child + '\Atraxi' )
- @new_resource.values([{ :name => "OC", :type => :string, :data => "Data" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "Data" }])
@new_resource.recursive(true)
@new_resource.architecture(:i386)
@new_resource.run_action(:create)
@registry.architecture = :i386
- expect(@registry.data_exists?(reg_child + '\Atraxi', { :name => "OC", :type => :string, :data => "Data" })).to eq(true)
+ expect(@registry.data_exists?(reg_child + '\Atraxi', { name: "OC", type: :string, data: "Data" })).to eq(true)
@registry.architecture = :x86_64
expect(@registry.key_exists?(reg_child + '\Atraxi')).to eq(false)
end
@@ -248,7 +272,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
it "prepares the reporting data for action :create" do
@new_resource.key(reg_child + '\Ood')
- @new_resource.values([{ :name => "ReportingVal1", :type => :string, :data => "report1" }, { :name => "ReportingVal2", :type => :string, :data => "report2" }])
+ @new_resource.values([{ name: "ReportingVal1", type: :string, data: "report1" }, { name: "ReportingVal2", type: :string, data: "report2" }])
@new_resource.recursive(true)
@new_resource.run_action(:create)
@report = @resource_reporter.prepare_run_data
@@ -257,8 +281,8 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_child + '\Ood')
- expect(@report["resources"][0]["after"][:values]).to eq([{ :name => "ReportingVal1", :type => :string, :data => "report1" },
- { :name => "ReportingVal2", :type => :string, :data => "report2" }])
+ expect(@report["resources"][0]["after"][:values]).to eq([{ name: "ReportingVal1", type: :string, data: "report1" },
+ { name: "ReportingVal2", type: :string, data: "report2" }])
expect(@report["resources"][0]["before"][:values]).to eq([])
expect(@report["resources"][0]["result"]).to eq("create")
expect(@report["status"]).to eq("success")
@@ -266,101 +290,154 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) do
Chef::Config[:why_run] = true
end
- it "does not throw an exception if the keys do not exist but recursive is set to false" do
+ it "does not raise an exception if the keys do not exist but recursive is set to false" do
@new_resource.key(reg_child + '\Slitheen\Raxicoricofallapatorius')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:create) # should not raise_error
expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
expect(@registry.key_exists?(reg_child + '\Slitheen\Raxicoricofallapatorius')).to eq(false)
end
+
it "does not create key if the action is create" do
@new_resource.key(reg_child + '\Slitheen')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:create)
expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
end
+
+ it "does not raise an exception if the action create and type key missing in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ name: "BriskWalk", data: "my_data" }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create and data key missing in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ name: "BriskWalk", type: :string }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create and only name key present in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ name: "BriskWalk" }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create and all keys are present in values hash" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "my_data" }])
+ @new_resource.run_action(:create) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false)
+ end
end
end
context "when action is create_if_missing" do
- before (:all) do
+ before(:all) do
reset_registry
end
it "creates registry key, value if the key is missing" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_parent)).to eq(true)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Orange" })).to eq(true)
end
it "does not create the key if it already exists with same value, type and data" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Color", :type => :string, :data => "Orange" }])
+ @new_resource.values([{ name: "Color", type: :string, data: "Orange" }])
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child)).to eq(true)
- expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Color", type: :string, data: "Orange" })).to eq(true)
end
it "creates a value if it does not exist" do
@new_resource.key(reg_child)
- @new_resource.values([{ :name => "Mango", :type => :string, :data => "Yellow" }])
+ @new_resource.values([{ name: "Mango", type: :string, data: "Yellow" }])
@new_resource.run_action(:create_if_missing)
- expect(@registry.data_exists?(reg_child, { :name => "Mango", :type => :string, :data => "Yellow" })).to eq(true)
+ expect(@registry.data_exists?(reg_child, { name: "Mango", type: :string, data: "Yellow" })).to eq(true)
end
it "creates subkey if parent exists" do
@new_resource.key(reg_child + '\Pyrovile')
- @new_resource.values([{ :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} }])
+ @new_resource.values([{ name: "Chef", type: :multi_string, data: %w{OpscodeOrange Rules} }])
@new_resource.recursive(false)
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child + '\Pyrovile')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\Pyrovile', { :name => "Chef", :type => :multi_string, :data => %w{OpscodeOrange Rules} })).to eq(true)
+ expect(@registry.value_exists?(reg_child + '\Pyrovile', { name: "Chef", type: :multi_string, data: %w{OpscodeOrange Rules} })).to eq(true)
end
- it "gives error if action create and parent does not exist and recursive is set to false" do
+ it "raises an error if action create and parent does not exist and recursive is set to false" do
@new_resource.key(reg_child + '\Sontaran\Sontar')
- @new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "MissingData" }])
@new_resource.recursive(false)
expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
end
+ it "raises an error if action create_if_missing and type key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC", data: "my_data" }])
+ expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "raises an error if action create_if_missing and data key missing in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC", type: :string }])
+ expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::RegKeyValuesDataMissing)
+ end
+
+ it "raises an error if action create_if_missing and only name key present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC" }])
+ expect { @new_resource.run_action(:create_if_missing) }.to raise_error(Chef::Exceptions::RegKeyValuesTypeMissing)
+ end
+
+ it "does not raise an error if action create_if_missing and all keys are present in values hash" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{ name: "OC", type: :string, data: "my_data" }])
+ expect { @new_resource.run_action(:create_if_missing) }.to_not raise_error
+ end
+
it "creates missing keys if action create and parent does not exist and recursive is set to true" do
@new_resource.key(reg_child + '\Sontaran\Sontar')
- @new_resource.values([{ :name => "OC", :type => :string, :data => "MissingData" }])
+ @new_resource.values([{ name: "OC", type: :string, data: "MissingData" }])
@new_resource.recursive(true)
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child + '\Sontaran\Sontar')).to eq(true)
- expect(@registry.value_exists?(reg_child + '\Sontaran\Sontar', { :name => "OC", :type => :string, :data => "MissingData" })).to eq(true)
+ expect(@registry.value_exists?(reg_child + '\Sontaran\Sontar', { name: "OC", type: :string, data: "MissingData" })).to eq(true)
end
it "creates key with multiple value as specified" do
@new_resource.key(reg_child + '\Adipose')
- @new_resource.values([{ :name => "one", :type => :string, :data => "1" }, { :name => "two", :type => :string, :data => "2" }, { :name => "three", :type => :string, :data => "3" }])
+ @new_resource.values([{ name: "one", type: :string, data: "1" }, { name: "two", type: :string, data: "2" }, { name: "three", type: :string, data: "3" }])
@new_resource.recursive(true)
@new_resource.run_action(:create_if_missing)
- @new_resource.values.each do |value|
+ @new_resource.each_value do |value|
expect(@registry.value_exists?(reg_child + '\Adipose', value)).to eq(true)
end
end
it "prepares the reporting data for :create_if_missing" do
@new_resource.key(reg_child + '\Judoon')
- @new_resource.values([{ :name => "ReportingVal3", :type => :string, :data => "report3" }])
+ @new_resource.values([{ name: "ReportingVal3", type: :string, data: "report3" }])
@new_resource.recursive(true)
@new_resource.run_action(:create_if_missing)
@report = @resource_reporter.prepare_run_data
@@ -369,7 +446,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_child + '\Judoon')
- expect(@report["resources"][0]["after"][:values]).to eq([{ :name => "ReportingVal3", :type => :string, :data => "report3" }])
+ expect(@report["resources"][0]["after"][:values]).to eq([{ name: "ReportingVal3", type: :string, data: "report3" }])
expect(@report["resources"][0]["before"][:values]).to eq([])
expect(@report["resources"][0]["result"]).to eq("create_if_missing")
expect(@report["status"]).to eq("success")
@@ -377,25 +454,54 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) do
Chef::Config[:why_run] = true
end
- it "does not throw an exception if the keys do not exist but recursive is set to false" do
+ it "does not raise an exception if the keys do not exist but recursive is set to false" do
@new_resource.key(reg_child + '\Zygons\Zygor')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:create_if_missing) # should not raise_error
expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
expect(@registry.key_exists?(reg_child + '\Zygons\Zygor')).to eq(false)
end
+
it "does nothing if the action is create_if_missing" do
@new_resource.key(reg_child + '\Zygons')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:create_if_missing)
expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
end
+
+ it "does not raise an exception if the action create_if_missing and type key missing in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ name: "BriskWalk", data: "my_data" }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create_if_missing and data key missing in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ name: "BriskWalk", type: :string }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create_if_missing and only name key present in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ name: "BriskWalk" }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
+
+ it "does not raise an exception if the action create_if_missing and all keys are present in values hash" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "my_data" }])
+ @new_resource.run_action(:create_if_missing) # should not raise_error
+ expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false)
+ end
end
end
@@ -416,66 +522,66 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
it "takes no action if the key exists but the value does not" do
- expect(@registry.data_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(true)
@new_resource.key(reg_parent + '\Opscode')
- @new_resource.values([{ :name => "LooksLike", :type => :multi_string, :data => %w{SeattleGrey OCOrange} }])
+ @new_resource.values([{ name: "LooksLike", type: :multi_string, data: %w{SeattleGrey OCOrange} }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
- expect(@registry.data_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
+ expect(@registry.data_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(true)
end
it "deletes only specified values under a key path" do
@new_resource.key(reg_parent + '\Opscode')
- @new_resource.values([{ :name => "Opscode", :type => :multi_string, :data => %w{Seattle Washington} }, { :name => "AKA", :type => :string, :data => "OC" }])
+ @new_resource.values([{ name: "Opscode", type: :multi_string, data: %w{Seattle Washington} }, { name: "AKA", type: :string, data: "OC" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
- expect(@registry.data_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
- expect(@registry.value_exists?(reg_parent + '\Opscode', { :name => "AKA", :type => :string, :data => "OC" })).to eq(false)
- expect(@registry.value_exists?(reg_parent + '\Opscode', { :name => "Opscode", :type => :multi_string, :data => %w{Seattle Washington} })).to eq(false)
+ expect(@registry.data_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(true)
+ expect(@registry.value_exists?(reg_parent + '\Opscode', { name: "AKA", type: :string, data: "OC" })).to eq(false)
+ expect(@registry.value_exists?(reg_parent + '\Opscode', { name: "Opscode", type: :multi_string, data: %w{Seattle Washington} })).to eq(false)
end
it "it deletes the values with the same name irrespective of it type and data" do
@new_resource.key(reg_parent + '\Opscode')
- @new_resource.values([{ :name => "Color", :type => :multi_string, :data => %w{Black Orange} }])
+ @new_resource.values([{ name: "Color", type: :multi_string, data: %w{Black Orange} }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
- expect(@registry.value_exists?(reg_parent + '\Opscode', { :name => "Color", :type => :string, :data => "Orange" })).to eq(false)
+ expect(@registry.value_exists?(reg_parent + '\Opscode', { name: "Color", type: :string, data: "Orange" })).to eq(false)
end
it "prepares the reporting data for action :delete" do
@new_resource.key(reg_parent + '\ReportKey')
- @new_resource.values([{ :name => "ReportVal4", :type => :string, :data => "report4" }, { :name => "ReportVal5", :type => :string, :data => "report5" }])
+ @new_resource.values([{ name: "ReportVal4", type: :string, data: "report4" }, { name: "ReportVal5", type: :string, data: "report5" }])
@new_resource.recursive(true)
@new_resource.run_action(:delete)
@report = @resource_reporter.prepare_run_data
- expect(@registry.value_exists?(reg_parent + '\ReportKey', [{ :name => "ReportVal4", :type => :string, :data => "report4" }, { :name => "ReportVal5", :type => :string, :data => "report5" }])).to eq(false)
+ expect(@registry.value_exists?(reg_parent + '\ReportKey', [{ name: "ReportVal4", type: :string, data: "report4" }, { name: "ReportVal5", type: :string, data: "report5" }])).to eq(false)
expect(@report["action"]).to eq("end")
expect(@report["resources"].count).to eq(1)
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_parent + '\ReportKey')
- expect(@report["resources"][0]["before"][:values]).to eq([{ :name => "ReportVal4", :type => :string, :data => "report4" },
- { :name => "ReportVal5", :type => :string, :data => "report5" }])
- #Not testing for after values to match since after -> new_resource values.
+ expect(@report["resources"][0]["before"][:values]).to eq([{ name: "ReportVal4", type: :string, data: "report4" },
+ { name: "ReportVal5", type: :string, data: "report5" }])
+ # Not testing for after values to match since after -> new_resource values.
expect(@report["resources"][0]["result"]).to eq("delete")
expect(@report["status"]).to eq("success")
expect(@report["total_res_count"]).to eq("1")
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) do
Chef::Config[:why_run] = true
end
it "does nothing if the action is delete" do
@new_resource.key(reg_parent + '\OpscodeWhyRun')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete)
@@ -485,7 +591,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
end
context "when the action is delete_key" do
- before (:all) do
+ before(:all) do
reset_registry
create_deletable_keys
end
@@ -516,7 +622,7 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
it "ignores the values under a key" do
@new_resource.key(reg_parent + '\OpscodeIgnoredValues')
- #@new_resource.values([{:name=>"DontExist", :type=>:string, :data=>"These will be ignored anyways"}])
+ # @new_resource.values([{:name=>"DontExist", :type=>:string, :data=>"These will be ignored anyways"}])
@new_resource.recursive(true)
@new_resource.run_action(:delete_key)
end
@@ -539,27 +645,27 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
expect(@report["resources"][0]["type"]).to eq("registry_key")
expect(@report["resources"][0]["name"]).to eq(resource_name)
expect(@report["resources"][0]["id"]).to eq(reg_parent + '\ReportKey')
- #Not testing for before or after values to match since
- #after -> new_resource.values and
- #before -> current_resource.values
+ # Not testing for before or after values to match since
+ # after -> new_resource.values and
+ # before -> current_resource.values
expect(@report["resources"][0]["result"]).to eq("delete_key")
expect(@report["status"]).to eq("success")
expect(@report["total_res_count"]).to eq("1")
end
context "while running in whyrun mode" do
- before (:each) do
+ before(:each) do
Chef::Config[:why_run] = true
end
it "does not throw an exception if the key has subkeys but recursive is set to false" do
@new_resource.key(reg_parent + '\OpscodeWhyRun')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete_key)
end
it "does nothing if the action is delete_key" do
@new_resource.key(reg_parent + '\OpscodeWhyRun')
- @new_resource.values([{ :name => "BriskWalk", :type => :string, :data => "is good for health" }])
+ @new_resource.values([{ name: "BriskWalk", type: :string, data: "is good for health" }])
@new_resource.recursive(false)
@new_resource.run_action(:delete_key)
diff --git a/spec/functional/resource/remote_directory_spec.rb b/spec/functional/resource/remote_directory_spec.rb
index c25e51cf2a..2d9d1de351 100644
--- a/spec/functional/resource/remote_directory_spec.rb
+++ b/spec/functional/resource/remote_directory_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index 1f92a567f3..09e4fdccb4 100644
--- a/spec/functional/resource/remote_file_spec.rb
+++ b/spec/functional/resource/remote_file_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
@@ -28,6 +28,9 @@ describe Chef::Resource::RemoteFile do
before(:each) do
@old_file_cache = Chef::Config[:file_cache_path]
Chef::Config[:file_cache_path] = file_cache_path
+ Chef::Config[:rest_timeout] = 2
+ Chef::Config[:http_retry_delay] = 1
+ Chef::Config[:http_retry_count] = 2
end
after(:each) do
@@ -55,11 +58,11 @@ describe Chef::Resource::RemoteFile do
let(:default_mode) { (0666 & ~File.umask).to_s(8) }
context "when fetching files over HTTP" do
- before(:each) do
- start_tiny_server
+ before(:all) do
+ start_tiny_server(RequestTimeout: 1)
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -97,21 +100,22 @@ describe Chef::Resource::RemoteFile do
context "when fetching files over HTTPS" do
- before(:each) do
+ before(:all) do
cert_text = File.read(File.expand_path("ssl/chef-rspec.cert", CHEF_SPEC_DATA))
cert = OpenSSL::X509::Certificate.new(cert_text)
key_text = File.read(File.expand_path("ssl/chef-rspec.key", CHEF_SPEC_DATA))
key = OpenSSL::PKey::RSA.new(key_text)
- server_opts = { :SSLEnable => true,
- :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
- :SSLCertificate => cert,
- :SSLPrivateKey => key }
+ server_opts = { SSLEnable: true,
+ SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
+ SSLCertificate: cert,
+ SSLPrivateKey: key,
+ RequestTimeout: 1 }
- start_tiny_server(server_opts)
+ start_tiny_server(**server_opts)
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -123,12 +127,183 @@ describe Chef::Resource::RemoteFile do
end
+ context "when running on Windows", :windows_only do
+ describe "when fetching files over SMB" do
+ include Chef::Mixin::ShellOut
+ let(:smb_share_root_directory) { directory = File.join(Dir.tmpdir, make_tmpname("windows_script_test")); Dir.mkdir(directory); directory }
+ let(:smb_file_local_file_name) { "smb_file.txt" }
+ let(:smb_file_local_path) { File.join( smb_share_root_directory, smb_file_local_file_name ) }
+ let(:smb_share_name) { "chef_smb_test" }
+ let(:smb_remote_path) { File.join("//#{ENV["COMPUTERNAME"]}", smb_share_name, smb_file_local_file_name).tr("/", "\\") }
+ let(:smb_file_content) { "hellofun" }
+ let(:local_destination_path) { File.join(Dir.tmpdir, make_tmpname("chef_remote_file")) }
+ let(:windows_current_user) { ENV["USERNAME"] }
+ let(:windows_current_user_domain) { ENV["USERDOMAIN"] || ENV["COMPUTERNAME"] }
+ let(:windows_current_user_qualified) { "#{windows_current_user_domain}\\#{windows_current_user}" }
+
+ let(:remote_domain) { nil }
+ let(:remote_user) { nil }
+ let(:remote_password) { nil }
+
+ let(:resource) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ resource = Chef::Resource::RemoteFile.new(path, run_context)
+ end
+
+ before do
+ shell_out("net.exe share #{smb_share_name} /delete")
+ File.write(smb_file_local_path, smb_file_content )
+ shell_out!("net.exe share #{smb_share_name}=\"#{smb_share_root_directory.tr("/", '\\')}\" /grant:\"authenticated users\",read")
+ end
+
+ after do
+ shell_out("net.exe share #{smb_share_name} /delete")
+ File.delete(smb_file_local_path) if File.exist?(smb_file_local_path)
+ File.delete(local_destination_path) if File.exist?(local_destination_path)
+ Dir.rmdir(smb_share_root_directory)
+ end
+
+ context "when configuring the Windows identity used to access the remote file" do
+ before do
+ resource.path(local_destination_path)
+ resource.source(smb_remote_path)
+ resource.remote_domain(remote_domain)
+ resource.remote_user(remote_user)
+ resource.remote_password(remote_password)
+ resource.node.default["platform_family"] = "windows"
+ allow_any_instance_of(Chef::Provider::RemoteFile::NetworkFile).to receive(:node).and_return({ "platform_family" => "windows" })
+ end
+
+ shared_examples_for "a remote_file resource accessing a remote file to which the specified user has access" do
+ it "has the same content as the original file" do
+ expect { resource.run_action(:create) }.not_to raise_error
+ expect(::File.read(local_destination_path).chomp).to eq smb_file_content
+ end
+ end
+
+ shared_examples_for "a remote_file resource accessing a remote file to which the specified user does not have access" do
+ it "causes an error to be raised" do
+ expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
+ end
+ end
+
+ shared_examples_for "a remote_file resource accessing a remote file with invalid user" do
+ it "causes an error to be raised" do
+ allow(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
+ expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32APIError)
+ end
+ end
+
+ context "when the file is accessible to non-admin users only as the current identity" do
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
+ end
+
+ context "when the resource is accessed using the current user's identity" do
+ let(:remote_user) { nil }
+ let(:remote_domain) { nil }
+ let(:remote_password) { nil }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+
+ describe "uses the ::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE constant to chunk the file" do
+ let(:invalid_chunk_size) { -1 }
+ before do
+ stub_const("::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE", invalid_chunk_size)
+ end
+
+ it "raises an ArgumentError when the chunk size is negative" do
+ expect(::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE).to eq(invalid_chunk_size)
+ expect { resource.run_action(:create) }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "when the file must be transferred in more than one chunk" do
+ before do
+ stub_const("::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE", 3)
+ end
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+ end
+ end
+
+ context "when the resource is accessed using an alternate user's identity with no access to the file" do
+ let(:windows_nonadmin_user) { "chefremfile1" }
+ let(:windows_nonadmin_user_password) { "j82ajfxK3;2Xe1" }
+ include_context "a non-admin Windows user"
+
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /deny \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
+ end
+
+ let(:remote_user) { windows_nonadmin_user }
+ let(:remote_domain) { windows_nonadmin_user_domain }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user does not have access"
+ end
+ end
+
+ context "when the the file is only accessible as a specific alternate identity" do
+ let(:windows_nonadmin_user) { "chefremfile2" }
+ let(:windows_nonadmin_user_password) { "j82ajfxK3;2Xe2" }
+ include_context "a non-admin Windows user"
+
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
+ end
+
+ context "when the resource is accessed using the specific non-qualified alternate user identity with access" do
+ let(:remote_user) { windows_nonadmin_user }
+ let(:remote_domain) { "." }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+ end
+
+ context "when the resource is accessed using the specific alternate user identity with access and the domain is specified" do
+ let(:remote_user) { windows_nonadmin_user }
+ let(:remote_domain) { windows_nonadmin_user_domain }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
+ end
+
+ context "when the resource is accessed using the current user's identity" do
+ before do
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_nonadmin_user_qualified}:(R)\" /deny #{windows_current_user_qualified}:(R) /inheritance:r")
+ end
+
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user does not have access"
+ end
+
+ context "when the resource is accessed using an alternate user's identity with no access to the file" do
+ let(:windows_nonadmin_user) { "chefremfile3" }
+ let(:windows_nonadmin_user_password) { "j82ajfxK3;2Xe3" }
+ include_context "a non-admin Windows user"
+
+ let(:remote_user) { windows_nonadmin_user_qualified }
+ let(:remote_domain) { nil }
+ let(:remote_password) { windows_nonadmin_user_password }
+
+ before do
+ allow_any_instance_of(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
+ end
+
+ it_behaves_like "a remote_file resource accessing a remote file with invalid user"
+ end
+ end
+ end
+ end
+ end
+
context "when dealing with content length checking" do
- before(:each) do
- start_tiny_server
+ before(:all) do
+ start_tiny_server(RequestTimeout: 1)
end
- after(:each) do
+ after(:all) do
stop_tiny_server
end
@@ -185,7 +360,7 @@ describe Chef::Resource::RemoteFile do
it "should raise ContentLengthMismatch" do
expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- #File.should_not exist(path) # XXX: CHEF-5081
+ # File.should_not exist(path) # XXX: CHEF-5081
end
end
@@ -198,7 +373,7 @@ describe Chef::Resource::RemoteFile do
it "should raise ContentLengthMismatch" do
expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::ContentLengthMismatch)
- #File.should_not exist(path) # XXX: CHEF-5081
+ # File.should_not exist(path) # XXX: CHEF-5081
end
end
@@ -234,13 +409,7 @@ describe Chef::Resource::RemoteFile do
it "should not create the file" do
# This can legitimately raise either Errno::EADDRNOTAVAIL or Errno::ECONNREFUSED
# in different Ruby versions.
- old_value = RSpec::Expectations.configuration.on_potential_false_positives
- RSpec::Expectations.configuration.on_potential_false_positives = :nothing
- begin
- expect { resource.run_action(:create) }.to raise_error
- ensure
- RSpec::Expectations.configuration.on_potential_false_positives = old_value
- end
+ expect { resource.run_action(:create) }.to raise_error(SystemCallError)
expect(File).not_to exist(path)
end
diff --git a/spec/functional/resource/rpm_spec.rb b/spec/functional/resource/rpm_spec.rb
index ce9332e4ed..15b6731357 100644
--- a/spec/functional/resource/rpm_spec.rb
+++ b/spec/functional/resource/rpm_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Prabhu Das (<prabhu.das@clogeny.com>)
-# Copyright:: Copyright 2013-2016, 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");
@@ -17,52 +17,49 @@
#
require "spec_helper"
-require "functional/resource/base"
require "chef/mixin/shell_out"
# run this test only for following platforms.
-exclude_test = !%w{aix centos redhat suse}.include?(ohai[:platform])
-describe Chef::Resource::RpmPackage, :requires_root, :external => exclude_test do
+exclude_test = !%w{aix rhel fedora suse amazon}.include?(ohai[:platform_family])
+describe Chef::Resource::RpmPackage, :requires_root, external: exclude_test do
include Chef::Mixin::ShellOut
let(:new_resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
new_resource = Chef::Resource::RpmPackage.new(@pkg_name, run_context)
new_resource.source @pkg_path
new_resource
end
def rpm_pkg_should_be_installed(resource)
- case ohai[:platform]
# Due to dependency issues , different rpm pkgs are used in different platforms.
# dummy rpm package works in aix, without any dependency issues.
- when "aix"
+ if ohai[:platform] == "aix"
expect(shell_out("rpm -qa | grep dummy").exitstatus).to eq(0)
# mytest rpm package works in centos, redhat and in suse without any dependency issues.
- when "centos", "redhat", "suse"
+ else
expect(shell_out("rpm -qa | grep mytest").exitstatus).to eq(0)
- ::File.exists?("/opt/mytest/mytest.sh") # The mytest rpm package contains the mytest.sh file
+ ::File.exist?("/opt/mytest/mytest.sh") # The mytest rpm package contains the mytest.sh file
end
end
def rpm_pkg_should_not_be_installed(resource)
- case ohai[:platform]
- when "aix"
+ if ohai[:platform] == "aix"
expect(shell_out("rpm -qa | grep dummy").exitstatus).to eq(1)
- when "centos", "redhat", "suse"
+ else
expect(shell_out("rpm -qa | grep mytest").exitstatus).to eq(1)
- !::File.exists?("/opt/mytest/mytest.sh")
+ !::File.exist?("/opt/mytest/mytest.sh")
end
end
before(:all) do
- case ohai[:platform]
# Due to dependency issues , different rpm pkgs are used in different platforms.
- when "aix"
+ if ohai[:platform] == "aix"
@pkg_name = "dummy"
@pkg_version = "1-0"
@pkg_path = "#{Dir.tmpdir}/dummy-1-0.aix6.1.noarch.rpm"
FileUtils.cp(File.join(CHEF_SPEC_ASSETS, "dummy-1-0.aix6.1.noarch.rpm") , @pkg_path)
- when "centos", "redhat", "suse"
+ else
@pkg_name = "mytest"
@pkg_version = "1.0-1"
@pkg_path = "#{Dir.tmpdir}/mytest-1.0-1.noarch.rpm"
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index 32529fbb0c..4707fa8ee0 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright 2011-2016, 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");
@@ -21,7 +21,7 @@ require "spec_helper"
describe Chef::Resource::Template do
def binread(file)
- File.open(file, "rb") { |f| f.read }
+ File.open(file, "rb", &:read)
end
include_context Chef::Resource::File
@@ -32,6 +32,7 @@ describe Chef::Resource::Template do
let(:node) do
node = Chef::Node.new
node.normal[:slappiness] = "a warm gun"
+ node.normal[:nested][:secret] = "value"
node
end
@@ -67,7 +68,7 @@ describe Chef::Resource::Template do
context "when the target file does not exist" do
it "creates the template with the rendered content using the variable attribute when the :create action is run" do
resource.source("openldap_variable_stuff.conf.erb")
- resource.variables(:secret => "nutella")
+ resource.variables(secret: "nutella")
resource.run_action(:create)
expect(IO.read(path)).to eq("super secret is nutella")
end
@@ -111,7 +112,7 @@ describe Chef::Resource::Template do
context "using single helper syntax referencing @node" do
before do
node.normal[:helper_test_attr] = "value from helper method"
- resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" }
+ resource.helper(:helper_method) { (@node[:helper_test_attr]).to_s }
end
it_behaves_like "a template with helpers"
@@ -202,11 +203,43 @@ describe Chef::Resource::Template do
it "output should contain platform's line endings" do
resource.run_action(:create)
binread(path).each_line do |line|
- expect(line).to end_with(Chef::Platform.windows? ? "\r\n" : "\n")
+ expect(line).to end_with(ChefUtils.windows? ? "\r\n" : "\n")
end
end
end
end
end
+ describe "when template variables contain lazy{} calls" do
+ it "resolves the DelayedEvaluator" do
+ resource.source("openldap_variable_stuff.conf.erb")
+ resource.variables(secret: Chef::DelayedEvaluator.new { "nutella" })
+ resource.run_action(:create)
+ expect(IO.read(path)).to eq("super secret is nutella")
+ end
+
+ it "does not mutate the resource variables" do
+ resource.source("openldap_variable_stuff.conf.erb")
+ resource.variables(secret: Chef::DelayedEvaluator.new { "nutella" })
+ resource.run_action(:create)
+ expect(resource.variables[:secret]).to be_a Chef::DelayedEvaluator
+ end
+
+ it "resolves the DelayedEvaluator when deeply nested" do
+ resource.source("openldap_nested_variable_stuff.erb")
+ resource.variables(secret: [{ "key" => Chef::DelayedEvaluator.new { "nutella" } }])
+ resource.run_action(:create)
+ expect(IO.read(path)).to eq("super secret is nutella")
+ end
+ end
+
+ describe "when passing a node attribute mash as a template variable" do
+ it "uses the node attributes like a hash" do
+ resource.source("openldap_variable_stuff.conf.erb")
+ resource.variables(node[:nested])
+ resource.run_action(:create)
+ expect(IO.read(path)).to eq("super secret is value")
+ end
+ end
+
end
diff --git a/spec/functional/resource/timezone_spec.rb b/spec/functional/resource/timezone_spec.rb
new file mode 100644
index 0000000000..d44d5b34a8
--- /dev/null
+++ b/spec/functional/resource/timezone_spec.rb
@@ -0,0 +1,41 @@
+#
+# Author:: Gary Bright (<digitalgaz@hotmail.com>)
+# Copyright:: Copyright (c) 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::Timezone, :windows_only do
+ let(:timezone) { "GMT Standard Time" }
+
+ def timezone_resource
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+
+ Chef::Resource::Timezone.new(timezone, run_context)
+ end
+
+ describe "when a timezone is provided on windows" do
+ it "should set a timezone" do
+ timezone_resource.run_action(:set)
+ end
+ end
+
+ describe "when a timezone is not provided on windows" do
+ let(:timezone) { nil }
+ it "raises an exception" do
+ expect { timezone_resource.run_action(:set) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+end
diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb
index bedb37838c..50da812b0f 100644
--- a/spec/functional/resource/user/dscl_spec.rb
+++ b/spec/functional/resource/user/dscl_spec.rb
@@ -1,5 +1,5 @@
#
-# Copyright:: Copyright 2014-2016, 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");
@@ -19,20 +19,17 @@ require "spec_helper"
require "chef/mixin/shell_out"
metadata = {
- :mac_osx_only => true,
- :requires_root => true,
- :not_supported_on_mac_osx_106 => true,
+ macos_1013: true,
+ requires_root: true,
}
describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metadata do
include Chef::Mixin::ShellOut
def clean_user
- begin
- shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
- rescue Mixlib::ShellOut::ShellCommandFailed
- # Raised when the user is already cleaned
- end
+ shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ # Raised when the user is already cleaned
end
def user_should_exist
@@ -124,13 +121,7 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
describe "when password is being set via shadow hash" do
let(:password) do
- if node[:platform_version].start_with?("10.7.")
- # On Mac 10.7 we only need to set the password
- "c9b3bd1a0cde797eef0eff16c580dab996ba3a21961cccc\
-d0f5e65c61558243e50b1a490088bd4824e3b35562d383ca02260398\
-ef1979b302212ec1c5383d1d05fc8d843"
- else
- "c734b6e4787c3727bb35e29fdd92b97c\
+ "c734b6e4787c3727bb35e29fdd92b97c\
1de12df509577a045728255ec7c6c5f5\
c18efa05ed02b682ffa7ebc05119900e\
b1d4880833aa7a190afc13e2bf0936b8\
@@ -138,7 +129,6 @@ b1d4880833aa7a190afc13e2bf0936b8\
9464a8c234f3919082400b4f939bb77b\
c5adbbac718b7eb99463a7b679571e0f\
1c9fef2ef08d0b9e9c2bcf644eed2ffc"
- end
end
let(:iterations) { 25000 }
diff --git a/spec/functional/resource/user/mac_user_spec.rb b/spec/functional/resource/user/mac_user_spec.rb
new file mode 100644
index 0000000000..dabc303afb
--- /dev/null
+++ b/spec/functional/resource/user/mac_user_spec.rb
@@ -0,0 +1,207 @@
+#
+# Copyright:: Copyright (c) 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 "chef/mixin/shell_out"
+
+metadata = {
+ macos_gte_1014: true,
+ requires_root: true,
+}
+
+describe "Chef::Resource::User with Chef::Provider::User::MacUser provider", metadata do
+ include Chef::Mixin::ShellOut
+
+ def clean_user
+ shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ # Raised when the user is already cleaned
+ end
+
+ def ensure_file_cache_path_exists
+ path = Chef::Config["file_cache_path"]
+ FileUtils.mkdir_p(path) unless File.directory?(path)
+ end
+
+ def user_should_exist
+ expect(shell_out("/usr/bin/dscl . -read /Users/#{username}").error?).to be(false)
+ end
+
+ def check_password(pass)
+ # In order to test the password we use dscl passwd command since
+ # that's the only command that gets the user password from CLI.
+ expect(shell_out("dscl . -passwd /Users/greatchef #{pass} new_password").exitstatus).to eq(0)
+ # Now reset the password back
+ expect(shell_out("dscl . -passwd /Users/greatchef new_password #{pass}").exitstatus).to eq(0)
+ end
+
+ let(:node) do
+ n = Chef::Node.new
+ n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
+ n
+ end
+
+ let(:events) do
+ Chef::EventDispatch::Dispatcher.new
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(node, {}, events)
+ end
+
+ let(:username) do
+ "greatchef"
+ end
+
+ let(:uid) { nil }
+ let(:gid) { 20 }
+ let(:home) { nil }
+ let(:manage_home) { false }
+ let(:password) { "XXXYYYZZZ" }
+ let(:comment) { "Great Chef" }
+ let(:shell) { "/bin/bash" }
+ let(:salt) { nil }
+ let(:iterations) { nil }
+
+ let(:user_resource) do
+ r = Chef::Resource::User::MacUser.new("TEST USER RESOURCE", run_context)
+ r.username(username)
+ r.uid(uid)
+ r.gid(gid)
+ r.home(home)
+ r.shell(shell)
+ r.comment(comment)
+ r.manage_home(manage_home)
+ r.password(password)
+ r.salt(salt)
+ r.iterations(iterations)
+ r
+ end
+
+ before do
+ clean_user
+ ensure_file_cache_path_exists
+ end
+
+ after(:each) do
+ clean_user
+ end
+
+ describe "action :create" do
+ it "should create the user" do
+ user_resource.run_action(:create)
+ user_should_exist
+ check_password(password)
+ end
+ end
+
+ describe "when user exists" do
+ before do
+ existing_resource = user_resource.dup
+ existing_resource.run_action(:create)
+ user_should_exist
+ end
+
+ describe "when password is updated" do
+ it "should update the password of the user" do
+ user_resource.password("mykitchen")
+ user_resource.run_action(:create)
+ check_password("mykitchen")
+ end
+ end
+ end
+
+ describe "when password is being set via shadow hash" do
+ let(:password) do
+ "c734b6e4787c3727bb35e29fdd92b97c\
+1de12df509577a045728255ec7c6c5f5\
+c18efa05ed02b682ffa7ebc05119900e\
+b1d4880833aa7a190afc13e2bf0936b8\
+20123e8c98f0f9bcac2a629d9163caac\
+9464a8c234f3919082400b4f939bb77b\
+c5adbbac718b7eb99463a7b679571e0f\
+1c9fef2ef08d0b9e9c2bcf644eed2ffc"
+ end
+
+ let(:iterations) { 25000 }
+ let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" }
+
+ it "action :create should create the user" do
+ user_resource.run_action(:create)
+ user_should_exist
+ check_password("soawesome")
+ end
+
+ describe "when user exists" do
+ before do
+ existing_resource = user_resource.dup
+ existing_resource.run_action(:create)
+ user_should_exist
+ end
+
+ describe "when password is updated" do
+ describe "without salt" do
+ let(:salt) { nil }
+
+ it "it sets the password" do
+ user_resource.password("mykitchen")
+ user_resource.run_action(:create)
+ check_password("mykitchen")
+ end
+ end
+
+ describe "with salt and plaintext password" do
+ it "raises Chef::Exceptions::User" do
+ expect do
+ user_resource.password("notasha512")
+ user_resource.run_action(:create)
+ end.to raise_error(Chef::Exceptions::User)
+ end
+ end
+ end
+ end
+ end
+
+ describe "when a user is member of some groups" do
+ let(:groups) { %w{staff operator} }
+
+ before do
+ existing_resource = user_resource.dup
+ existing_resource.run_action(:create)
+
+ groups.each do |group|
+ shell_out!("/usr/bin/dscl . -append '/Groups/#{group}' GroupMembership #{username}")
+ end
+ end
+
+ after do
+ groups.each do |group|
+ # Do not raise an error when user is correctly removed
+ shell_out("/usr/bin/dscl . -delete '/Groups/#{group}' GroupMembership #{username}")
+ end
+ end
+
+ it ":remove action removes the user from the groups and deletes the user" do
+ user_resource.run_action(:remove)
+ groups.each do |group|
+ # Do not raise an error when group is empty
+ expect(shell_out("dscl . read /Groups/staff GroupMembership").stdout).not_to include(group)
+ end
+ end
+ end
+
+end
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
deleted file mode 100644
index 79d62436f5..0000000000
--- a/spec/functional/resource/user/useradd_spec.rb
+++ /dev/null
@@ -1,698 +0,0 @@
-# encoding: UTF-8
-#
-# Author:: Daniel DeLeo (<dan@chef.io>)
-# Copyright:: Copyright 2013-2016, 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 "functional/resource/base"
-require "chef/mixin/shell_out"
-
-def resource_for_platform(username, run_context)
- Chef::Resource.resource_for_node(:user, node).new(username, run_context)
-end
-
-# ideally we could somehow pass an array of [ ...::Aix, ...::Linux ] to the
-# filter, but we have to pick the right one for the O/S.
-def user_provider_filter
- case ohai[:os]
- when "aix"
- Chef::Provider::User::Aix
- when "linux"
- Chef::Provider::User::Linux
- end
-end
-
-metadata = {
- :unix_only => true,
- :requires_root => true,
- :not_supported_on_mac_osx => true,
- :provider => { :user => user_provider_filter },
-}
-
-describe Chef::Provider::User::Useradd, metadata do
-
- include Chef::Mixin::ShellOut
-
- # Utility code for /etc/passwd interaction, avoid any caching of user records:
- PwEntry = Struct.new(:name, :passwd, :uid, :gid, :gecos, :home, :shell)
-
- class UserNotFound < StandardError; end
-
- def pw_entry
- passwd_file = File.open("/etc/passwd", "rb") { |f| f.read }
- matcher = /^#{Regexp.escape(username)}.+$/
- if passwd_entry = passwd_file.scan(matcher).first
- PwEntry.new(*passwd_entry.split(":"))
- else
- raise UserNotFound, "no entry matching #{matcher.inspect} found in /etc/passwd"
- end
- end
-
- def etc_shadow
- case ohai[:platform]
- when "aix"
- File.open("/etc/security/passwd") { |f| f.read }
- else
- File.open("/etc/shadow") { |f| f.read }
- end
- end
-
- def self.quote_in_username_unsupported?
- if OHAI_SYSTEM["platform_family"] == "debian"
- false
- else
- "Only debian family systems support quotes in username"
- end
- end
-
- def password_should_be_set
- if ohai[:platform] == "aix"
- expect(pw_entry.passwd).to eq("!")
- else
- expect(pw_entry.passwd).to eq("x")
- end
- end
-
- def try_cleanup
- ["/home/cheftestfoo", "/home/cheftestbar", "/home/cf-test"].each do |f|
- FileUtils.rm_rf(f) if File.exists? f
- end
-
- ["cf-test"].each do |u|
- r = resource_for_platform("DELETE USER", run_context)
- r.manage_home true
- r.username("cf-test")
- r.run_action(:remove)
- end
- end
-
- before do
- # Silence shell_out live stream
- Chef::Log.level = :warn
- try_cleanup
- end
-
- after do
- max_retries = 3
- while max_retries > 0
- begin
- pw_entry # will raise if the user doesn't exist
- status = shell_out!("userdel", "-r", username, :returns => [0, 8, 12])
-
- # Error code 8 during userdel indicates that the user is logged in.
- # This occurs randomly because the accounts daemon holds a lock due to which userdel fails.
- # The work around is to retry userdel for 3 times.
- break if status.exitstatus != 8
-
- sleep 1
- max_retries = max_retries - 1
- rescue UserNotFound
- break
- end
- end
-
- status.error! if max_retries == 0
- end
-
- let(:node) do
- n = Chef::Node.new
- n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
- n
- end
-
- let(:events) do
- Chef::EventDispatch::Dispatcher.new
- end
-
- let(:run_context) do
- Chef::RunContext.new(node, {}, events)
- end
-
- let(:username) { "cf-test" }
- let(:uid) { nil }
- let(:home) { nil }
- let(:manage_home) { false }
- let(:password) { nil }
- let(:system) { false }
- let(:comment) { nil }
-
- let(:user_resource) do
- r = resource_for_platform("TEST USER RESOURCE", run_context)
- r.username(username)
- r.uid(uid)
- r.home(home)
- r.comment(comment)
- r.manage_home(manage_home)
- r.password(password)
- r.system(system)
- r
- end
-
- let(:expected_shadow) do
- if ohai[:platform] == "aix"
- expected_shadow = "cf-test" # For aix just check user entry in shadow file
- else
- expected_shadow = "cf-test:$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- describe "action :create" do
-
- context "when the user does not exist beforehand" do
- before do
- user_resource.run_action(:create)
- expect(user_resource).to be_updated_by_last_action
- end
-
- it "ensures the user exists" do
- expect(pw_entry.name).to eq(username)
- end
-
- # On Debian, the only constraints are that usernames must neither start
- # with a dash ('-') nor plus ('+') nor tilde ('~') nor contain a colon
- # (':'), a comma (','), or a whitespace (space: ' ', end of line: '\n',
- # tabulation: '\t', etc.). Note that using a slash ('/') may break the
- # default algorithm for the definition of the user's home directory.
-
- context "and the username contains a single quote", skip: quote_in_username_unsupported? do
-
- let(:username) { "t'bilisi" }
-
- it "ensures the user exists" do
- expect(pw_entry.name).to eq(username)
- end
- end
-
- context "when uid is set" do
- # Should verify uid not in use...
- let(:uid) { 1999 }
-
- it "ensures the user has the given uid" do
- expect(pw_entry.uid).to eq("1999")
- end
- end
-
- context "when comment is set" do
- let(:comment) { "hello this is dog" }
-
- it "ensures the comment is set" do
- expect(pw_entry.gecos).to eq("hello this is dog")
- end
-
- context "in standard gecos format" do
- let(:comment) { "Bobo T. Clown,some building,555-555-5555,@boboclown" }
-
- it "ensures the comment is set" do
- expect(pw_entry.gecos).to eq(comment)
- end
- end
-
- context "to a string containing multibyte characters" do
- let(:comment) { "(╯°□°)╯︵ ┻━┻" }
-
- it "ensures the comment is set" do
- actual = pw_entry.gecos
- actual.force_encoding(Encoding::UTF_8) if "".respond_to?(:force_encoding)
- expect(actual).to eq(comment)
- end
- end
-
- context "to a string containing an apostrophe `'`" do
- let(:comment) { "don't go" }
-
- it "ensures the comment is set" do
- expect(pw_entry.gecos).to eq(comment)
- end
- end
- end
-
- context "when home is set" do
- let(:home) { "/home/#{username}" }
-
- it "ensures the user's home is set to the given path" do
- expect(pw_entry.home).to eq(home)
- end
-
- it "does not create the home dir without `manage_home'" do
- expect(File).not_to exist(home)
- end
-
- context "and manage_home is enabled" do
- let(:manage_home) { true }
-
- it "ensures the user's home directory exists" do
- expect(File).to exist(home)
- end
- end
-
- context "and manage_home is the default" do
- let(:manage_home) { nil }
-
- it "does not create the home dir without `manage_home'" do
- expect(File).not_to exist(home)
- end
- end
- end
-
- context "when a password is specified" do
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "sets the user's shadow password" do
- password_should_be_set
- expect(etc_shadow).to include(expected_shadow)
- end
- end
-
- context "when a system user is specified", skip: aix? do
- let(:system) { true }
- let(:uid_min) do
- # from `man useradd`, login user means uid will be between
- # UID_SYS_MIN and UID_SYS_MAX defined in /etc/login.defs. On my
- # Ubuntu 13.04 system, these are commented out, so we'll look at
- # UID_MIN to find the lower limit of the non-system-user range, and
- # use that value in our assertions.
- login_defs = File.open("/etc/login.defs", "rb") { |f| f.read }
- uid_min_scan = /^UID_MIN\s+(\d+)/
- login_defs.match(uid_min_scan)[1]
- end
-
- it "ensures the user has the properties of a system user" do
- expect(pw_entry.uid.to_i).to be < uid_min.to_i
- end
- end
- end # when the user does not exist beforehand
-
- context "when the user already exists" do
-
- let(:expect_updated?) { true }
-
- let(:existing_uid) { nil }
- let(:existing_home) { nil }
- let(:existing_manage_home) { false }
- let(:existing_password) { nil }
- let(:existing_system) { false }
- let(:existing_comment) { nil }
-
- let(:existing_user) do
- r = resource_for_platform("TEST USER RESOURCE", run_context)
- # username is identity attr, must match.
- r.username(username)
- r.uid(existing_uid)
- r.home(existing_home)
- r.comment(existing_comment)
- r.manage_home(existing_manage_home)
- r.password(existing_password)
- r.system(existing_system)
- r
- end
-
- before do
- if reason = skip
- skip(reason)
- end
- existing_user.run_action(:create)
- expect(existing_user).to be_updated_by_last_action
- user_resource.run_action(:create)
- expect(user_resource.updated_by_last_action?).to eq(expect_updated?)
- end
-
- context "and all properties are in the desired state" do
- let(:uid) { 1999 }
- let(:home) { "/home/bobo" }
- let(:manage_home) { true }
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- let(:system) { false }
- let(:comment) { "hello this is dog" }
-
- let(:existing_uid) { uid }
- let(:existing_home) { home }
- let(:existing_manage_home) { manage_home }
- let(:existing_password) { password }
- let(:existing_system) { false }
- let(:existing_comment) { comment }
-
- let(:expect_updated?) { false }
-
- it "does not update the user" do
- expect(user_resource).not_to be_updated
- end
- end
-
- context "and the uid is updated" do
- let(:uid) { 1999 }
- let(:existing_uid) { 1998 }
-
- it "ensures the uid is set to the desired value" do
- expect(pw_entry.uid).to eq("1999")
- end
- end
-
- context "and the comment is updated" do
- let(:comment) { "hello this is dog" }
- let(:existing_comment) { "woof" }
-
- it "ensures the comment field is set to the desired value" do
- expect(pw_entry.gecos).to eq("hello this is dog")
- end
- end
-
- context "and home directory is updated" do
- let(:existing_home) { "/home/cheftestfoo" }
- let(:home) { "/home/cheftestbar" }
- it "ensures the home directory is set to the desired value" do
- expect(pw_entry.home).to eq("/home/cheftestbar")
- end
-
- context "and manage_home is enabled" do
- let(:existing_manage_home) { true }
- let(:manage_home) { true }
- it "moves the home directory to the new location" do
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).to exist("/home/cheftestbar")
- end
- end
-
- context "and manage_home wasn't enabled but is now" do
- let(:existing_manage_home) { false }
- let(:manage_home) { true }
-
- if %w{rhel fedora}.include?(OHAI_SYSTEM["platform_family"])
- # Inconsistent behavior. See: CHEF-2205
- it "created the home dir b/c of CHEF-2205 so it still exists" do
- # This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).to exist("/home/cheftestbar")
- end
- elsif ohai[:platform] == "aix"
- it "creates the home dir in the desired location" do
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).to exist("/home/cheftestbar")
- end
- else
- it "does not create the home dir in the desired location (XXX)" do
- # This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/cheftestfoo")
- expect(File).not_to exist("/home/cheftestbar")
- end
- end
- end
-
- context "and manage_home was enabled but is not now" do
- let(:existing_manage_home) { true }
- let(:manage_home) { false }
-
- it "leaves the old home directory around (XXX)" do
- # Would it be better to remove the old home?
- expect(File).to exist("/home/cheftestfoo")
- expect(File).not_to exist("/home/cheftestbar")
- end
- end
- end
-
- context "and a password is added" do
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "ensures the password is set" do
- password_should_be_set
- expect(etc_shadow).to include(expected_shadow)
- end
-
- end
-
- context "and the password is updated" do
- # openssl passwd -1 "OLDpassword"
- let(:existing_password) do
- case ohai[:platform]
- when "aix"
- "jkzG6MvUxjk2g"
- else
- "$1$1dVmwm4z$CftsFn8eBDjDRUytYKkXB."
- end
- end
-
- # openssl passwd -1 "secretpassword"
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "ensures the password is set to the desired value" do
- password_should_be_set
- expect(etc_shadow).to include(expected_shadow)
- end
- end
-
- context "and the user is changed from not-system to system" do
- let(:existing_system) { false }
- let(:system) { true }
-
- let(:expect_updated?) { false }
-
- it "does not modify the user at all" do
- end
- end
-
- context "and the user is changed from system to not-system" do
- let(:existing_system) { true }
- let(:system) { false }
-
- let(:expect_updated?) { false }
-
- it "does not modify the user at all" do
- end
- end
-
- end # when the user already exists
- end # action :create
-
- shared_context "user exists for lock/unlock" do
- let(:user_locked_context?) { false }
-
- def shadow_entry
- etc_shadow.lines.find { |l| l.include?(username) }
- end
-
- def shadow_password
- shadow_entry.split(":")[1]
- end
-
- def aix_user_lock_status
- lock_info = shell_out!("lsuser -a account_locked #{username}")
- /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)[1]
- end
-
- def user_account_should_be_locked
- case ohai[:platform]
- when "aix"
- expect(aix_user_lock_status).to eq("true")
- else
- expect(shadow_password).to include("!")
- end
- end
-
- def user_account_should_be_unlocked
- case ohai[:platform]
- when "aix"
- expect(aix_user_lock_status).to eq("false")
- else
- expect(shadow_password).not_to include("!")
- end
- end
-
- def lock_user_account
- case ohai[:platform]
- when "aix"
- shell_out!("chuser account_locked=true #{username}")
- else
- shell_out!("usermod -L #{username}")
- end
- end
-
- before do
- # create user and setup locked/unlocked state
- user_resource.dup.run_action(:create)
-
- if user_locked_context?
- lock_user_account
- user_account_should_be_locked
- elsif password
- user_account_should_be_unlocked
- end
- end
- end
-
- describe "action :lock" do
- context "when the user does not exist" do
- it "raises a sensible error" do
- expect { user_resource.run_action(:lock) }.to raise_error(Chef::Exceptions::User)
- end
- end
-
- context "when the user exists" do
-
- include_context "user exists for lock/unlock"
-
- before do
- user_resource.run_action(:lock)
- end
-
- context "and the user is not locked" do
- # user will be locked if it has no password
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- it "locks the user's password" do
- user_account_should_be_locked
- end
- end
-
- context "and the user is locked" do
- # user will be locked if it has no password
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- let(:user_locked_context?) { true }
- it "does not update the user" do
- expect(user_resource).not_to be_updated_by_last_action
- end
- end
- end
- end # action :lock
-
- describe "action :unlock" do
- context "when the user does not exist" do
- it "raises a sensible error" do
- expect { user_resource.run_action(:unlock) }.to raise_error(Chef::Exceptions::User)
- end
- end
-
- context "when the user exists" do
-
- include_context "user exists for lock/unlock"
-
- before do
- begin
- user_resource.run_action(:unlock)
- @error = nil
- rescue Exception => e
- @error = e
- end
- end
-
- context "and has no password" do
-
- # TODO: platform_family should be setup in spec_helper w/ tags
- if %w{suse opensuse}.include?(OHAI_SYSTEM["platform_family"])
- # suse gets this right:
- it "errors out trying to unlock the user" do
- expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed)
- expect(@error.message).to include("Cannot unlock the password")
- end
- else
-
- # borked on all other platforms:
- it "is marked as updated but doesn't modify the user (XXX)" do
- # This should be an error instead; note that usermod still exits 0
- # (which is probably why this case silently fails):
- #
- # DEBUG: ---- Begin output of usermod -U chef-functional-test ----
- # DEBUG: STDOUT:
- # DEBUG: STDERR: usermod: unlocking the user's password would result in a passwordless account.
- # You should set a password with usermod -p to unlock this user's password.
- # DEBUG: ---- End output of usermod -U chef-functional-test ----
- # DEBUG: Ran usermod -U chef-functional-test returned 0
- expect(@error).to be_nil
- if ohai[:platform] == "aix"
- expect(pw_entry.passwd).to eq("*")
- user_account_should_be_unlocked
- else
- expect(pw_entry.passwd).to eq("x")
- expect(shadow_password).to include("!")
- end
- end
- end
- end
-
- context "and has a password" do
- let(:password) do
- case ohai[:platform]
- when "aix"
- "eL5qfEVznSNss"
- else
- "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/"
- end
- end
-
- context "and the user is not locked" do
- it "does not update the user" do
- expect(user_resource).not_to be_updated_by_last_action
- end
- end
-
- context "and the user is locked" do
- let(:user_locked_context?) { true }
-
- it "unlocks the user's password" do
- user_account_should_be_unlocked
- end
- end
- end
- end
- end # action :unlock
-
-end
diff --git a/spec/functional/resource/user/windows_spec.rb b/spec/functional/resource/user/windows_spec.rb
index f61a51c636..72db110266 100644
--- a/spec/functional/resource/user/windows_spec.rb
+++ b/spec/functional/resource/user/windows_spec.rb
@@ -1,5 +1,7 @@
# Author:: Jay Mundrawala (<jdm@chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software
+# Author:: Stuart Preston (<stuart@chef.io>)
+#
+# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +24,7 @@ describe Chef::Provider::User::Windows, :windows_only do
include Chef::Mixin::ShellOut
let(:username) { "ChefFunctionalTest" }
- let(:password) { SecureRandom.uuid }
+ let(:password) { "DummyP2ssw0rd!" }
let(:node) do
n = Chef::Node.new
@@ -31,9 +33,10 @@ describe Chef::Provider::User::Windows, :windows_only do
end
let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:logger) { double("Mixlib::Log::Child").as_null_object }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
let(:new_resource) do
- Chef::Resource::User.new(username, run_context).tap do |r|
+ Chef::Resource::User::WindowsUser.new(username, run_context).tap do |r|
r.provider(Chef::Provider::User::Windows)
r.password(password)
end
@@ -43,33 +46,142 @@ describe Chef::Provider::User::Windows, :windows_only do
shell_out("net user #{u} /delete")
end
- before do
+ def backup_secedit_policy
+ backup_command = "secedit /export /cfg #{ENV["TEMP"]}\\secedit_restore.inf /areas SECURITYPOLICY"
+ shell_out(backup_command)
+ end
+
+ def restore_secedit_policy
+ security_database = "C:\\windows\\security\\database\\seceditnew.sdb"
+ restore_command = "secedit /configure /db #{security_database} /cfg #{ENV["TEMP"]}\\secedit_restore.inf /areas SECURITYPOLICY"
+ shell_out(restore_command)
+ end
+
+ def set_windows_minimum_password_length(minimum_password_length = 0)
+ require "tempfile"
+ temp_security_database = "C:\\windows\\security\\database\\seceditnew.sdb"
+ temp_security_template = Tempfile.new(["chefpolicy", ".inf"])
+ file_content = <<~EOF
+ [Unicode]
+ Unicode=yes
+ [System Access]
+ MinimumPasswordLength = #{minimum_password_length}
+ PasswordComplexity = 0
+ [Version]
+ signature="$CHICAGO$"
+ Revision=1
+ EOF
+ windows_template_path = temp_security_template.path.gsub("/") { "\\" }
+ security_command = "secedit /configure /db #{temp_security_database} /cfg #{windows_template_path} /areas SECURITYPOLICY"
+ temp_security_template.write(file_content)
+ temp_security_template.close
+ shell_out(security_command)
+ end
+
+ before(:all) do
+ backup_secedit_policy
+ end
+
+ before(:each) do
delete_user(username)
+ allow(run_context).to receive(:logger).and_return(logger)
+ end
+
+ after(:all) do
+ restore_secedit_policy
end
describe "action :create" do
- it "creates a user when a username and password are given" do
- new_resource.run_action(:create)
- expect(new_resource).to be_updated_by_last_action
- expect(shell_out("net user #{username}").exitstatus).to eq(0)
- end
+ context "on a Windows system with a policy that requires non-blank passwords and no complexity requirements" do
- it "reports no changes if there are no changes needed" do
- new_resource.run_action(:create)
- new_resource.run_action(:create)
- expect(new_resource).not_to be_updated_by_last_action
+ before(:all) do
+ set_windows_minimum_password_length(1)
+ end
+
+ context "when a username and non-empty password are given" do
+ it "creates a user" do
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it "is idempotent" do
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "allows changing the password" do
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when a username and empty password are given" do
+ it "does not create the specified user" do
+ new_resource.password("")
+ expect { new_resource.run_action(:create) }.to raise_exception(Chef::Exceptions::Win32APIError, /The password does not meet the password policy requirements/)
+ end
+ end
end
- it "allows chaning the password" do
- new_resource.run_action(:create)
- new_resource.password(SecureRandom.uuid)
- new_resource.run_action(:create)
- expect(new_resource).to be_updated_by_last_action
+ context "on a Windows system with a policy that allows blank passwords" do
+
+ before(:all) do
+ set_windows_minimum_password_length(0)
+ end
+
+ context "when a username and non-empty password are given" do
+ it "creates a user" do
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it "is idempotent" do
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "allows changing the password" do
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ context "when a username and empty password are given" do
+ it "creates a user" do
+ new_resource.password("")
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it "is idempotent" do
+ new_resource.password("")
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it "allows changing the password from empty to a value" do
+ new_resource.password("")
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
end
context "with a gid specified" do
it "warns unsupported" do
- expect(Chef::Log).to receive(:warn).with(/not implemented/)
+ expect(logger).to receive(:warn).with(/not implemented/)
new_resource.gid("agroup")
new_resource.run_action(:create)
end
diff --git a/spec/functional/resource/windows_certificate_spec.rb b/spec/functional/resource/windows_certificate_spec.rb
new file mode 100644
index 0000000000..b5d0484e0c
--- /dev/null
+++ b/spec/functional/resource/windows_certificate_spec.rb
@@ -0,0 +1,316 @@
+# Author: Nimesh Patni (nimesh.patni@msystechnologies.com)
+# Copyright:: Copyright (c) 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 "chef/mixin/powershell_exec"
+require "chef/resource/windows_certificate"
+
+describe Chef::Resource::WindowsCertificate, :windows_only do
+ include Chef::Mixin::PowershellExec
+
+ def create_store
+ powershell_exec <<~EOC
+ New-Item -Path Cert:\\LocalMachine\\#{store}
+ EOC
+ end
+
+ def delete_store
+ powershell_exec <<~EOC
+ Remove-Item -Path Cert:\\LocalMachine\\#{store} -Recurse
+ EOC
+ end
+
+ def certificate_count
+ powershell_exec(<<~EOC).result.to_i
+ (Get-ChildItem -Force -Path Cert:\\LocalMachine\\#{store} | measure).Count
+ EOC
+ end
+
+ let(:password) { "P@ssw0rd!" }
+ let(:store) { "Chef-Functional-Test" }
+ let(:cer_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.cer") }
+ let(:base64_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "base64_test.cer") }
+ let(:pem_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.pem") }
+ let(:p7b_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.p7b") }
+ let(:pfx_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "test.pfx") }
+ let(:tests_thumbprint) { "e45a4a7ff731e143cf20b8bfb9c7c4edd5238bb3" }
+ let(:other_cer_path) { File.join(CHEF_SPEC_DATA, "windows_certificates", "othertest.cer") }
+ let(:others_thumbprint) { "6eae1deefaf59daf1a97c9ceeff39c98b3da38cb" }
+ let(:p7b_thumbprint) { "f867e25b928061318ed2c36ca517681774b06260" }
+ let(:p7b_nested_thumbprint) { "dc395eae6be5b69951b8b6e1090cfc33df30d2cd" }
+
+ let(:resource) do
+ run_context = Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ Chef::Resource::WindowsCertificate.new("ChefFunctionalTest", run_context).tap do |r|
+ r.store_name = store
+ end
+ end
+
+ before do
+ # Bypass validation of the store name so we can use a fake test store.
+ allow_any_instance_of(Chef::Mixin::ParamsValidate)
+ .to receive(:_pv_equal_to)
+ .with({ store_name: store }, :store_name, anything)
+ .and_return(true)
+
+ create_store
+ end
+
+ after { delete_store }
+
+ describe "action: create" do
+ it "starts with no certificates" do
+ expect(certificate_count).to eq(0)
+ end
+
+ it "can add a certificate idempotently" do
+ resource.source = cer_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+ expect(resource).to be_updated_by_last_action
+
+ # Adding the cert again should have no effect
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+
+ # Adding the cert again with a different format should have no effect
+ resource.source = pem_path
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+
+ # Adding another cert should work correctly
+ resource.source = other_cer_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(2)
+ expect(resource).to be_updated_by_last_action
+ end
+
+ it "can add a base64 encoded certificate idempotently" do
+ resource.source = base64_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "can add a PEM certificate idempotently" do
+ resource.source = pem_path
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "can add a P7B certificate idempotently" do
+ resource.source = p7b_path
+ resource.run_action(:create)
+
+ # A P7B cert includes nested certs
+ expect(certificate_count).to eq(3)
+
+ resource.run_action(:create)
+
+ expect(resource).not_to be_updated_by_last_action
+ expect(certificate_count).to eq(3)
+ end
+
+ it "can add a PFX certificate idempotently with a valid password" do
+ resource.source = pfx_path
+ resource.pfx_password = password
+ resource.run_action(:create)
+
+ expect(certificate_count).to eq(1)
+
+ resource.run_action(:create)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "raises an error when adding a PFX certificate with an invalid password" do
+ resource.source = pfx_path
+ resource.pfx_password = "Invalid password"
+
+ expect { resource.run_action(:create) }.to raise_error(OpenSSL::PKCS12::PKCS12Error)
+ end
+ end
+
+ describe "action: verify" do
+ it "fails with no certificates in the store" do
+ expect(Chef::Log).to receive(:info).with("Certificate not found")
+
+ resource.source = tests_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ context "with a certificate in the store" do
+ before do
+ resource.source = cer_path
+ resource.run_action(:create)
+ end
+
+ it "succeeds with a valid thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+
+ resource.source = tests_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "fails with an invalid thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate not found")
+
+ resource.source = others_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+
+ context "with a nested certificate in the store" do
+ before do
+ resource.source = p7b_path
+ resource.run_action(:create)
+ end
+
+ it "succeeds with the main certificate's thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+
+ resource.source = p7b_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "succeeds with the nested certificate's thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+
+ resource.source = p7b_nested_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "fails with an invalid thumbprint" do
+ expect(Chef::Log).to receive(:info).with("Certificate not found")
+
+ resource.source = others_thumbprint
+ resource.run_action(:verify)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "action: fetch" do
+ it "does nothing with no certificates in the store" do
+ expect(Chef::Log).not_to receive(:info)
+
+ resource.source = tests_thumbprint
+ resource.run_action(:fetch)
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ context "with a certificate in the store" do
+ before do
+ resource.source = cer_path
+ resource.run_action(:create)
+ end
+
+ it "succeeds with a valid thumbprint" do
+ resource.source = tests_thumbprint
+
+ Dir.mktmpdir do |dir|
+ path = File.join(dir, "test.pem")
+ expect(Chef::Log).to receive(:info).with("Certificate export in #{path}")
+
+ resource.cert_path = path
+ resource.run_action(:fetch)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+
+ it "fails with an invalid thumbprint" do
+ expect(Chef::Log).not_to receive(:info)
+
+ resource.source = others_thumbprint
+
+ Dir.mktmpdir do |dir|
+ path = File.join(dir, "test.pem")
+
+ resource.cert_path = path
+ resource.run_action(:fetch)
+
+ expect(File.exist?(path)).to be_falsy
+ end
+
+ expect(resource).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "action: delete" do
+ it "does nothing when attempting to delete a certificate that doesn't exist" do
+ expect(Chef::Log).to receive(:debug).with("Certificate not found")
+
+ resource.source = tests_thumbprint
+ resource.run_action(:delete)
+ end
+
+ it "deletes an existing certificate while leaving other certificates alone" do
+ # Add two certs
+ resource.source = cer_path
+ resource.run_action(:create)
+
+ resource.source = other_cer_path
+ resource.run_action(:create)
+
+ # Delete the first cert added
+ resource.source = tests_thumbprint
+ resource.run_action(:delete)
+
+ expect(certificate_count).to eq(1)
+ expect(resource).to be_updated_by_last_action
+
+ resource.run_action(:delete)
+ expect(certificate_count).to eq(1)
+ expect(resource).not_to be_updated_by_last_action
+
+ # Verify second cert still exists
+ expect(Chef::Log).to receive(:info).with("Certificate is valid")
+ resource.source = others_thumbprint
+ resource.run_action(:verify)
+ end
+ end
+end
diff --git a/spec/functional/resource/windows_env_spec.rb b/spec/functional/resource/windows_env_spec.rb
new file mode 100644
index 0000000000..bbcbf393e2
--- /dev/null
+++ b/spec/functional/resource/windows_env_spec.rb
@@ -0,0 +1,285 @@
+#
+# Author:: Adam Edwards (<adamed@chef.io>)
+# Copyright:: Copyright (c) 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::WindowsEnv, :windows_only do
+ context "when running on Windows" do
+ let(:chef_env_test_lower_case) { "chefenvtest" }
+ let(:chef_env_test_mixed_case) { "chefENVtest" }
+ let(:chef_env_with_delim) { "chef_env_with_delim" }
+ let(:chef_env_delim) { ";" }
+ let(:chef_env_test_delim) { "#{value1};#{value2}" }
+ let(:env_dne_key) { "env_dne_key" }
+ let(:env_value1) { "value1" }
+ let(:env_value2) { "value2" }
+ let(:delim_value) { "#{env_value1};#{env_value2}" }
+ let(:env_user) { ENV["USERNAME"].upcase }
+ let(:default_env_user) { "<SYSTEM>" }
+
+ let(:env_obj) do
+ wmi = WmiLite::Wmi.new
+ environment_variables = wmi.query("select * from Win32_Environment where name = '#{test_resource.key_name}'")
+ if environment_variables && environment_variables.length > 0
+ environment_variables.each do |env|
+ env_obj = env.wmi_ole_object
+ return env_obj if env_obj.username.split('\\').last.casecmp(test_resource.user) == 0
+ end
+ end
+ nil
+ end
+
+ let(:env_value_expandable) { "%SystemRoot%" }
+ let(:test_run_context) do
+ node = Chef::Node.new
+ node.default["os"] = "windows"
+ node.default["platform"] = "windows"
+ node.default["platform_version"] = "6.1"
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+ let(:test_resource) do
+ Chef::Resource::WindowsEnv.new("unknown", test_run_context)
+ end
+
+ before(:each) do
+ resource_lower = Chef::Resource::WindowsEnv.new(chef_env_test_lower_case, test_run_context)
+ resource_lower.run_action(:delete)
+ resource_lower = Chef::Resource::WindowsEnv.new(chef_env_test_lower_case, test_run_context)
+ resource_lower.user(env_user)
+ resource_lower.run_action(:delete)
+ resource_mixed = Chef::Resource::WindowsEnv.new(chef_env_test_mixed_case, test_run_context)
+ resource_mixed.run_action(:delete)
+ resource_mixed = Chef::Resource::WindowsEnv.new(chef_env_test_mixed_case, test_run_context)
+ resource_lower.user(env_user)
+ resource_mixed.run_action(:delete)
+ end
+
+ context "when the create action is invoked" do
+ it "should create an environment variable for action create" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ end
+
+ it "should create an environment variable with default user System for action create" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ expect(env_obj.username.upcase).to eq(default_env_user)
+ end
+
+ it "should create an environment variable with user for action create" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.user(env_user)
+ test_resource.run_action(:create)
+ expect(env_obj.username.split('\\').last.upcase).to eq(env_user)
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ end
+ it "should modify an existing variable's value to a new value" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value2)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+
+ it "should not modify an existing variable's value to a new value if the users are different" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value2)
+ test_resource.user(env_user)
+ test_resource.run_action(:create)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.user(default_env_user)
+ expect(env_obj.variablevalue).to eq(env_value1)
+ end
+
+ it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.key_name(chef_env_test_mixed_case)
+ test_resource.value(env_value2)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+ end
+
+ it "should not expand environment variables if the variable is not PATH" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value_expandable)
+ test_resource.run_action(:create)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
+ end
+ end
+
+ context "when the modify action is invoked" do
+ it "should raise an exception for modify if the variable doesn't exist" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ expect { test_resource.run_action(:modify) }.to raise_error(Chef::Exceptions::WindowsEnv)
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ end
+
+ it "should modify an existing variable's value to a new value" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value2)
+ test_resource.run_action(:modify)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+
+ # This example covers Chef Issue #1754
+ it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.key_name(chef_env_test_mixed_case)
+ test_resource.value(env_value2)
+ test_resource.run_action(:modify)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value2)
+ end
+
+ it "should not expand environment variables if the variable is not PATH" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.value(env_value_expandable)
+ test_resource.run_action(:modify)
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable)
+ end
+ end
+
+ context "when using PATH" do
+ let(:random_name) { Time.now.to_i }
+ let(:env_val) { "#{env_value_expandable}_#{random_name}" }
+ let!(:path_before) { test_resource.provider_for_action(test_resource.action).env_value("PATH") || "" }
+ let!(:env_path_before) { ENV["PATH"] }
+
+ it "should expand PATH" do
+ expect(path_before).not_to include(env_val)
+ test_resource.key_name("PATH")
+ test_resource.value("#{path_before};#{env_val}")
+ test_resource.run_action(:create)
+ expect(ENV["PATH"]).not_to include(env_val)
+ expect(ENV["PATH"]).to include((random_name).to_s)
+ end
+
+ after(:each) do
+ # cleanup so we don't flood the path
+ test_resource.key_name("PATH")
+ test_resource.value(path_before)
+ test_resource.run_action(:create)
+ ENV["PATH"] = env_path_before
+ end
+ end
+
+ end
+
+ context "when the delete action is invoked" do
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.run_action(:create)
+ end
+
+ it "should delete a System environment variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.run_action(:delete)
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ end
+
+ it "should not delete an System environment variable if user are passed" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.user(env_user)
+ test_resource.run_action(:delete)
+ test_resource.user(default_env_user)
+ expect(env_obj).not_to be_nil
+ end
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ test_resource.user(env_user)
+ test_resource.run_action(:create)
+ end
+
+ it "should delete a user environment variable" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.run_action(:delete)
+ expect(env_obj).to eq(nil)
+ end
+
+ it "should not delete an user environment variable if user is not passed" do
+ expect(ENV[chef_env_test_lower_case]).to eq(env_value1)
+ test_resource.user(default_env_user)
+ test_resource.run_action(:delete)
+ test_resource.user(env_user)
+ expect(env_obj).not_to be_nil
+ end
+ end
+
+ context "when env variable exist with same name" do
+ before(:each) do
+ test_resource.key_name(chef_env_with_delim)
+ test_resource.delim(chef_env_delim)
+ test_resource.value(delim_value)
+ test_resource.run_action(:create)
+ end
+
+ it "should not delete variable when a delim present" do
+ expect(ENV[chef_env_with_delim]).to eq(delim_value)
+ test_resource.value(env_value1)
+ test_resource.run_action(:delete)
+ expect(ENV[chef_env_with_delim]).to eq(env_value2)
+ end
+ end
+
+ it "should not raise an exception when a non-existent environment variable is deleted" do
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ test_resource.key_name(chef_env_test_lower_case)
+ test_resource.value(env_value1)
+ expect { test_resource.run_action(:delete) }.not_to raise_error
+ expect(ENV[chef_env_test_lower_case]).to eq(nil)
+ end
+
+ it "should delete a value from the current process even if it is not in the registry" do
+ expect(ENV[env_dne_key]).to eq(nil)
+ ENV[env_dne_key] = env_value1
+ test_resource.key_name(env_dne_key)
+ test_resource.run_action(:delete)
+ expect(ENV[env_dne_key]).to eq(nil)
+ end
+
+ end
+ end
+end
diff --git a/spec/functional/resource/windows_firewall_rule_spec.rb b/spec/functional/resource/windows_firewall_rule_spec.rb
new file mode 100644
index 0000000000..86220c1b71
--- /dev/null
+++ b/spec/functional/resource/windows_firewall_rule_spec.rb
@@ -0,0 +1,93 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 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 "chef/mixin/powershell_exec"
+
+describe Chef::Resource::WindowsFirewallRule, :windows_only do
+ include Chef::Mixin::PowershellExec
+
+ let(:rule_name) { "fake_rule" }
+ let(:remote_port) { "5555" }
+ let(:enabled) { false }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsFirewallRule.new("test firewall rule", run_context)
+ new_resource.rule_name rule_name
+ new_resource.remote_port remote_port
+ new_resource.enabled enabled
+ new_resource
+ end
+
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
+ context "create a new rule" do
+ after { delete_rule }
+
+ it "creates the rule" do
+ subject.run_action(:create)
+ expect(get_installed_rule_name).to eq(rule_name)
+ expect(get_installed_rule_remote_port).to eq(remote_port)
+ end
+
+ it "does not create rule if it already exists" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates the rule if it changed" do
+ subject.run_action(:create)
+ subject.remote_port = "7777"
+ subject.run_action(:create)
+ expect(get_installed_rule_remote_port).to eq("7777")
+ end
+ end
+
+ context "delete a rule" do
+ it "deletes an existing rule" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ expect(get_installed_rule_name).to be_empty
+ end
+
+ it "does not delete rule if it does not exist" do
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ def get_installed_rule_name
+ powershell_exec!("(Get-NetFirewallRule -Name #{rule_name} -ErrorAction SilentlyContinue).Name").result
+ end
+
+ def get_installed_rule_remote_port
+ powershell_exec!("((Get-NetFirewallRule -Name #{rule_name} -ErrorAction SilentlyContinue) | Get-NetFirewallPortFilter).RemotePort").result
+ end
+
+ def delete_rule
+ rule_to_remove = Chef::Resource::WindowsFirewallRule.new(rule_name, run_context)
+ rule_to_remove.run_action(:delete)
+ end
+end
diff --git a/spec/functional/resource/windows_font_spec.rb b/spec/functional/resource/windows_font_spec.rb
new file mode 100644
index 0000000000..e46e4aca17
--- /dev/null
+++ b/spec/functional/resource/windows_font_spec.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Dheeraj Singh Dubey (<ddubey@chef.io>)
+# Copyright:: Copyright (c) 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::WindowsFont, :windows_only do
+ let(:resource_name) { "Playmaker.ttf" }
+ let(:resource_source) { "https://www.wfonts.com/download/data/2020/05/06/playmaker/Playmaker.ttf" }
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default[:platform] = ohai[:platform]
+ node.default[:platform_version] = ohai[:platform_version]
+ node.default[:os] = ohai[:os]
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ end
+
+ subject do
+ resource = Chef::Resource::WindowsFont.new(resource_name, run_context)
+ resource.source resource_source
+ resource
+ end
+
+ it "installs font on first install" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "does not install font when already installed" do
+ subject.run_action(:install)
+ expect(subject).not_to be_updated_by_last_action
+ end
+end
diff --git a/spec/functional/resource/windows_package_spec.rb b/spec/functional/resource/windows_package_spec.rb
index bc508dc526..5fed41e9ae 100644
--- a/spec/functional/resource/windows_package_spec.rb
+++ b/spec/functional/resource/windows_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Matt Wrock (<matt@mattwrock.com>)
-# Copyright:: Copyright 2015-2016, 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");
@@ -17,7 +17,6 @@
#
require "spec_helper"
-require "functional/resource/base"
describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
let(:pkg_name) { nil }
@@ -26,6 +25,10 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
let(:pkg_version) { nil }
let(:pkg_type) { nil }
let(:pkg_options) { nil }
+ let(:remote_file_attributes) { nil }
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
subject do
new_resource = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
@@ -34,13 +37,14 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
new_resource.installer_type pkg_type
new_resource.options pkg_options
new_resource.checksum pkg_checksum
+ new_resource.remote_file_attributes
new_resource
end
describe "install package" do
let(:pkg_name) { "Microsoft Visual C++ 2005 Redistributable" }
- let(:pkg_checksum) { "d6832398e3bc9156a660745f427dc1c2392ce4e9a872e04f41f62d0c6bae07a8" }
- let(:pkg_path) { "https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe" }
+ let(:pkg_checksum) { "4ee4da0fe62d5fa1b5e80c6e6d88a4a2f8b3b140c35da51053d0d7b72a381d29" }
+ let(:pkg_path) { "https://download.microsoft.com/download/8/B/4/8B42259F-5D70-43F4-AC2E-4B208FD8D66A/vcredist_x86.EXE" }
let(:pkg_checksum) { nil }
let(:pkg_type) { :custom }
let(:pkg_options) { "/Q" }
@@ -56,9 +60,9 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
end
context "installing additional version" do
- let(:pkg_path) { "https://download.microsoft.com/download/e/1/c/e1c773de-73ba-494a-a5ba-f24906ecf088/vcredist_x86.exe" }
- let(:pkg_checksum) { "eb00f891919d4f894ab725b158459db8834470c382dc60cd3c3ee2c6de6da92c" }
- let(:pkg_version) { "8.0.56336" }
+ let(:pkg_path) { "https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe" }
+ let(:pkg_checksum) { "d6832398e3bc9156a660745f427dc1c2392ce4e9a872e04f41f62d0c6bae07a8" }
+ let(:pkg_version) { "8.0.59193" }
it "installs older version" do
subject.run_action(:install)
@@ -70,14 +74,14 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
subject { Chef::Resource::WindowsPackage.new(pkg_name, run_context) }
context "multiple versions and a version given to remove" do
- before { subject.version("8.0.56336") }
+ before { subject.version("8.0.59193") }
it "removes specified version" do
subject.run_action(:remove)
expect(subject).to be_updated_by_last_action
prov = subject.provider_for_action(:remove)
prov.load_current_resource
- expect(prov.current_version_array).to eq([["8.0.59193"]])
+ expect(prov.current_version_array).to eq([["8.0.61001"]])
end
end
@@ -102,8 +106,8 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
install1.run_action(:install)
install2 = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
- install2.source "https://download.microsoft.com/download/e/1/c/e1c773de-73ba-494a-a5ba-f24906ecf088/vcredist_x86.exe"
- install2.version "8.0.56336"
+ install2.source "https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe"
+ install2.version "8.0.59193"
install2.installer_type pkg_type
install2.options pkg_options
install2.run_action(:install)
@@ -136,7 +140,7 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
context "inno" do
let(:pkg_name) { "Mercurial 3.6.1 (64-bit)" }
- let(:pkg_path) { "http://mercurial.selenic.com/release/windows/Mercurial-3.6.1-x64.exe" }
+ let(:pkg_path) { "https://www.mercurial-scm.org/release/windows/Mercurial-3.6.1-x64.exe" }
let(:pkg_checksum) { "febd29578cb6736163d232708b834a2ddd119aa40abc536b2c313fc5e1b5831d" }
it "finds the correct installer type" do
@@ -165,4 +169,25 @@ describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
expect(subject).to be_updated_by_last_action
end
end
+
+ describe "install package with remote_file_attributes" do
+ let(:pkg_name) { "7zip" }
+ let(:pkg_path) { "http://www.7-zip.org/a/7z938-x64.msi" }
+ let(:remote_file_attributes) {
+ {
+ path: ::File.join(Chef::Config[:file_cache_path], "7zip.msi"),
+ checksum: "7c8e873991c82ad9cfcdbdf45254ea6101e9a645e12977dcd518979e50fdedf3",
+ }
+ }
+
+ it "installs the package" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "uninstalls the package" do
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
end
diff --git a/spec/functional/resource/windows_path_spec.rb b/spec/functional/resource/windows_path_spec.rb
new file mode 100644
index 0000000000..b2a3e5f5a4
--- /dev/null
+++ b/spec/functional/resource/windows_path_spec.rb
@@ -0,0 +1,68 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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::WindowsPath, :windows_only do
+ let(:path) { "test_path" }
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ before(:all) do
+ @old_path = ENV["PATH"].dup
+ end
+
+ after(:all) do
+ ENV["PATH"] = @old_path
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
+ new_resource
+ end
+
+ describe "adding path" do
+ after { remove_path }
+
+ it "appends the user given path in the Environment variable Path" do
+ subject.run_action(:add)
+ expect(ENV["PATH"]).to include(path)
+ end
+ end
+
+ describe "removing path" do
+ before { add_path }
+
+ it "removes the user given path from the Environment variable Path" do
+ subject.run_action(:remove)
+ expect(ENV["PATH"]).not_to include(path)
+ end
+ end
+
+ def remove_path
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
+ new_resource.run_action(:remove)
+ end
+
+ def add_path
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
+ new_resource.run_action(:add)
+ end
+end
diff --git a/spec/functional/resource/windows_security_policy_spec.rb b/spec/functional/resource/windows_security_policy_spec.rb
new file mode 100644
index 0000000000..76764e01b0
--- /dev/null
+++ b/spec/functional/resource/windows_security_policy_spec.rb
@@ -0,0 +1,86 @@
+#
+# Author:: Ashwini Nehate (<anehate@chef.io>)
+# Copyright:: Copyright (c) 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::WindowsSecurityPolicy, :windows_only do
+ let(:secoption) { "MaximumPasswordAge" }
+ let(:secvalue) { "30" }
+ let(:windows_test_run_context) do
+ node = Chef::Node.new
+ node.consume_external_attrs(OHAI_SYSTEM.data, {}) # node[:languages][:powershell][:version]
+ node.automatic["os"] = "windows"
+ node.automatic["platform"] = "windows"
+ node.automatic["platform_version"] = "6.1"
+ node.automatic["kernel"][:machine] = :x86_64 # Only 64-bit architecture is supported
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsSecurityPolicy.new(secoption, windows_test_run_context)
+ new_resource.secoption = secoption
+ new_resource.secvalue = secvalue
+ new_resource
+ end
+
+ describe "Set MaximumPasswordAge Policy" do
+ after {
+ subject.secvalue("60")
+ subject.run_action(:set)
+ }
+
+ it "should set MaximumPasswordAge to 30" do
+ subject.secvalue("30")
+ subject.run_action(:set)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "should be idempotent" do
+ subject.secvalue("30")
+ subject.run_action(:set)
+ guardscript_and_script_time = subject.elapsed_time
+ subject.run_action(:set)
+ only_guardscript_time = subject.elapsed_time
+ expect(only_guardscript_time).to be < guardscript_and_script_time
+ end
+ end
+
+ describe "secoption and id: " do
+ it "accepts 'MinimumPasswordAge', 'MinimumPasswordAge', 'MaximumPasswordAge', 'MinimumPasswordLength', 'PasswordComplexity', 'PasswordHistorySize', 'LockoutBadCount', 'RequireLogonToChangePassword', 'ForceLogoffWhenHourExpire', 'NewAdministratorName', 'NewGuestName', 'ClearTextPassword', 'LSAAnonymousNameLookup', 'EnableAdminAccount', 'EnableGuestAccount' " do
+ expect { subject.secoption("MinimumPasswordAge") }.not_to raise_error
+ expect { subject.secoption("MaximumPasswordAge") }.not_to raise_error
+ expect { subject.secoption("MinimumPasswordLength") }.not_to raise_error
+ expect { subject.secoption("PasswordComplexity") }.not_to raise_error
+ expect { subject.secoption("PasswordHistorySize") }.not_to raise_error
+ expect { subject.secoption("LockoutBadCount") }.not_to raise_error
+ expect { subject.secoption("RequireLogonToChangePassword") }.not_to raise_error
+ expect { subject.secoption("ForceLogoffWhenHourExpire") }.not_to raise_error
+ expect { subject.secoption("NewAdministratorName") }.not_to raise_error
+ expect { subject.secoption("NewGuestName") }.not_to raise_error
+ expect { subject.secoption("ClearTextPassword") }.not_to raise_error
+ expect { subject.secoption("LSAAnonymousNameLookup") }.not_to raise_error
+ expect { subject.secoption("EnableAdminAccount") }.not_to raise_error
+ expect { subject.secoption("EnableGuestAccount") }.not_to raise_error
+ end
+
+ it "rejects any other option" do
+ expect { subject.secoption "XYZ" }.to raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/functional/resource/windows_service_spec.rb b/spec/functional/resource/windows_service_spec.rb
index 531f9e9250..4c0c3acb58 100644
--- a/spec/functional/resource/windows_service_spec.rb
+++ b/spec/functional/resource/windows_service_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Chris Doherty (<cdoherty@chef.io>)
-# Copyright:: Copyright 2014-2016, 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");
@@ -18,13 +18,12 @@
require "spec_helper"
-describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only, :appveyor_only, :broken => true do
- # Marking as broken. This test is causing appveyor tests to exit with 116.
+describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only do
include_context "using Win32::Service"
let(:username) { "service_spec_user" }
- let(:qualified_username) { "#{ENV['COMPUTERNAME']}\\#{username}" }
+ let(:qualified_username) { "#{ENV["COMPUTERNAME"]}\\#{username}" }
let(:password) { "1a2b3c4X!&narf" }
let(:user_resource) do
@@ -36,7 +35,7 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
end
let(:global_service_file_path) do
- "#{ENV['WINDIR']}\\temp\\#{File.basename(test_service[:service_file_path])}"
+ "#{ENV["WINDIR"]}\\temp\\#{File.basename(test_service[:service_file_path])}"
end
let(:service_params) do
@@ -59,10 +58,14 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
let(:service_resource) do
r = Chef::Resource::WindowsService.new(service_params[:service_name], run_context)
- [:run_as_user, :run_as_password].each { |prop| r.send(prop, service_params[prop]) }
+ %i{run_as_user run_as_password}.each { |prop| r.send(prop, service_params[prop]) }
r
end
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
before do
user_resource.run_action(:create)
diff --git a/spec/functional/resource/windows_share_spec.rb b/spec/functional/resource/windows_share_spec.rb
new file mode 100644
index 0000000000..6fae7d45d3
--- /dev/null
+++ b/spec/functional/resource/windows_share_spec.rb
@@ -0,0 +1,103 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright (c) 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 "chef/mixin/powershell_exec"
+
+describe Chef::Resource::WindowsShare, :windows_only do
+ include Chef::Mixin::PowershellExec
+
+ let(:share_name) { "fake_share" }
+ let(:path) { ENV["temp"] }
+ let(:concurrent_user_limit) { 7 }
+ let(:full_users) { ["#{ENV["USERNAME"]}"] }
+
+ let(:run_context) do
+ node = Chef::Node.new
+ node.default["hostname"] = ENV["COMPUTERNAME"]
+ Chef::RunContext.new(node, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsShare.new("test windows share", run_context)
+ new_resource.share_name share_name
+ new_resource.path path
+ new_resource.concurrent_user_limit concurrent_user_limit
+ new_resource.full_users full_users
+ new_resource
+ end
+
+ let(:provider) do
+ provider = subject.provider_for_action(subject.action)
+ provider
+ end
+
+ context "create a new share" do
+ after { delete_share }
+
+ it "creates the share" do
+ subject.run_action(:create)
+ share = get_installed_share
+ expect(share["Name"]).to eq(share_name)
+ expect(share["Path"]).to eq(path)
+ expect(get_installed_share_access["AccountName"]).to eq("#{ENV["COMPUTERNAME"]}\\#{full_users[0]}")
+ end
+
+ it "does not create share if it already exists" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates the share if it changed" do
+ subject.run_action(:create)
+ subject.concurrent_user_limit 8
+ subject.full_users ["BUILTIN\\Administrators"]
+ subject.run_action(:create)
+ share = get_installed_share
+ expect(share["ConcurrentUserLimit"]).to eq(8)
+ expect(get_installed_share_access["AccountName"]).to eq("BUILTIN\\Administrators")
+ end
+
+ end
+
+ context "delete a share" do
+ it "deletes an existing share" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ expect(get_installed_share).to be_empty
+ end
+
+ it "does not delete share if it does not exist" do
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ def get_installed_share
+ powershell_exec!("Get-SmbShare -Name #{share_name} -ErrorAction SilentlyContinue").result
+ end
+
+ def get_installed_share_access
+ powershell_exec!("Get-SmbShareAccess -Name #{share_name} -ErrorAction SilentlyContinue").result
+ end
+
+ def delete_share
+ rule_to_remove = Chef::Resource::WindowsShare.new(share_name, run_context)
+ rule_to_remove.run_action(:delete)
+ end
+end
diff --git a/spec/functional/resource/windows_task_spec.rb b/spec/functional/resource/windows_task_spec.rb
new file mode 100644
index 0000000000..3affa625fd
--- /dev/null
+++ b/spec/functional/resource/windows_task_spec.rb
@@ -0,0 +1,1969 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "chef-utils/dist"
+
+describe Chef::Resource::WindowsTask, :windows_only do
+ # resource.task.application_name will default to task_name unless resource.command is set
+ let(:task_name) { "chef-client-functional-test" }
+ let(:new_resource) { Chef::Resource::WindowsTask.new(task_name, run_context) }
+ let(:windows_task_provider) do
+ new_resource.provider_for_action(:create)
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ describe "action :create" do
+ after { delete_task }
+ context "when command is with arguments" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ # Make sure MM/DD/YYYY is accepted
+
+ new_resource.start_day "09/20/2017"
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ context "With Arguments" do
+ it "creates scheduled task and sets command arguments" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(ChefUtils::Dist::Infra::CLIENT)
+ expect(current_resource.task.parameters).to eq("-W")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W"
+ subject.run_action(:create)
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W"
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments when arguments inclusive single quotes" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(ChefUtils::Dist::Infra::CLIENT)
+ expect(current_resource.task.parameters).to eq("-W -L 'C:\\chef\\chef-ad-join.log'")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'"
+ subject.run_action(:create)
+ subject.command "#{ChefUtils::Dist::Infra::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'"
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments with spaces in command" do
+ subject.command '"C:\\Program Files\\example\\program.exe" -arg1 --arg2'
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq('"C:\\Program Files\\example\\program.exe"')
+ expect(current_resource.task.parameters).to eq("-arg1 --arg2")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command '"C:\\Program Files\\example\\program.exe" -arg1 --arg2'
+ subject.run_action(:create)
+ subject.command '"C:\\Program Files\\example\\program.exe" -arg1 --arg2'
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments with spaces in arguments" do
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq("powershell.exe")
+ expect(current_resource.task.parameters).to eq('-file "C:\\Program Files\\app\\script.ps1"')
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ subject.run_action(:create)
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task and sets command arguments" do
+ subject.command "ping http://www.google.com"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq("ping")
+ expect(current_resource.task.parameters).to eq("http://www.google.com")
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command "ping http://www.google.com"
+ subject.run_action(:create)
+ subject.command "ping http://www.google.com"
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "Without Arguments" do
+ it "creates scheduled task and sets command arguments" do
+ subject.command ChefUtils::Dist::Infra::CLIENT
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(ChefUtils::Dist::Infra::CLIENT)
+ expect(current_resource.task.parameters).to be_empty
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.command ChefUtils::Dist::Infra::CLIENT
+ subject.run_action(:create)
+ subject.command ChefUtils::Dist::Infra::CLIENT
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ it "creates scheduled task and Re-sets command arguments" do
+ subject.command 'powershell.exe -file "C:\\Program Files\\app\\script.ps1"'
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.application_name).to eq("powershell.exe")
+ expect(current_resource.task.parameters).to eq('-file "C:\\Program Files\\app\\script.ps1"')
+
+ subject.command "powershell.exe"
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.application_name).to eq("powershell.exe")
+ expect(current_resource.task.parameters).to be_empty
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ context "when description is passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource.command task_name
+ # Make sure MM/DD/YYYY is accepted
+ new_resource.start_day "09/20/2017"
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ let(:some_description) { "this is test description" }
+
+ it "create the task and sets its description" do
+ subject.description some_description
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.description).to eq(some_description)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.description some_description
+ subject.run_action(:create)
+ subject.description some_description
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates task with new description if task already exist" do
+ subject.description some_description
+ subject.run_action(:create)
+ subject.description "test description"
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ context "when frequency_modifier are not passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ # Make sure MM/DD/YYYY is accepted
+ new_resource.start_day "09/20/2017"
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "creates a scheduled task to run every 1 hr starting on 09/20/2017" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(task_name)
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:start_year]).to eq("2017")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("20")
+ expect(trigger_details[:minutes_interval]).to eq(60)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "frequency :minute" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :minute
+ new_resource.frequency_modifier 15
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates a scheduled task that runs after every 15 minutes" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:minutes_interval]).to eq(15)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates a scheduled task when frequency_modifier updated to 20" do
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:minutes_interval]).to eq(15)
+ subject.frequency_modifier 20
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ # #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:minutes_interval]).to eq(20)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ end
+ end
+
+ context "frequency :hourly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :hourly
+ new_resource.frequency_modifier 3
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates a scheduled task that runs after every 3 hrs" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:minutes_interval]).to eq(180)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates a scheduled task to run every 5 hrs when frequency modifier updated to 5" do
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:minutes_interval]).to eq(180)
+ # updating frequency modifier to 5 from 3
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:minutes_interval]).to eq(300)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ end
+ end
+
+ context "frequency :daily" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :daily
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates a scheduled task to run daily" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(2)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days_interval]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "frequency :monthly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ context "with start_day and start_time" do
+ before do
+ subject.start_day "02/12/2018"
+ subject.start_time "05:15"
+ end
+
+ it "if day property is not set creates a scheduled task to run monthly on first day of the month" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on first, second and third day of the month" do
+ subject.day "1, 2, 3"
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(7)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1, 2, 3"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on 1, 2, 3, 4, 8, 20, 21, 15, 28, 31 day of the month" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 15, 28, 31"
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1209548943) # TODO:: windows_task_provider.send(:days_of_month)
+ expect(trigger_details[:type][:months]).to eq(4095) # windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 15, 28, 31"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on Jan, Feb, Apr, Dec on 1st 2nd 3rd 4th 8th and 20th day of these months" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 30"
+ subject.months "Jan, Feb, May, Sep, Dec"
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(538443919) # TODO:windows_task_provider.send(:days_of_month)
+ expect(trigger_details[:type][:months]).to eq(2323) # windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1, 2, 3, 4, 8, 20, 21, 30"
+ subject.months "Jan, Feb, May, Sep, Dec"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly by giving day option with frequency_modifier" do
+ subject.frequency_modifier "First"
+ subject.day "Mon, Fri, Sun"
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(5)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days_of_week]).to eq(35)
+ expect(trigger_details[:type][:weeks_of_month]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(4095) # windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "First"
+ subject.day "Mon, Fri, Sun"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "with frequency_modifier" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "raises argument error if frequency_modifier is 'first, second' and day is not provided." do
+ subject.frequency_modifier "first, second"
+ expect { subject.after_created }.to raise_error("Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be separated by comma.")
+ end
+
+ it "raises argument error if months is passed along with frequency_modifier" do
+ subject.frequency_modifier 3
+ subject.months "Jan, Mar"
+ expect { subject.after_created }.to raise_error("For frequency :monthly either use property months or frequency_modifier to set months.")
+ end
+
+ it "not raises any Argument error if frequency_modifier set as 'first, second, third' and day is provided" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error
+ end
+
+ it "not raises any Argument error if frequency_modifier 2 " do
+ subject.frequency_modifier 2
+ subject.day "Mon, Sun"
+ expect { subject.after_created }.not_to raise_error
+ end
+
+ it "raises argument error if frequency_modifier > 12" do
+ subject.frequency_modifier 13
+ expect { subject.after_created }.to raise_error("frequency_modifier value 13 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+
+ it "raises argument error if frequency_modifier < 1" do
+ subject.frequency_modifier 0
+ expect { subject.after_created }.to raise_error("frequency_modifier value 0 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+
+ it "creates scheduled task to run task monthly on Monday and Friday of first, second and third week of month" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(5)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:weeks_of_month]).to eq(7)
+ expect(trigger_details[:type][:days_of_week]).to eq(34)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "first, second, third"
+ subject.day "Mon, Fri"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates scheduled task to run task monthly on every 6 months when frequency_modifier is 6 and to run on 1st and 2nd day of month" do
+ subject.frequency_modifier 6
+ subject.day "1, 2"
+ expect { subject.after_created }.not_to raise_error
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(2080)
+ expect(trigger_details[:type][:days]).to eq(3)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier 6
+ subject.day "1, 2"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when day is set as last or lastday for frequency :monthly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates scheduled task to run monthly to run last day of the month" do
+ subject.day "last"
+ expect { subject.after_created }.not_to raise_error
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days]).to eq(0)
+ expect(trigger_details[:run_on_last_day_of_month]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "last"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "day property set as 'lastday' creates scheduled task to run monthly to run last day of the month" do
+ subject.day "lastday"
+ expect { subject.after_created }.not_to raise_error
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days]).to eq(0)
+ expect(trigger_details[:run_on_last_day_of_month]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "lastday"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when frequency_modifier is set as last for frequency :monthly" do
+ it "creates scheduled task to run monthly on last week of the month" do
+ subject.frequency_modifier "last"
+ subject.day "Mon, Fri"
+ expect { subject.after_created }.not_to raise_error
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(5)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days_of_week]).to eq(34)
+ expect(trigger_details[:run_on_last_week_of_month]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "last"
+ subject.day "Mon, Fri"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when wild card (*) set as months" do
+ it "creates the scheduled task to run on 1st day of the all months" do
+ subject.months "*"
+ expect { subject.after_created }.not_to raise_error
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ expect(trigger_details[:type][:days]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.months "*"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when wild card (*) set as day" do
+ it "raises argument error" do
+ subject.day "*"
+ expect { subject.after_created }.to raise_error("day wild card (*) is only valid with frequency :weekly")
+ end
+ end
+
+ context "Pass either start day or start time by passing day compulsory or only pass frequency_modifier" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :monthly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates a scheduled task to run monthly on second day of the month" do
+ subject.day "2"
+ subject.start_day "03/07/2018"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(2)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "2"
+ subject.start_day "03/07/2018"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on first, second and third day of the month" do
+ subject.day "1,2,3"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(7)
+ expect(trigger_details[:type][:months]).to eq(4095)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "1,2,3"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on each wednesday of the month" do
+ subject.frequency_modifier "1"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(4095) # windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "2"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "creates a scheduled task to run monthly on each wednesday of the month" do
+ subject.frequency_modifier "2"
+ subject.months = nil
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ # loading current resource
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(4)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:type][:days]).to eq(1)
+ expect(trigger_details[:type][:months]).to eq(2730) # windows_task_provider.send(:months_of_year)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency_modifier "2"
+ subject.months = nil
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ ## ToDO: Add functional specs to handle frequency monthly with frequency modifier set as 1-12
+ context "frequency :once" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ context "when start_time is not provided" do
+ it "raises argument error" do
+ expect { subject.after_created }.to raise_error("`start_time` needs to be provided with `frequency :once`")
+ end
+ end
+
+ context "when start_time is provided" do
+ it "creates the scheduled task to run once at 5pm" do
+ subject.start_time "17:00"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect("#{trigger_details[:start_hour]}:#{trigger_details[:start_minute]}" ).to eq(subject.start_time)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.start_time "17:00"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "frequency :weekly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :weekly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task to run weekly" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(1)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "when wild card (*) is set as day" do
+ it "creates hte scheduled task for all days of week" do
+ subject.day "*"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(1)
+ expect(trigger_details[:type][:days_of_week]).to eq(127)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "*"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when days are provided" do
+ it "creates the scheduled task to run on particular days" do
+ subject.day "Mon, Fri"
+ subject.frequency_modifier 2
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(2)
+ expect(trigger_details[:type][:days_of_week]).to eq(34)
+ end
+
+ it "updates the scheduled task to run on if frequency_modifier is updated" do
+ subject.day "sun"
+ subject.frequency_modifier 2
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:type][:weeks_interval]).to eq(2)
+ expect(trigger_details[:type][:days_of_week]).to eq(1)
+ subject.day "Mon, Sun"
+ subject.frequency_modifier 3
+ # call for update
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(3)
+ expect(trigger_details[:type][:days_of_week]).to eq(3)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.day "Mon, Fri"
+ subject.frequency_modifier 3
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when day property set as last" do
+ it "raises argument error" do
+ subject.day "last"
+ expect { subject.after_created }.to raise_error("day values 1-31 or last is only valid with frequency :monthly")
+ end
+ end
+
+ context "when invalid day is passed" do
+ it "raises error" do
+ subject.day "abc"
+ expect { subject.after_created }.to raise_error("day property invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN, *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "when months are passed" do
+ it "raises error that months are supported only when frequency=:monthly" do
+ subject.months "Jan"
+ expect { subject.after_created }.to raise_error("months property is only valid for tasks that run monthly")
+ end
+ end
+
+ context "when start_day is not set" do
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "updates the day if start_day is not provided and user updates day property" do
+ skip "Unable to run this test case since start_day is current system date which can be different each time so can't verify the dynamic values"
+ subject.run_action(:create)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(trigger_details[:type][:days_of_week]).to eq(8)
+ subject.day "Sat"
+ subject.run_action(:create)
+ # #loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(3)
+ expect(trigger_details[:type][:weeks_interval]).to eq(1)
+ expect(trigger_details[:type][:days_of_week]).to eq(64)
+ end
+ end
+ end
+
+ context "frequency :onstart" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :onstart
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task to run at system start up" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(8)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "when start_day and start_time is set" do
+ it "creates task to activate on '09/10/2018' at '15:00' when start_day = '09/10/2018' and start_time = '15:00' provided" do
+ subject.start_day "09/10/2018"
+ subject.start_time "15:00"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(8)
+ expect(trigger_details[:start_year]).to eq("2018")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("10")
+ expect(trigger_details[:start_hour]).to eq("15")
+ expect(trigger_details[:start_minute]).to eq("00")
+ end
+ end
+ end
+
+ context "frequency :on_logon" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :on_logon
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task to on logon" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(9)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "when start_day and start_time is set" do
+ it "creates task to activate on '09/10/2018' at '15:00' when start_day = '09/10/2018' and start_time = '15:00' provided" do
+ subject.start_day "09/10/2018"
+ subject.start_time "15:00"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(9)
+ expect(trigger_details[:start_year]).to eq("2018")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("10")
+ expect(trigger_details[:start_hour]).to eq("15")
+ expect(trigger_details[:start_minute]).to eq("00")
+ end
+ end
+ end
+
+ context "frequency :on_idle" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :on_idle
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ context "when idle_time is not passed" do
+ it "raises error" do
+ expect { subject.after_created }.to raise_error("idle_time value should be set for :on_idle frequency.")
+ end
+ end
+
+ context "when idle_time is passed" do
+ it "creates the scheduled task to run when system is idle" do
+ subject.idle_time 20
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(6)
+ expect(current_resource.task.settings[:idle_settings][:idle_duration]).to eq("PT20M")
+ expect(current_resource.task.settings[:run_only_if_idle]).to eq(true)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.idle_time 20
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when start_day and start_time is set" do
+ it "creates task to activate on '09/10/2018' at '15:00' when start_day = '09/10/2018' and start_time = '15:00' provided" do
+ subject.idle_time 20
+ subject.start_day "09/10/2018"
+ subject.start_time "15:00"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(trigger_details[:trigger_type]).to eq(6)
+ expect(trigger_details[:start_year]).to eq("2018")
+ expect(trigger_details[:start_month]).to eq("09")
+ expect(trigger_details[:start_day]).to eq("10")
+ expect(trigger_details[:start_hour]).to eq("15")
+ expect(trigger_details[:start_minute]).to eq("00")
+ end
+ end
+ end
+
+ context "when random_delay is passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "sets the random_delay for frequency :minute" do
+ subject.frequency :minute
+ subject.random_delay "20"
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ trigger_details = current_resource.task.trigger(0)
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(trigger_details[:trigger_type]).to eq(1)
+ expect(trigger_details[:random_minutes_interval]).to eq(20)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency :minute
+ subject.random_delay "20"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "raises error if invalid random_delay is passed" do
+ subject.frequency :minute
+ subject.random_delay "abc"
+ expect { subject.after_created }.to raise_error("Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60').")
+ end
+
+ it "raises error if random_delay is passed with frequency on_idle" do
+ subject.frequency :on_idle
+ subject.random_delay "20"
+ expect { subject.after_created }.to raise_error("`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly")
+ end
+ end
+
+ context "when battery options are passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "sets the default if options are not provided" do
+ subject.frequency :minute
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.stop_if_going_on_batteries).to eql(false)
+ expect(current_resource.disallow_start_if_on_batteries).to eql(false)
+ end
+
+ it "sets disallow_start_if_on_batteries to true" do
+ subject.frequency :minute
+ subject.disallow_start_if_on_batteries true
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:disallow_start_if_on_batteries]).to eql(true)
+ end
+
+ it "sets disallow_start_if_on_batteries to false" do
+ subject.frequency :minute
+ subject.disallow_start_if_on_batteries false
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:disallow_start_if_on_batteries]).to eql(false)
+ end
+
+ it "sets stop_if_going_on_batteries to true" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries true
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:stop_if_going_on_batteries]).to eql(true)
+ end
+
+ it "sets stop_if_going_on_batteries to false" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries false
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:stop_if_going_on_batteries]).to eql(false)
+ end
+
+ it "sets the default if options are nil" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries nil
+ subject.disallow_start_if_on_batteries nil
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:stop_if_going_on_batteries]).to eql(false)
+ expect(current_resource.task.settings[:disallow_start_if_on_batteries]).to eql(false)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries true
+ subject.disallow_start_if_on_batteries false
+ subject.run_action(:create)
+ subject.frequency :minute
+ subject.stop_if_going_on_batteries true
+ subject.disallow_start_if_on_batteries false
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "frequency :none" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :none
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task to run on demand only" do
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+
+ expect(current_resource.task.application_name).to eq(task_name)
+ expect(current_resource.task.principals[:run_level]).to eq(1)
+ expect(current_resource.task.trigger_count).to eq(0)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when start_when_available is passed" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "sets start_when_available to true" do
+ subject.frequency :minute
+ subject.start_when_available true
+ call_for_create_action
+ # loading current resource again to check new task is creted and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:start_when_available]).to eql(true)
+ end
+
+ it "sets start_when_available to false" do
+ subject.frequency :minute
+ subject.start_when_available false
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:start_when_available]).to eql(false)
+ end
+
+ it "sets the default if start_when_available is nil" do
+ subject.frequency :minute
+ subject.start_when_available nil
+ call_for_create_action
+ # loading current resource again to check new task is created and it matches task parameters
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.settings[:start_when_available]).to eql(false)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.frequency :minute
+ subject.start_when_available true
+ subject.run_action(:create)
+ subject.frequency :minute
+ subject.start_when_available true
+ subject.disallow_start_if_on_batteries false
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ context "task_name with parent folder" do
+ describe "task_name with path '\\foo\\chef-client-functional-test' " do
+ let(:task_name) { "\\foo\\chef-client-functional-test" }
+ after { delete_task }
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task with task name 'chef-client-functional-test' inside path '\\foo'" do
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(task_name)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "task_name with path '\\foo\\bar\\chef-client-functional-test' " do
+ let(:task_name) { "\\foo\\bar\\chef-client-functional-test" }
+ after { delete_task }
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "creates the scheduled task with task with name 'chef-client-functional-test' inside path '\\foo\\bar' " do
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(true)
+ expect(current_resource.task.application_name).to eq(task_name)
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "priority" do
+ after { delete_task }
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.frequency :once
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "default sets to 7" do
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("below_normal_7")
+ end
+
+ it "0 sets priority level to critical" do
+ subject.priority = 0
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("critical")
+ end
+
+ it "2 sets priority level to highest" do
+ subject.priority = 1
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("highest")
+ end
+
+ it "2 sets priority level to above_normal" do
+ subject.priority = 2
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("above_normal_2")
+ end
+
+ it "3 sets priority level to above_normal" do
+ subject.priority = 3
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("above_normal_3")
+ end
+
+ it "4 sets priority level to normal" do
+ subject.priority = 4
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("normal_4")
+ end
+
+ it "5 sets priority level to normal" do
+ subject.priority = 5
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("normal_5")
+ end
+
+ it "6 sets priority level to normal" do
+ subject.priority = 6
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("normal_6")
+ end
+
+ it "7 sets priority level to below_normal" do
+ subject.priority = 7
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("below_normal_7")
+ end
+
+ it "8 sets priority level to below_normal" do
+ subject.priority = 8
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("below_normal_8")
+ end
+
+ it "9 sets priority level to lowest" do
+ subject.priority = 9
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("lowest")
+ end
+
+ it "10 sets priority level to idle" do
+ subject.priority = 10
+ call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.priority).to eq("idle")
+ end
+
+ it "is idempotent" do
+ subject.priority 8
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ end
+
+ describe "Examples of idempotent checks for each frequency" do
+ after { delete_task }
+ context "For frequency :once" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.start_time "17:00"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 1" do
+ subject.frequency_modifier 1
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :none" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource.frequency :none
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 1" do
+ subject.frequency_modifier 1
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :weekly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :weekly
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding start_day" do
+ subject.start_day "12/28/2018"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.frequency_modifier 3
+ subject.random_delay "60"
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :monthly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :once
+ new_resource.start_time "17:00"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 1" do
+ subject.frequency_modifier 1
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :hourly" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :hourly
+ new_resource.frequency_modifier 5
+ new_resource.random_delay "2400"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :daily" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :daily
+ new_resource.frequency_modifier 2
+ new_resource.random_delay "2400"
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :on_logon" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.frequency :on_logon
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier and random_delay" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "create task by adding frequency_modifier as 5" do
+ subject.frequency_modifier 5
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "For frequency :onstart" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.frequency :onstart
+ new_resource.frequency_modifier 20
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ it "create task by adding frequency_modifier as 20" do
+ subject.run_action(:create)
+ subject.run_action(:create)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "#after_created" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource
+ end
+
+ context "when start_day is passed with frequency :onstart" do
+ it "does not raises error" do
+ subject.frequency :onstart
+ subject.start_day "09/20/2017"
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a non system user is passed without password" do
+ it "raises error" do
+ subject.user "USER"
+ subject.frequency :onstart
+ expect { subject.after_created }.to raise_error(%q{Please provide a password or check if this task needs to be interactive! Valid passwordless users are: 'SYSTEM', 'NT AUTHORITY\SYSTEM', 'LOCAL SERVICE', 'NT AUTHORITY\LOCAL SERVICE', 'NETWORK SERVICE', 'NT AUTHORITY\NETWORK SERVICE', 'ADMINISTRATORS', 'BUILTIN\ADMINISTRATORS', 'USERS', 'BUILTIN\USERS', 'GUESTS', 'BUILTIN\GUESTS'})
+ end
+ it "does not raises error when task is interactive" do
+ subject.user "USER"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a system user is passed without password" do
+ it "does not raises error" do
+ subject.user "ADMINISTRATORS"
+ subject.frequency :onstart
+ expect { subject.after_created }.not_to raise_error
+ end
+ it "does not raises error when task is interactive" do
+ subject.user "ADMINISTRATORS"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a non system user is passed with password" do
+ it "does not raises error" do
+ subject.user "USER"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ expect { subject.after_created }.not_to raise_error
+ end
+ it "does not raises error when task is interactive" do
+ subject.user "USER"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.not_to raise_error
+ end
+ end
+
+ context "when a system user is passed with password" do
+ it "raises error" do
+ subject.user "ADMINISTRATORS"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ expect { subject.after_created }.to raise_error("Password is not required for system users.")
+ end
+ it "raises error when task is interactive" do
+ subject.user "ADMINISTRATORS"
+ subject.password "XXXX"
+ subject.frequency :onstart
+ subject.interactive_enabled true
+ expect { subject.after_created }.to raise_error("Password is not required for system users.")
+ end
+ end
+
+ context "when frequency_modifier > 1439 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 1450
+ subject.frequency :minute
+ expect { subject.after_created }.to raise_error("frequency_modifier value 1450 is invalid. Valid values for :minute frequency are 1 - 1439.")
+ end
+ end
+
+ context "when frequency_modifier > 23 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 24
+ subject.frequency :hourly
+ expect { subject.after_created }.to raise_error("frequency_modifier value 24 is invalid. Valid values for :hourly frequency are 1 - 23.")
+ end
+ end
+
+ context "when frequency_modifier > 23 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 366
+ subject.frequency :daily
+ expect { subject.after_created }.to raise_error("frequency_modifier value 366 is invalid. Valid values for :daily frequency are 1 - 365.")
+ end
+ end
+
+ context "when frequency_modifier > 52 is passed for frequency=:minute" do
+ it "raises error" do
+ subject.frequency_modifier 53
+ subject.frequency :weekly
+ expect { subject.after_created }.to raise_error("frequency_modifier value 53 is invalid. Valid values for :weekly frequency are 1 - 52.")
+ end
+ end
+
+ context "when invalid frequency_modifier is passed for :monthly frequency" do
+ it "raises error" do
+ subject.frequency :monthly
+ subject.frequency_modifier "13"
+ expect { subject.after_created }.to raise_error("frequency_modifier value 13 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+ end
+
+ context "when invalid frequency_modifier is passed for :monthly frequency" do
+ it "raises error" do
+ subject.frequency :monthly
+ subject.frequency_modifier "xyz"
+ expect { subject.after_created }.to raise_error("frequency_modifier value xyz is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'.")
+ end
+ end
+
+ context "when invalid months are passed" do
+ it "raises error" do
+ subject.months "xyz"
+ subject.frequency :monthly
+ expect { subject.after_created }.to raise_error("months property invalid. Only valid values are: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC, *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "when idle_time > 999 is passed" do
+ it "raises error" do
+ subject.idle_time 1000
+ subject.frequency :on_idle
+ expect { subject.after_created }.to raise_error("idle_time value 1000 is invalid. Valid values for :on_idle frequency are 1 - 999.")
+ end
+ end
+
+ context "when idle_time is passed for frequency=:monthly" do
+ it "raises error" do
+ subject.idle_time 300
+ subject.frequency :monthly
+ expect { subject.after_created }.to raise_error("idle_time property is only valid for tasks that run on_idle")
+ end
+ end
+ end
+
+ describe "action :delete" do
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accepts this
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "does not converge the resource if it is already converged" do
+ subject.run_action(:create)
+ subject.run_action(:delete)
+ subject.run_action(:delete)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ describe "action :run" do
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command "dir"
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "runs the existing task" do
+ subject.run_action(:create)
+ subject.run_action(:run)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("queued").or eq("running").or eq("ready") # queued or can be running
+ end
+ end
+
+ describe "action :end", :volatile do
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command "dir"
+ new_resource.run_level :highest
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource
+ end
+
+ it "ends the running task" do
+ subject.run_action(:create)
+ subject.run_action(:run)
+ subject.run_action(:end)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("queued").or eq("ready") # queued or can be ready
+ end
+ end
+
+ describe "action :enable" do
+ let(:task_name) { "chef-client-functional-test-enable" }
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "enables the disabled task" do
+ subject.run_action(:create)
+ subject.run_action(:disable)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("not scheduled")
+ subject.run_action(:enable)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("ready")
+ end
+ end
+
+ describe "action :disable" do
+ let(:task_name) { "chef-client-functional-test-disable" }
+ after { delete_task }
+
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "disables the task" do
+ subject.run_action(:create)
+ subject.run_action(:disable)
+ current_resource = call_for_load_current_resource
+ expect(current_resource.task.status).to eq("not scheduled")
+ end
+ end
+
+ describe "action :change" do
+ after { delete_task }
+ subject do
+ new_resource = Chef::Resource::WindowsTask.new(task_name, run_context)
+ new_resource.command task_name
+ new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since
+ new_resource.frequency :hourly
+ new_resource
+ end
+
+ it "call action_create since change action is alias for create" do
+ subject.run_action(:change)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ def delete_task
+ task_to_delete = Chef::Resource::WindowsTask.new(task_name, run_context)
+ task_to_delete.run_action(:delete)
+ end
+
+ def call_for_create_action
+ current_resource = call_for_load_current_resource
+ expect(current_resource.exists).to eq(false)
+ subject.run_action(:create)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ def call_for_load_current_resource
+ windows_task_provider.send(:load_current_resource)
+ end
+end
diff --git a/spec/functional/resource/windows_user_privilege_spec.rb b/spec/functional/resource/windows_user_privilege_spec.rb
new file mode 100644
index 0000000000..f52bbbe23b
--- /dev/null
+++ b/spec/functional/resource/windows_user_privilege_spec.rb
@@ -0,0 +1,192 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@chef.io>)
+# Copyright:: Copyright (c) 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 "spec_helper"
+
+describe Chef::Resource::WindowsUserPrivilege, :windows_only do
+ let(:principal) { nil }
+ let(:privilege) { nil }
+ let(:users) { nil }
+ let(:sensitive) { true }
+
+ let(:windows_test_run_context) do
+ node = Chef::Node.new
+ node.consume_external_attrs(OHAI_SYSTEM.data, {}) # node[:languages][:powershell][:version]
+ node.automatic["os"] = "windows"
+ node.automatic["platform"] = "windows"
+ node.automatic["platform_version"] = "6.1"
+ node.automatic["kernel"][:machine] = :x86_64 # Only 64-bit architecture is supported
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ end
+
+ subject do
+ new_resource = Chef::Resource::WindowsUserPrivilege.new(principal, windows_test_run_context)
+ new_resource.privilege = privilege
+ new_resource.principal = principal
+ new_resource.users = users
+ new_resource
+ end
+
+ describe "#add privilege" do
+ after { subject.run_action(:remove) }
+
+ context "when privilege is passed as string" do
+ let(:principal) { "Administrator" }
+ let(:privilege) { "SeCreateSymbolicLinkPrivilege" }
+
+ it "adds user to privilege" do
+ # Removing so that add update happens
+ subject.run_action(:remove)
+ subject.run_action(:add)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:add)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when privilege is passed as array" do
+ let(:principal) { "Administrator" }
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege SeCreatePagefilePrivilege} }
+
+ it "adds user to privilege" do
+ subject.run_action(:add)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:add)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "#set privilege" do
+ after { remove_user_privilege("Administrator", subject.privilege) }
+
+ let(:principal) { "user_privilege" }
+ let(:users) { %w{Administrators Administrator} }
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege} }
+
+ it "sets user to privilege" do
+ subject.action(:set)
+ subject.run_action(:set)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.action(:set)
+ subject.run_action(:set)
+ subject.run_action(:set)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ it "raise error if users not provided" do
+ subject.users = nil
+ subject.action(:set)
+ expect { subject.run_action(:set) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+
+ describe "#remove privilege" do
+ let(:principal) { "Administrator" }
+ context "when privilege is passed as array" do
+ let(:privilege) { "SeCreateSymbolicLinkPrivilege" }
+ it "remove user from privilege" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ subject.run_action(:remove)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ context "when privilege is passed as array" do
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege SeCreatePagefilePrivilege} }
+ it "remove user from privilege" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.run_action(:add)
+ subject.run_action(:remove)
+ subject.run_action(:remove)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+ end
+
+ describe "running with non admin user" do
+ include Chef::Mixin::UserContext
+
+ let(:user) { "security_user" }
+ let(:password) { "Security@123" }
+ let(:principal) { "user_privilege" }
+ let(:users) { ["Administrators", "#{domain}\\security_user"] }
+ let(:privilege) { %w{SeCreateSymbolicLinkPrivilege} }
+
+ let(:domain) do
+ ENV["COMPUTERNAME"]
+ end
+
+ before do
+ allow_any_instance_of(Chef::Mixin::UserContext).to receive(:node).and_return({ "platform_family" => "windows" })
+ add_user = Mixlib::ShellOut.new("net user #{user} #{password} /ADD")
+ add_user.run_command
+ add_user.error!
+ end
+
+ after do
+ remove_user_privilege("#{domain}\\#{user}", subject.privilege)
+ delete_user = Mixlib::ShellOut.new("net user #{user} /delete")
+ delete_user.run_command
+ delete_user.error!
+ end
+
+ it "sets user to privilege" do
+ subject.action(:set)
+ subject.run_action(:set)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "is idempotent" do
+ subject.action(:set)
+ subject.run_action(:set)
+ subject.run_action(:set)
+ expect(subject).not_to be_updated_by_last_action
+ end
+ end
+
+ def remove_user_privilege(user, privilege)
+ subject.action(:remove)
+ subject.principal = user
+ subject.privilege = privilege
+ subject.run_action(:remove)
+ end
+end
diff --git a/spec/functional/resource/yum_package_spec.rb b/spec/functional/resource/yum_package_spec.rb
new file mode 100644
index 0000000000..5f902cff17
--- /dev/null
+++ b/spec/functional/resource/yum_package_spec.rb
@@ -0,0 +1,981 @@
+#
+# Copyright:: Copyright (c) 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 "chef/mixin/shell_out"
+
+# run this test only for following platforms.
+exclude_test = !(%w{rhel fedora amazon}.include?(ohai[:platform_family]) && !File.exist?("/usr/bin/dnf"))
+describe Chef::Resource::YumPackage, :requires_root, external: exclude_test do
+ include Chef::Mixin::ShellOut
+
+ # NOTE: every single test here either needs to explicitly call flush_cache or needs to explicitly
+ # call preinstall (which explicitly calls flush_cache). It is your responsibility to do one or the
+ # other in order to minimize calling flush_cache a half dozen times per test.
+
+ def flush_cache
+ Chef::Resource::YumPackage.new("shouldnt-matter", run_context).run_action(:flush_cache)
+ end
+
+ def preinstall(*rpms)
+ rpms.each do |rpm|
+ shell_out!("rpm -ivh #{CHEF_SPEC_ASSETS}/yumrepo/#{rpm}")
+ end
+ flush_cache
+ end
+
+ before(:all) do
+ shell_out!("yum -y install yum-utils")
+ end
+
+ before(:each) do
+ File.open("/etc/yum.repos.d/chef-yum-localtesting.repo", "w+") do |f|
+ f.write <<~EOF
+ [chef-yum-localtesting]
+ name=Chef DNF spec testing repo
+ baseurl=file://#{CHEF_SPEC_ASSETS}/yumrepo
+ enable=1
+ gpgcheck=0
+ EOF
+ end
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | grep chef_rpm | xargs -r rpm -e")
+ # next line is useful cleanup if you happen to have been testing both yum + dnf func tests on the same box and
+ # have some dnf garbage left around
+ FileUtils.rm_f "/etc/yum.repos.d/chef-dnf-localtesting.repo"
+ end
+
+ after(:all) do
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | grep chef_rpm | xargs -r rpm -e")
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:yum_package) do
+ r = Chef::Resource::YumPackage.new(package_name, run_context)
+ r.options("--nogpgcheck")
+ r
+ end
+
+ def pkg_arch
+ ohai[:kernel][:machine]
+ end
+
+ describe ":install" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+
+ it "installs if the package is not installed" do
+ flush_cache
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install twice" do
+ flush_cache
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the i686 package is installed", :intel_64bit do
+ skip "FIXME: do nothing, or install the #{pkg_arch} version?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "does not install if the prior version i686 package is installed", :intel_64bit do
+ skip "FIXME: do nothing, or install the #{pkg_arch} version?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.i686$")
+ end
+ end
+
+ context "with versions or globs in the name" do
+ it "works with a version" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "works with an older version" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with an evra" do
+ flush_cache
+ yum_package.package_name("chef_rpm-0:1.2-1.#{pkg_arch}")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with version and release" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1.2-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a version glob" do
+ flush_cache
+ yum_package.package_name("chef_rpm-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "works with a name glob + version glob" do
+ flush_cache
+ yum_package.package_name("chef_rp*-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "upgrades when the installed version does not match the version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm-1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}")
+ end
+
+ it "downgrades when the installed version is higher than the package_name version" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm-1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ # version only matches the actual yum version, does not work with epoch or release or combined evr
+ context "with version property" do
+ it "matches the full version" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches with a glob" do
+ # we are unlikely to ever fix this. if you've found this comment you should use e.g. "tcpdump-4*" in
+ # the name field rather than trying to use a name of "tcpdump" and a version of "4*".
+ pending "this does not work, is not easily supported by the underlying yum libraries, but does work in the new dnf_package provider"
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches the vr" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1.10-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches the evr" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("0:1.10-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches with a vr glob" do
+ pending "doesn't work on command line either"
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("1.10-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "matches with an evr glob" do
+ pending "doesn't work on command line either"
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.version("0:1.10-1*")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+
+ context "downgrades" do
+ it "downgrades the package when allow_downgrade" do
+ flush_cache
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm")
+ yum_package.allow_downgrade true
+ yum_package.version("1.2-1")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with arches", :intel_64bit do
+ it "installs with 64-bit arch in the name" do
+ flush_cache
+ yum_package.package_name("chef_rpm.#{pkg_arch}")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "installs with 32-bit arch in the name" do
+ flush_cache
+ yum_package.package_name("chef_rpm.i686")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "installs with 64-bit arch in the property" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.arch((pkg_arch).to_s)
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "installs with 32-bit arch in the property" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.arch("i686")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+ end
+
+ context "with constraints" do
+ it "with nothing installed, it installs the latest version" do
+ flush_cache
+ yum_package.package_name("chef_rpm >= 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm >= 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "when it is met, it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm >= 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with nothing intalled, it installs the latest version" do
+ flush_cache
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with an equality constraint, when it is not met by an installed rpm, it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm = 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with an equality constraint, when it is met by an installed rpm, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm = 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "when it is met by an installed rpm, it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when there is no solution to the contraint" do
+ flush_cache
+ yum_package.package_name("chef_rpm > 2.0")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "when there is no solution to the contraint but an rpm is preinstalled" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 2.0")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "with a less than constraint, when nothing is installed, it installs" do
+ flush_cache
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a less than constraint, when the install version matches, it does nothing" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a less than constraint, when the install version fails, it should downgrade" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with source arguments" do
+ it "raises an exception when the package does not exist" do
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "does not raise a hard exception in why-run mode when the package does not exist" do
+ Chef::Config[:why_run] = true
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/this-file-better-not-exist.rpm")
+ yum_package.run_action(:install)
+ expect { yum_package.run_action(:install) }.not_to raise_error
+ end
+
+ it "installs the package when using the source argument" do
+ flush_cache
+ yum_package.name "something"
+ yum_package.package_name "somethingelse"
+ yum_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "downgrade on a local file is ignored when allow_downgrade is false" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade false
+ yum_package.version "1.2-1"
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "downgrade on a local file with allow_downgrade true works" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.version "1.2-1"
+ yum_package.allow_downgrade true
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "does not downgrade the package with :install" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not upgrade the package with :install" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed and there is a version string" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.version "1.2-1"
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed and there is a version string with arch" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.version "1.2-1.#{pkg_arch}"
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "multipackage with arches", :intel_64bit do
+ it "installs two rpms" do
+ flush_cache
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ it "does nothing if both are installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ flush_cache
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ end
+
+ it "installs the second rpm if the first is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ it "installs the first rpm if the second is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.package_name([ "chef_rpm.#{pkg_arch}", "chef_rpm.i686" ] )
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs two rpms with multi-arch" do
+ flush_cache
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the second rpm if the first is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "installs the first rpm if the second is installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.#{pkg_arch}$/)
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match(/^chef_rpm-1.10-1.i686$/)
+ end
+
+ # unlikely to work consistently correct, okay to deprecate the arch-array in favor of the arch in the name
+ it "does nothing if both are installed (muti-arch)" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ yum_package.package_name(%w{chef_rpm chef_rpm} )
+ yum_package.arch([pkg_arch, "i686"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be false
+ end
+ end
+
+ context "repo controls" do
+ it "should fail with the repo disabled" do
+ flush_cache
+ yum_package.options("--disablerepo=chef-yum-localtesting")
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ end
+
+ it "should work with disablerepo first" do
+ flush_cache
+ yum_package.options(["--disablerepo=*", "--enablerepo=chef-yum-localtesting"])
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "should work to enable a disabled repo" do
+ shell_out!("yum-config-manager --disable chef-yum-localtesting")
+ flush_cache
+ expect { yum_package.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /No candidate version available/)
+ flush_cache
+ yum_package.options("--enablerepo=chef-yum-localtesting")
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "when an idempotent install action is run, does not leave repos disabled" do
+ flush_cache
+ # this is a bit tricky -- we need this action to be idempotent, so that it doesn't recycle any
+ # caches, but need it to hit whatavailable with the repo disabled. using :upgrade like this
+ # accomplishes both those goals (it would be easier if we had other rpms in this repo, but with
+ # one rpm we need to do this).
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.options("--disablerepo=chef-yum-localtesting")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ # now we're still using the same cache in the yum_helper.py cache and we test to see if the
+ # repo that we temporarily disabled is enabled on this pass.
+ yum_package.package_name("chef_rpm-1.10-1.#{pkg_arch}")
+ yum_package.options(nil)
+ yum_package.run_action(:install)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+ end
+
+ describe ":upgrade" do
+
+ context "with source arguments" do
+ it "installs the package when using the source argument" do
+ flush_cache
+ yum_package.name "something"
+ yum_package.package_name "somethingelse"
+ yum_package.source("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "installs the package when the name is a path to a file" do
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "downgrades the package when allow_downgrade is true" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "upgrades the package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "is idempotent when the package is already installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with a local source" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ flush_cache
+ yum_package.package_name("#{CHEF_SPEC_ASSETS}/yumrepo/chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ context "version pinning" do
+ it "with an equality pin in the name it upgrades a prior package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm-1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a prco equality pin in the name it upgrades a prior package" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm == 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with an equality pin in the name it downgrades a later package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm-1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a prco equality pin in the name it downgrades a later package" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm == 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and no rpm installed it installs" do
+ flush_cache
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and no rpm installed it installs" do
+ flush_cache
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and matching rpm installed it does nothing" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and no rpm installed it installs" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "with a > pin in the name and non-matching rpm installed it upgrades" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.package_name("chef_rpm > 1.2")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "with a < pin in the name and non-matching rpm installed it downgrades" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.allow_downgrade true
+ yum_package.package_name("chef_rpm < 1.10")
+ yum_package.run_action(:upgrade)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+ end
+
+ describe ":remove" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "does not remove the package twice" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the i686 package is installed", :intel_64bit do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version i686 package is installed", :intel_64bit do
+ skip "FIXME: should this be fixed or is the current behavior correct?"
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+ end
+
+ context "with 64-bit arch", :intel_64bit do
+ let(:package_name) { "chef_rpm.#{pkg_arch}" }
+ it "does nothing if the package is not installed" do
+ flush_cache
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "does nothing if the i686 package is installed" do
+ preinstall("chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.i686$")
+ end
+
+ it "does nothing if the prior version i686 package is installed" do
+ preinstall("chef_rpm-1.2-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.i686$")
+ end
+ end
+
+ context "with 32-bit arch", :intel_64bit do
+ let(:package_name) { "chef_rpm.i686" }
+ it "removes only the 32-bit arch if both are installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm", "chef_rpm-1.10-1.i686.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/yum.repos.d/chef-yum-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ yum_package.run_action(:remove)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+ end
+ end
+
+ describe ":lock and :unlock" do
+ before(:all) do
+ shell_out!("yum -y install yum-versionlock")
+ end
+
+ before(:each) do
+ shell_out("yum versionlock delete '*:chef_rpm-*'") # will exit with error when nothing is locked, we don't care
+ end
+
+ it "locks an rpm" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:lock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).to match("^0:chef_rpm-")
+ end
+
+ it "does not lock if its already locked" do
+ flush_cache
+ shell_out!("yum versionlock add chef_rpm")
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:lock)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("yum versionlock list").stdout.chomp).to match("^0:chef_rpm-")
+ end
+
+ it "unlocks an rpm" do
+ flush_cache
+ shell_out!("yum versionlock add chef_rpm")
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:unlock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).not_to match("^0:chef_rpm-")
+ end
+
+ it "does not unlock an already locked rpm" do
+ flush_cache
+ yum_package.package_name("chef_rpm")
+ yum_package.run_action(:unlock)
+ expect(yum_package.updated_by_last_action?).to be false
+ expect(shell_out("yum versionlock list").stdout.chomp).not_to match("^0:chef_rpm-")
+ end
+
+ it "check that we can lock based on provides" do
+ flush_cache
+ yum_package.package_name("chef_rpm_provides")
+ yum_package.run_action(:lock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).to match("^0:chef_rpm-")
+ end
+
+ it "check that we can unlock based on provides" do
+ flush_cache
+ shell_out!("yum versionlock add chef_rpm")
+ yum_package.package_name("chef_rpm_provides")
+ yum_package.run_action(:unlock)
+ expect(yum_package.updated_by_last_action?).to be true
+ expect(shell_out("yum versionlock list").stdout.chomp).not_to match("^0:chef_rpm-")
+ end
+ end
+end
diff --git a/spec/functional/resource/zypper_package_spec.rb b/spec/functional/resource/zypper_package_spec.rb
new file mode 100644
index 0000000000..ce6a3bf33c
--- /dev/null
+++ b/spec/functional/resource/zypper_package_spec.rb
@@ -0,0 +1,247 @@
+#
+# Author:: Dheeraj Dubey (<dheeraj.dubey@msystechnologies.com>)
+# Copyright:: Copyright (c) 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 "chef/mixin/shell_out"
+
+describe Chef::Resource::ZypperPackage, :requires_root, :suse_only do
+ include Chef::Mixin::ShellOut
+
+ # NOTE: every single test here needs to explicitly call preinstall.
+
+ def preinstall(*rpms)
+ rpms.each do |rpm|
+ shell_out!("rpm -ivh #{CHEF_SPEC_ASSETS}/zypprepo/#{rpm}")
+ end
+ end
+
+ before(:each) do
+ File.open("/etc/zypp/repos.d/chef-zypp-localtesting.repo", "w+") do |f|
+ f.write <<~EOF
+ [chef-zypp-localtesting]
+ name=Chef zypper spec testing repo
+ baseurl=file://#{CHEF_SPEC_ASSETS}/zypprepo
+ enable=1
+ gpgcheck=0
+ EOF
+ end
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm | xargs -r rpm -e")
+ # next line is useful cleanup if you happen to have been testing zypper func tests on the same box and
+ # have some zypper garbage left around
+ # FileUtils.rm_f "/etc/zypp/repos.d/chef-zypp-localtesting.repo"
+ end
+
+ after(:all) do
+ shell_out!("rpm -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm | xargs -r rpm -e")
+ FileUtils.rm_f "/etc/zypp/repos.d/chef-zypp-localtesting.repo"
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
+ end
+
+ let(:package_name) { "chef_rpm" }
+ let(:zypper_package) do
+ r = Chef::Resource::ZypperPackage.new(package_name, run_context)
+ r.global_options("--no-gpg-checks")
+ r
+ end
+
+ def pkg_arch
+ ohai[:kernel][:machine]
+ end
+
+ context "installing a package" do
+ after { remove_package }
+ it "installs the latest version" do
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install twice" do
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "does not install if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "multipackage installs which result in nils from the superclass" do
+ # this looks weird, it tests an internal condition of the allow_nils behavior where the arrays passed to install_package will have
+ # nil values, and ensures that doesn't wind up creating weirdness in the resulting shell_out that causes it to fail
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.package_name(%w{chef_rpm chef_rpm})
+ zypper_package.version(["1.2", "1.10"])
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+ end
+
+ context "with versions" do
+ it "works with a version" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.10")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
+
+ it "works with an older version" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.2")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "works with version and release" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.2-1")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+
+ it "downgrades when the installed version is higher than the package_name version" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ zypper_package.allow_downgrade true
+ zypper_package.package_name("chef_rpm")
+ zypper_package.version("1.2-1")
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
+ end
+ end
+
+ describe ":remove" do
+ context "vanilla use case" do
+ let(:package_name) { "chef_rpm" }
+ it "does nothing if the package is not installed" do
+ zypper_package.run_action(:remove)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the package is installed" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:remove)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "does not remove the package twice" do
+ preinstall("chef_rpm-1.10-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:remove)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ zypper_package.run_action(:remove)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+
+ it "removes the package if the prior version package is installed" do
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:remove)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+ end
+
+ context "with no available version" do
+ it "works when a package is installed" do
+ FileUtils.rm_f "/etc/zypp/repos.d/chef-zypp-localtesting.repo"
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.run_action(:remove)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^package chef_rpm is not installed$")
+ end
+ end
+ end
+
+ describe ":lock and :unlock" do
+ before(:each) do
+ shell_out("zypper removelock chef_rpm") # will exit with error when nothing is locked, we don't care
+ end
+
+ it "locks an rpm" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:lock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).to match("chef_rpm")
+ end
+
+ it "does not lock if its already locked" do
+ shell_out!("zypper addlock chef_rpm")
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:lock)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).to match("chef_rpm")
+ end
+
+ it "unlocks an rpm" do
+ shell_out!("zypper addlock chef_rpm")
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:unlock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).not_to match("chef_rpm")
+ end
+
+ it "does not unlock an already locked rpm" do
+ zypper_package.package_name("chef_rpm")
+ zypper_package.run_action(:unlock)
+ expect(zypper_package.updated_by_last_action?).to be false
+ expect(shell_out("zypper locks | grep chef_rpm").stdout.chomp).not_to match("chef_rpm")
+ end
+
+ it "check that we can lock based on provides" do
+ zypper_package.package_name("chef_rpm_provides")
+ zypper_package.run_action(:lock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm_provides").stdout.chomp).to match("chef_rpm_provides")
+ end
+
+ it "check that we can unlock based on provides" do
+ shell_out!("zypper addlock chef_rpm_provides")
+ zypper_package.package_name("chef_rpm_provides")
+ zypper_package.run_action(:unlock)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("zypper locks | grep chef_rpm_provides").stdout.chomp).not_to match("chef_rpm_provides")
+ end
+ end
+ def remove_package
+ pkg_to_remove = Chef::Resource::ZypperPackage.new(package_name, run_context)
+ pkg_to_remove.run_action(:remove)
+ end
+end