summaryrefslogtreecommitdiff
path: root/spec/support
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support')
-rw-r--r--spec/support/key_helpers.rb104
-rw-r--r--spec/support/lib/chef/provider/openldap_includer.rb29
-rw-r--r--spec/support/lib/chef/resource/cat.rb1
-rw-r--r--spec/support/lib/chef/resource/one_two_three_four.rb6
-rw-r--r--spec/support/lib/chef/resource/openldap_includer.rb27
-rw-r--r--spec/support/lib/chef/resource/with_state.rb9
-rw-r--r--spec/support/lib/chef/resource/zen_follower.rb5
-rw-r--r--spec/support/lib/chef/resource/zen_master.rb7
-rw-r--r--spec/support/mock/platform.rb2
-rw-r--r--spec/support/shared/context/client.rb277
-rw-r--r--spec/support/shared/examples/client.rb53
-rw-r--r--spec/support/shared/functional/file_resource.rb4
-rw-r--r--spec/support/shared/functional/securable_resource.rb70
-rw-r--r--spec/support/shared/functional/securable_resource_with_reporting.rb8
-rw-r--r--spec/support/shared/functional/windows_script.rb2
-rw-r--r--spec/support/shared/integration/integration_helper.rb11
-rw-r--r--spec/support/shared/unit/api_versioning.rb77
-rw-r--r--spec/support/shared/unit/knife_shared.rb40
-rw-r--r--spec/support/shared/unit/provider/file.rb39
-rw-r--r--spec/support/shared/unit/user_and_client_shared.rb115
20 files changed, 802 insertions, 84 deletions
diff --git a/spec/support/key_helpers.rb b/spec/support/key_helpers.rb
new file mode 100644
index 0000000000..076f709380
--- /dev/null
+++ b/spec/support/key_helpers.rb
@@ -0,0 +1,104 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 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'
+
+shared_examples_for "a knife key command" do
+ let(:stderr) { StringIO.new }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "before apply_params! is called" do
+ context "when apply_params! is called with invalid args (missing actor)" do
+ let(:params) { [] }
+ it "shows the usage" do
+ expect(command).to receive(:show_usage)
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+
+ it "outputs the proper error" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ expect(stderr.string).to include(command.actor_missing_error)
+ end
+
+ it "exits 1" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+ end
+ end # before apply_params! is called
+
+ context "after apply_params! is called with valid args" do
+ before do
+ command.apply_params!(params)
+ end
+
+ it "properly defines the actor" do
+ expect(command.actor).to eq("charmander")
+ end
+ end # after apply_params! is called with valid args
+
+ context "when the command is run" do
+ before do
+ allow(command).to receive(:service_object).and_return(service_object)
+ allow(command).to receive(:name_args).and_return(["charmander"])
+ end
+
+ context "when the command is successful" do
+ before do
+ expect(service_object).to receive(:run)
+ end
+ end
+ end
+end # a knife key command
+
+shared_examples_for "a knife key command with a keyname as the second arg" do
+ let(:stderr) { StringIO.new }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "before apply_params! is called" do
+ context "when apply_params! is called with invalid args (missing keyname)" do
+ let(:params) { ["charmander"] }
+ it "shows the usage" do
+ expect(command).to receive(:show_usage)
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+
+ it "outputs the proper error" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ expect(stderr.string).to include(command.keyname_missing_error)
+ end
+
+ it "exits 1" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+ end
+ end # before apply_params! is called
+end
diff --git a/spec/support/lib/chef/provider/openldap_includer.rb b/spec/support/lib/chef/provider/openldap_includer.rb
new file mode 100644
index 0000000000..afb0c7cf01
--- /dev/null
+++ b/spec/support/lib/chef/provider/openldap_includer.rb
@@ -0,0 +1,29 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, 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.
+#
+
+class Chef
+ class Provider
+ class OpenldapIncluder < Chef::Provider::LWRPBase
+ provides :openldap_includer
+
+ def action_run
+ include_recipe "openldap::default"
+ end
+ end
+ end
+end
diff --git a/spec/support/lib/chef/resource/cat.rb b/spec/support/lib/chef/resource/cat.rb
index ecca50cb53..efc78aa59c 100644
--- a/spec/support/lib/chef/resource/cat.rb
+++ b/spec/support/lib/chef/resource/cat.rb
@@ -23,7 +23,6 @@ class Chef
attr_accessor :action
def initialize(name, run_context=nil)
- @resource_name = :cat
super
@action = "sell"
end
diff --git a/spec/support/lib/chef/resource/one_two_three_four.rb b/spec/support/lib/chef/resource/one_two_three_four.rb
index 296d2cd970..8f273a0cda 100644
--- a/spec/support/lib/chef/resource/one_two_three_four.rb
+++ b/spec/support/lib/chef/resource/one_two_three_four.rb
@@ -19,12 +19,8 @@
class Chef
class Resource
class OneTwoThreeFour < Chef::Resource
- attr_reader :i_can_count
- def initialize(name, run_context)
- @resource_name = :one_two_three_four
- super
- end
+ attr_reader :i_can_count
def i_can_count(tf)
@i_can_count = tf
diff --git a/spec/support/lib/chef/resource/openldap_includer.rb b/spec/support/lib/chef/resource/openldap_includer.rb
new file mode 100644
index 0000000000..6f443b4c7c
--- /dev/null
+++ b/spec/support/lib/chef/resource/openldap_includer.rb
@@ -0,0 +1,27 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2010 Opscode, 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.
+#
+
+
+class Chef
+ class Resource
+ class OpenldapIncluder < Chef::Resource::LWRPBase
+ allowed_actions :run
+ default_action :run
+ end
+ end
+end
diff --git a/spec/support/lib/chef/resource/with_state.rb b/spec/support/lib/chef/resource/with_state.rb
index 226de0a6d2..773ae7ddb8 100644
--- a/spec/support/lib/chef/resource/with_state.rb
+++ b/spec/support/lib/chef/resource/with_state.rb
@@ -23,15 +23,6 @@ class Chef
class Resource
class WithState < Chef::Resource
attr_accessor :state
-
- def initialize(name, run_context=nil)
- @resource_name = :with_state
- super
- end
-
- def state
- @state
- end
end
end
end
diff --git a/spec/support/lib/chef/resource/zen_follower.rb b/spec/support/lib/chef/resource/zen_follower.rb
index ddc289e48d..155e6ae729 100644
--- a/spec/support/lib/chef/resource/zen_follower.rb
+++ b/spec/support/lib/chef/resource/zen_follower.rb
@@ -24,11 +24,6 @@ class Chef
provides :follower, platform: "zen"
- def initialize(name, run_context=nil)
- @resource_name = :zen_follower
- super
- end
-
def master(arg=nil)
if !arg.nil?
@master = arg
diff --git a/spec/support/lib/chef/resource/zen_master.rb b/spec/support/lib/chef/resource/zen_master.rb
index d47d174e28..4106549d79 100644
--- a/spec/support/lib/chef/resource/zen_master.rb
+++ b/spec/support/lib/chef/resource/zen_master.rb
@@ -22,13 +22,10 @@ require 'chef/json_compat'
class Chef
class Resource
class ZenMaster < Chef::Resource
+ allowed_actions :win, :score
+
attr_reader :peace
- def initialize(name, run_context=nil)
- @resource_name = :zen_master
- super
- allowed_actions << :win << :score
- end
def peace(tf)
@peace = tf
diff --git a/spec/support/mock/platform.rb b/spec/support/mock/platform.rb
index ab2c19baff..7eae82fe7d 100644
--- a/spec/support/mock/platform.rb
+++ b/spec/support/mock/platform.rb
@@ -6,7 +6,7 @@
# testing code that mixes in platform specific modules like +Chef::Mixin::Securable+
# or +Chef::FileAccessControl+
def platform_mock(platform = :unix, &block)
- allow(Chef::Platform).to receive(:windows?).and_return(platform == :windows ? true : false)
+ allow(ChefConfig).to receive(:windows?).and_return(platform == :windows ? true : false)
ENV['SYSTEMDRIVE'] = (platform == :windows ? 'C:' : nil)
if platform == :windows
diff --git a/spec/support/shared/context/client.rb b/spec/support/shared/context/client.rb
new file mode 100644
index 0000000000..eb537e9889
--- /dev/null
+++ b/spec/support/shared/context/client.rb
@@ -0,0 +1,277 @@
+
+require 'spec_helper'
+
+# Stubs a basic client object
+shared_context "client" do
+ let(:fqdn) { "hostname.example.org" }
+ let(:hostname) { "hostname" }
+ let(:machinename) { "machinename.example.org" }
+ let(:platform) { "example-platform" }
+ let(:platform_version) { "example-platform-1.0" }
+
+ let(:ohai_data) do
+ {
+ :fqdn => fqdn,
+ :hostname => hostname,
+ :machinename => machinename,
+ :platform => platform,
+ :platform_version => platform_version
+ }
+ end
+
+ let(:ohai_system) do
+ ohai = instance_double("Ohai::System", :all_plugins => true, :data => ohai_data)
+ allow(ohai).to receive(:[]) do |k|
+ ohai_data[k]
+ end
+ ohai
+ end
+
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.name(fqdn)
+ n.chef_environment("_default")
+ end
+ end
+
+ let(:json_attribs) { nil }
+ let(:client_opts) { {} }
+
+ let(:client) do
+ Chef::Config[:event_loggers] = []
+ Chef::Client.new(json_attribs, client_opts).tap do |c|
+ c.node = node
+ end
+ end
+
+ before do
+ Chef::Log.logger = Logger.new(StringIO.new)
+
+ # Node/Ohai data
+ #Chef::Config[:node_name] = fqdn
+ allow(Ohai::System).to receive(:new).and_return(ohai_system)
+ end
+end
+
+# Stubs a client for a client run.
+# Requires a client object be defined in the scope of this included context.
+# e.g.:
+# describe "some functionality" do
+# include_context "client"
+# include_context "a client run"
+# ...
+# end
+shared_context "a client run" do
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+
+ let(:api_client_exists?) { false }
+ let(:enable_fork) { false }
+
+ let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") }
+ let(:http_node_load) { double("Chef::REST (node)") }
+ let(:http_node_save) { double("Chef::REST (node save)") }
+
+ let(:runner) { instance_double("Chef::Runner") }
+ let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => false) }
+
+ def stub_for_register
+ # --Client.register
+ # Make sure Client#register thinks the client key doesn't
+ # exist, so it tries to register and create one.
+ allow(File).to receive(:exists?).and_call_original
+ expect(File).to receive(:exists?).
+ with(Chef::Config[:client_key]).
+ exactly(:once).
+ and_return(api_client_exists?)
+
+ unless api_client_exists?
+ # Client.register will register with the validation client name.
+ expect_any_instance_of(Chef::ApiClient::Registration).to receive(:run)
+ end
+ end
+
+ def stub_for_node_load
+ # Client.register will then turn around create another
+ # Chef::REST object, this time with the client key it got from the
+ # previous step.
+ expect(Chef::REST).to receive(:new).
+ with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key]).
+ exactly(:once).
+ and_return(http_node_load)
+
+ # --Client#build_node
+ # looks up the node, which we will return, then later saves it.
+ expect(Chef::Node).to receive(:find_or_create).with(fqdn).and_return(node)
+
+ # --ResourceReporter#node_load_completed
+ # gets a run id from the server for storing resource history
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::ResourceReporter).to receive(:node_load_completed)
+ end
+
+ def stub_for_sync_cookbooks
+ # --Client#setup_run_context
+ # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
+ #
+ expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks)
+ expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ expect(http_cookbook_sync).to receive(:post).
+ with("environments/_default/cookbook_versions", {:run_list => []}).
+ and_return({})
+ end
+
+ def stub_for_converge
+ # define me
+ end
+
+ def stub_for_audit
+ # define me
+ end
+
+ def stub_for_node_save
+ # define me
+ end
+
+ def stub_for_run
+ # define me
+ end
+
+ before do
+ Chef::Config[:client_fork] = enable_fork
+ Chef::Config[:cache_path] = windows? ? 'C:\chef' : '/var/chef'
+ Chef::Config[:why_run] = false
+ Chef::Config[:audit_mode] = :enabled
+
+ stub_const("Chef::Client::STDOUT_FD", stdout)
+ stub_const("Chef::Client::STDERR_FD", stderr)
+
+ stub_for_register
+ stub_for_node_load
+ stub_for_sync_cookbooks
+ stub_for_converge
+ stub_for_audit
+ stub_for_node_save
+
+ expect_any_instance_of(Chef::RunLock).to receive(:acquire)
+ expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
+ expect_any_instance_of(Chef::RunLock).to receive(:release)
+
+ # Post conditions: check that node has been filled in correctly
+ expect(client).to receive(:run_started)
+
+ stub_for_run
+ end
+end
+
+shared_context "converge completed" do
+ def stub_for_converge
+ # --Client#converge
+ expect(Chef::Runner).to receive(:new).and_return(runner)
+ expect(runner).to receive(:converge).and_return(true)
+ end
+
+ def stub_for_node_save
+ allow(node).to receive(:data_for_save).and_return(node.for_json)
+
+ # --Client#save_updated_node
+ expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key], validate_utf8: false).and_return(http_node_save)
+ expect(http_node_save).to receive(:put_rest).with("nodes/#{fqdn}", node.for_json).and_return(true)
+ end
+end
+
+shared_context "converge failed" do
+ let(:converge_error) do
+ err = Chef::Exceptions::UnsupportedAction.new("Action unsupported")
+ err.set_backtrace([ "/path/recipe.rb:15", "/path/recipe.rb:12" ])
+ err
+ end
+
+ def stub_for_converge
+ expect(Chef::Runner).to receive(:new).and_return(runner)
+ expect(runner).to receive(:converge).and_raise(converge_error)
+ end
+
+ def stub_for_node_save
+ expect(client).to_not receive(:save_updated_node)
+ end
+end
+
+shared_context "audit phase completed" do
+ def stub_for_audit
+ # -- Client#run_audits
+ expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
+ expect(audit_runner).to receive(:run).and_return(true)
+ expect(client.events).to receive(:audit_phase_complete)
+ end
+end
+
+shared_context "audit phase failed with error" do
+ let(:audit_error) do
+ err = RuntimeError.new("Unexpected audit error")
+ err.set_backtrace([ "/path/recipe.rb:57", "/path/recipe.rb:55" ])
+ err
+ end
+
+ def stub_for_audit
+ expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
+ expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!")
+ expect(audit_runner).to receive(:run).and_raise(audit_error)
+ expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!")
+ end
+end
+
+shared_context "audit phase completed with failed controls" do
+ let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => true,
+ :num_failed => 1, :num_total => 3) }
+
+ let(:audit_error) do
+ err = Chef::Exceptions::AuditsFailed.new(audit_runner.num_failed, audit_runner.num_total)
+ err.set_backtrace([ "/path/recipe.rb:108", "/path/recipe.rb:103" ])
+ err
+ end
+
+ def stub_for_audit
+ expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
+ expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!")
+ expect(audit_runner).to receive(:run)
+ expect(Chef::Exceptions::AuditsFailed).to receive(:new).with(
+ audit_runner.num_failed, audit_runner.num_total
+ ).and_return(audit_error)
+ expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!")
+ end
+end
+
+shared_context "run completed" do
+ def stub_for_run
+ expect(client).to receive(:run_completed_successfully)
+
+ # --ResourceReporter#run_completed
+ # updates the server with the resource history
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::ResourceReporter).to receive(:run_completed)
+ # --AuditReporter#run_completed
+ # posts the audit data to server.
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_completed)
+ end
+end
+
+shared_context "run failed" do
+ def stub_for_run
+ expect(client).to receive(:run_failed)
+
+ # --ResourceReporter#run_completed
+ # updates the server with the resource history
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed)
+ # --AuditReporter#run_completed
+ # posts the audit data to server.
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed)
+ end
+
+ before do
+ expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
+ end
+end
diff --git a/spec/support/shared/examples/client.rb b/spec/support/shared/examples/client.rb
new file mode 100644
index 0000000000..330cb40ac6
--- /dev/null
+++ b/spec/support/shared/examples/client.rb
@@ -0,0 +1,53 @@
+
+require 'spec_helper'
+require 'spec/support/shared/context/client'
+
+# requires platform and platform_version be defined
+shared_examples "a completed run" do
+ include_context "run completed"
+
+ it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges, and runs audits" do
+ # This is what we're testing.
+ expect(client.run).to be true
+
+ # fork is stubbed, so we can see the outcome of the run
+ expect(node.automatic_attrs[:platform]).to eq(platform)
+ expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
+ end
+end
+
+shared_examples "a completed run with audit failure" do
+ include_context "run completed"
+
+ before do
+ expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
+ end
+
+ it "converges, runs audits, saves the node and raises the error in a wrapping error" do
+ expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
+ expect(error.wrapped_errors.size).to eq(run_errors.size)
+ run_errors.each do |run_error|
+ expect(error.wrapped_errors).to include(run_error)
+ expect(error.backtrace).to include(*run_error.backtrace)
+ end
+ end
+
+ # fork is stubbed, so we can see the outcome of the run
+ expect(node.automatic_attrs[:platform]).to eq(platform)
+ expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
+ end
+end
+
+shared_examples "a failed run" do
+ include_context "run failed"
+
+ it "skips node save and raises the error in a wrapping error" do
+ expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
+ expect(error.wrapped_errors.size).to eq(run_errors.size)
+ run_errors.each do |run_error|
+ expect(error.wrapped_errors).to include(run_error)
+ expect(error.backtrace).to include(*run_error.backtrace)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared/functional/file_resource.rb b/spec/support/shared/functional/file_resource.rb
index 4f8e2f5b71..3ce3c9c94e 100644
--- a/spec/support/shared/functional/file_resource.rb
+++ b/spec/support/shared/functional/file_resource.rb
@@ -592,10 +592,6 @@ shared_examples_for "a configured file resource" do
File.open(path, "wb") { |f| f.write(wrong_content) }
end
- it "updates the source file content" do
- skip
- end
-
it "marks the resource as updated" do
resource.run_action(:create)
expect(resource).to be_updated_by_last_action
diff --git a/spec/support/shared/functional/securable_resource.rb b/spec/support/shared/functional/securable_resource.rb
index e016bb685d..b3c32356aa 100644
--- a/spec/support/shared/functional/securable_resource.rb
+++ b/spec/support/shared/functional/securable_resource.rb
@@ -163,9 +163,6 @@ shared_examples_for "a securable resource with existing target" do
let(:desired_gid) { 1337 }
let(:expected_gid) { 1337 }
- skip "should set an owner (Rerun specs under root)", :requires_unprivileged_user => true
- skip "should set a group (Rerun specs under root)", :requires_unprivileged_user => true
-
describe "when setting the owner", :requires_root do
before do
resource.owner expected_user_name
@@ -205,11 +202,6 @@ shared_examples_for "a securable resource with existing target" do
resource.run_action(:create)
end
- it "should set permissions as specified" do
- pending("Linux does not support lchmod")
- expect{ File.lstat(path).mode & 007777 }.to eq(@mode_string.oct & 007777)
- end
-
it "is marked as updated only if changes are made" do
expect(resource.updated_by_last_action?).to eq(expect_updated?)
end
@@ -222,15 +214,28 @@ shared_examples_for "a securable resource with existing target" do
resource.run_action(:create)
end
- it "should set permissions in numeric form as a ruby-interpreted octal" do
- pending('Linux does not support lchmod')
- expect{ File.lstat(path).mode & 007777 }.to eq(@mode_integer & 007777)
- end
-
it "is marked as updated only if changes are made" do
expect(resource.updated_by_last_action?).to eq(expect_updated?)
end
end
+
+ describe "when setting the suid bit", :requires_root do
+ before do
+ @suid_mode = 04776
+ resource.mode @suid_mode
+ resource.run_action(:create)
+ end
+
+ it "should set the suid bit" do
+ expect(File.lstat(path).mode & 007777).to eq(@suid_mode & 007777)
+ end
+
+ it "should retain the suid bit when updating the user" do
+ resource.user 1338
+ resource.run_action(:create)
+ expect(File.lstat(path).mode & 007777).to eq(@suid_mode & 007777)
+ end
+ end
end
context "on Windows", :windows_only do
@@ -288,17 +293,13 @@ shared_examples_for "a securable resource without existing target" do
include_context "diff disabled"
- context "on Unix", :unix_only do
- skip "if we need any securable resource tests on Unix without existing target resource."
- end
-
context "on Windows", :windows_only do
include_context "use Windows permissions"
- it "sets owner to Administrators on create if owner is not specified" do
+ it "leaves owner as system default on create if owner is not specified" do
expect(File.exist?(path)).to eq(false)
resource.run_action(:create)
- expect(descriptor.owner).to eq(SID.Administrators)
+ expect(descriptor.owner).to eq(SID.default_security_object_owner)
end
it "sets owner when owner is specified" do
@@ -318,22 +319,24 @@ shared_examples_for "a securable resource without existing target" do
end
it "leaves owner alone if owner is not specified and resource already exists" do
- # Set owner to Guest so it's not the same as the current user (which is the default on create)
- resource.owner 'Guest'
+ arbitrary_non_default_owner = SID.Guest
+ expect(arbitrary_non_default_owner).not_to eq(SID.default_security_object_owner)
+
+ resource.owner 'Guest' # Change to arbitrary_non_default_owner once issue #1508 is fixed
resource.run_action(:create)
- expect(descriptor.owner).to eq(SID.Guest)
+ expect(descriptor.owner).to eq(arbitrary_non_default_owner)
new_resource = create_resource
expect(new_resource.owner).to eq(nil)
new_resource.run_action(:create)
- expect(descriptor.owner).to eq(SID.Guest)
+ expect(descriptor.owner).to eq(arbitrary_non_default_owner)
end
- it "sets group to None on create if group is not specified" do
+ it "leaves group as system default on create if group is not specified" do
expect(resource.group).to eq(nil)
expect(File.exist?(path)).to eq(false)
resource.run_action(:create)
- expect(descriptor.group).to eq(SID.None)
+ expect(descriptor.group).to eq(SID.default_security_object_group)
end
it "sets group when group is specified" do
@@ -346,23 +349,18 @@ shared_examples_for "a securable resource without existing target" do
expect { resource.group 'Lance "The Nose" Glindenberry III' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
- it "sets group when group is specified with a \\" do
- pending("Need to find a group containing a backslash that is on most peoples' machines")
- resource.group "#{ENV['COMPUTERNAME']}\\Administrators"
- resource.run_action(:create)
- expect{ descriptor.group }.to eq(SID.Everyone)
- end
-
it "leaves group alone if group is not specified and resource already exists" do
- # Set group to Everyone so it's not the default (None)
- resource.group 'Everyone'
+ arbitrary_non_default_group = SID.Everyone
+ expect(arbitrary_non_default_group).not_to eq(SID.default_security_object_group)
+
+ resource.group 'Everyone' # Change to arbitrary_non_default_group once issue #1508 is fixed
resource.run_action(:create)
- expect(descriptor.group).to eq(SID.Everyone)
+ expect(descriptor.group).to eq(arbitrary_non_default_group)
new_resource = create_resource
expect(new_resource.group).to eq(nil)
new_resource.run_action(:create)
- expect(descriptor.group).to eq(SID.Everyone)
+ expect(descriptor.group).to eq(arbitrary_non_default_group)
end
describe "with rights and deny_rights attributes" do
diff --git a/spec/support/shared/functional/securable_resource_with_reporting.rb b/spec/support/shared/functional/securable_resource_with_reporting.rb
index 37fc538801..3176ebba0d 100644
--- a/spec/support/shared/functional/securable_resource_with_reporting.rb
+++ b/spec/support/shared/functional/securable_resource_with_reporting.rb
@@ -279,14 +279,14 @@ shared_examples_for "a securable resource with reporting" do
end
it "has empty values for file metadata in 'current_resource'" do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
expect(current_resource.owner).to be_nil
expect(current_resource.expanded_rights).to be_nil
end
context "and no security metadata is specified in new_resource" do
before do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
end
it "sets the metadata values on the new_resource as strings after creating" do
@@ -322,7 +322,7 @@ shared_examples_for "a securable resource with reporting" do
let(:expected_user_name) { 'domain\user' }
before do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
resource.owner(expected_user_name)
resource.run_action(:create)
end
@@ -336,7 +336,7 @@ shared_examples_for "a securable resource with reporting" do
context "when the target file exists" do
before do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
FileUtils.touch(resource.path)
resource.action(:create)
end
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index 35b86dc4e8..3499cc98ec 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -114,7 +114,7 @@ shared_context Chef::Resource::WindowsScript do
describe "when the run action is invoked on Windows" do
it "executes the script code" do
- resource.code("@whoami > #{script_output_path}")
+ resource.code("whoami > #{script_output_path}")
resource.returns(0)
resource.run_action(:run)
end
diff --git a/spec/support/shared/integration/integration_helper.rb b/spec/support/shared/integration/integration_helper.rb
index e6942c62af..927ff2f42b 100644
--- a/spec/support/shared/integration/integration_helper.rb
+++ b/spec/support/shared/integration/integration_helper.rb
@@ -22,14 +22,19 @@ require 'fileutils'
require 'chef/config'
require 'chef/json_compat'
require 'chef/server_api'
-require 'chef_zero/rspec'
require 'support/shared/integration/knife_support'
require 'support/shared/integration/app_server_support'
+require 'cheffish/rspec/chef_run_support'
require 'spec_helper'
module IntegrationSupport
include ChefZero::RSpec
+ def self.included(includer_class)
+ includer_class.extend(Cheffish::RSpec::ChefRunSupport)
+ includer_class.extend(ClassMethods)
+ end
+
module ClassMethods
include ChefZero::RSpec
@@ -49,10 +54,6 @@ module IntegrationSupport
end
end
- def self.included(includer_class)
- includer_class.extend(ClassMethods)
- end
-
def api
Chef::ServerAPI.new
end
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
new file mode 100644
index 0000000000..a4f353de60
--- /dev/null
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 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 "chef/exceptions"
+
+shared_examples_for "version handling" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+
+ before do
+ allow(rest_v1).to receive(http_verb).and_raise(exception_406)
+ end
+
+ context "when the server does not support the min or max server API version that Chef::User supports" do
+ before do
+ allow(object).to receive(:server_client_api_version_intersection).and_return([])
+ end
+
+ it "raises the original exception" do
+ expect{ object.send(method) }.to raise_error(exception_406)
+ end
+ end # when the server does not support the min or max server API version that Chef::User supports
+end # version handling
+
+shared_examples_for "user and client reregister" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:generic_exception) { Exception.new }
+ let(:min_version) { "2" }
+ let(:max_version) { "5" }
+ let(:return_hash_406) {
+ {
+ "min_version" => min_version,
+ "max_version" => max_version,
+ "request_version" => "30"
+ }
+ }
+
+ context "when V0 is not supported by the server" do
+ context "when the exception is 406 and returns x-ops-server-api-version header" do
+ before do
+ allow(rest_v0).to receive(:put).and_raise(exception_406)
+ allow(response_406).to receive(:[]).with('x-ops-server-api-version').and_return(Chef::JSONCompat.to_json(return_hash_406))
+ end
+
+ it "raises an error about only V0 being supported" do
+ expect(object).to receive(:reregister_only_v0_supported_error_msg).with(max_version, min_version)
+ expect{ object.reregister }.to raise_error(Chef::Exceptions::OnlyApiVersion0SupportedForAction)
+ end
+
+ end
+ context "when the exception is not versioning related" do
+ before do
+ allow(rest_v0).to receive(:put).and_raise(generic_exception)
+ end
+
+ it "raises the original error" do
+ expect{ object.reregister }.to raise_error(generic_exception)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared/unit/knife_shared.rb b/spec/support/shared/unit/knife_shared.rb
new file mode 100644
index 0000000000..8c9010f3cf
--- /dev/null
+++ b/spec/support/shared/unit/knife_shared.rb
@@ -0,0 +1,40 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 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.
+#
+
+
+shared_examples_for "mandatory field missing" do
+ context "when field is nil" do
+ before do
+ knife.name_args = name_args
+ end
+
+ it "exits 1" do
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "prints the usage" do
+ expect(knife).to receive(:show_usage)
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "prints a relevant error message" do
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /You must specify a #{fieldname}/
+ end
+ end
+end
diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb
index 86f32c9e89..7de9698451 100644
--- a/spec/support/shared/unit/provider/file.rb
+++ b/spec/support/shared/unit/provider/file.rb
@@ -255,7 +255,7 @@ shared_examples_for Chef::Provider::File do
context "examining file security metadata on Unix with a file that exists" do
before do
# fake that we're on unix even if we're on windows
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
# mock up the filesystem to behave like unix
setup_normal_file
stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000)
@@ -331,7 +331,7 @@ shared_examples_for Chef::Provider::File do
context "examining file security metadata on Unix with a file that does not exist" do
before do
# fake that we're on unix even if we're on windows
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
setup_missing_file
end
@@ -380,7 +380,7 @@ shared_examples_for Chef::Provider::File do
before do
# fake that we're on unix even if we're on windows
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
# mock up the filesystem to behave like unix
setup_normal_file
stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000)
@@ -529,26 +529,49 @@ shared_examples_for Chef::Provider::File do
:for_reporting => diff_for_reporting )
allow(diff).to receive(:diff).with(resource_path, tempfile_path).and_return(true)
expect(provider).to receive(:diff).at_least(:once).and_return(diff)
- expect(provider).to receive(:managing_content?).at_least(:once).and_return(true)
expect(provider).to receive(:checksum).with(tempfile_path).and_return(tempfile_sha256)
- expect(provider).to receive(:checksum).with(resource_path).and_return(tempfile_sha256)
+ allow(provider).to receive(:managing_content?).and_return(true)
+ allow(provider).to receive(:checksum).with(resource_path).and_return(tempfile_sha256)
+ expect(resource).not_to receive(:checksum).with(tempfile_sha256) # do not mutate the new resource
expect(provider.deployment_strategy).to receive(:deploy).with(tempfile_path, normalized_path)
end
context "when the file was created" do
before { expect(provider).to receive(:needs_creating?).at_least(:once).and_return(true) }
- it "does not backup the file and does not produce a diff for reporting" do
+ it "does not backup the file" do
expect(provider).not_to receive(:do_backup)
provider.send(:do_contents_changes)
+ end
+
+ it "does not produce a diff for reporting" do
+ provider.send(:do_contents_changes)
expect(resource.diff).to be_nil
end
+
+ it "renders the final checksum correctly for reporting" do
+ provider.send(:do_contents_changes)
+ expect(resource.state_for_resource_reporter[:checksum]).to eql(tempfile_sha256)
+ end
end
context "when the file was not created" do
- before { expect(provider).to receive(:needs_creating?).at_least(:once).and_return(false) }
- it "backs up the file and produces a diff for reporting" do
+ before do
+ allow(provider).to receive(:do_backup) # stub do_backup
+ expect(provider).to receive(:needs_creating?).at_least(:once).and_return(false)
+ end
+
+ it "backs up the file" do
expect(provider).to receive(:do_backup)
provider.send(:do_contents_changes)
+ end
+
+ it "produces a diff for reporting" do
+ provider.send(:do_contents_changes)
expect(resource.diff).to eq(diff_for_reporting)
end
+
+ it "renders the final checksum correctly for reporting" do
+ provider.send(:do_contents_changes)
+ expect(resource.state_for_resource_reporter[:checksum]).to eql(tempfile_sha256)
+ end
end
end
diff --git a/spec/support/shared/unit/user_and_client_shared.rb b/spec/support/shared/unit/user_and_client_shared.rb
new file mode 100644
index 0000000000..bc5ffa07c2
--- /dev/null
+++ b/spec/support/shared/unit/user_and_client_shared.rb
@@ -0,0 +1,115 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 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.
+#
+
+shared_examples_for "user or client create" do
+
+ context "when server API V1 is valid on the Chef Server receiving the request" do
+
+ it "creates a new object via the API" do
+ expect(rest_v1).to receive(:post).with(url, payload).and_return({})
+ object.create
+ end
+
+ it "creates a new object via the API with a public_key when it exists" do
+ object.public_key "some_public_key"
+ expect(rest_v1).to receive(:post).with(url, payload.merge({:public_key => "some_public_key"})).and_return({})
+ object.create
+ end
+
+ context "raise error when create_key and public_key are both set" do
+
+ before do
+ object.public_key "key"
+ object.create_key true
+ end
+
+ it "rasies the proper error" do
+ expect { object.create }.to raise_error(error)
+ end
+ end
+
+ context "when create_key == true" do
+ before do
+ object.create_key true
+ end
+
+ it "creates a new object via the API with create_key" do
+ expect(rest_v1).to receive(:post).with(url, payload.merge({:create_key => true})).and_return({})
+ object.create
+ end
+ end
+
+ context "when chef_key is returned by the server" do
+ let(:chef_key) {
+ {
+ "chef_key" => {
+ "public_key" => "some_public_key"
+ }
+ }
+ }
+
+ it "puts the public key into the objectr returned by create" do
+ expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
+ new_object = object.create
+ expect(new_object.public_key).to eq("some_public_key")
+ end
+
+ context "when private_key is returned in chef_key" do
+ let(:chef_key) {
+ {
+ "chef_key" => {
+ "public_key" => "some_public_key",
+ "private_key" => "some_private_key"
+ }
+ }
+ }
+
+ it "puts the private key into the object returned by create" do
+ expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
+ new_object = object.create
+ expect(new_object.private_key).to eq("some_private_key")
+ end
+ end
+ end # when chef_key is returned by the server
+
+ end # when server API V1 is valid on the Chef Server receiving the request
+
+ context "when server API V1 is not valid on the Chef Server receiving the request" do
+
+ context "when the server supports API V0" do
+ before do
+ allow(object).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(rest_v1).to receive(:post).and_raise(exception_406)
+ end
+
+ it "creates a new object via the API" do
+ expect(rest_v0).to receive(:post).with(url, payload).and_return({})
+ object.create
+ end
+
+ it "creates a new object via the API with a public_key when it exists" do
+ object.public_key "some_public_key"
+ expect(rest_v0).to receive(:post).with(url, payload.merge({:public_key => "some_public_key"})).and_return({})
+ object.create
+ end
+
+ end # when the server supports API V0
+ end # when server API V1 is not valid on the Chef Server receiving the request
+
+end # user or client create
+