summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chef-config/lib/chef-config/config.rb41
-rw-r--r--chef-config/spec/unit/config_spec.rb104
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb26
-rw-r--r--lib/chef/chef_fs/config.rb34
-rw-r--r--lib/chef/local_mode.rb2
-rw-r--r--lib/chef/provider/apt_update.rb78
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/apt_update.rb33
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--spec/integration/knife/chef_fs_data_store_spec.rb639
-rw-r--r--spec/unit/provider/apt_update_spec.rb113
-rw-r--r--spec/unit/resource/apt_update_spec.rb38
12 files changed, 871 insertions, 239 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index eda684a6b4..68cece43da 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -114,6 +114,8 @@ module ChefConfig
File.expand_path("..", path)
end
end
+ elsif configuration[:cookbook_artifact_path]
+ File.expand_path("..", self.configuration[:cookbook_artifact_path])
else
cache_path
end
@@ -123,7 +125,7 @@ module ChefConfig
# In local mode, we auto-discover the repo root by looking for a path with "cookbooks" under it.
# This allows us to run config-free.
path = cwd
- until File.directory?(PathHelper.join(path, "cookbooks"))
+ until File.directory?(PathHelper.join(path, "cookbooks")) || File.directory?(PathHelper.join(path, "cookbook_artifacts"))
new_path = File.expand_path("..", path)
if new_path == path
ChefConfig.logger.warn("No cookbooks directory found at or above current directory. Assuming #{Dir.pwd}.")
@@ -306,6 +308,28 @@ module ChefConfig
default :diff_output_threshold, 1000000
default :local_mode, false
+ # Configures the mode of operation for ChefFS, which is applied to the
+ # ChefFS-based knife commands and chef-client's local mode. (ChefFS-based
+ # knife commands include: knife delete, knife deps, knife diff, knife down,
+ # knife edit, knife list, knife show, knife upload, and knife xargs.)
+ #
+ # Valid values are:
+ # * "static": ChefFS only manages objects that exist in a traditional Chef
+ # Repo as of Chef 11.
+ # * "everything": ChefFS manages all object types that existed on the OSS
+ # Chef 11 server.
+ # * "hosted_everything": ChefFS manages all object types as of the Chef 12
+ # Server, including RBAC objects and Policyfile objects (new to Chef 12).
+ default :repo_mode do
+ if local_mode && !chef_zero.osc_compat
+ "hosted_everything"
+ elsif chef_server_url =~ /\/+organizations\/.+/
+ "hosted_everything"
+ else
+ "everything"
+ end
+ end
+
default :pid_file, nil
# Whether Chef Zero local mode should bind to a port. All internal requests
@@ -321,6 +345,21 @@ module ChefConfig
default(:enabled) { ChefConfig::Config.local_mode }
default :host, "localhost"
default :port, 8889.upto(9999) # Will try ports from 8889-9999 until one works
+
+ # When set to a String, Chef Zero disables multitenant support. This is
+ # what you want when using Chef Zero to serve a single Chef Repo. Setting
+ # this to `false` enables multi-tenant.
+ default :single_org, "chef"
+
+ # Whether Chef Zero should operate in a mode analogous to OSS Chef Server
+ # 11 (true) or Chef Server 12 (false). Chef Zero can still serve
+ # policyfile objects in Chef 11 mode, as long as `repo_mode` is set to
+ # "hosted_everything". The primary differences are:
+ # * Chef 11 mode doesn't support multi-tennant, so there is no
+ # distinction between global and org-specific objects (since there are
+ # no orgs).
+ # * Chef 11 mode doesn't expose RBAC objects
+ default :osc_compat, false
end
default :chef_server_url, "https://localhost:443"
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
index 5983981ddd..ec14ad065a 100644
--- a/chef-config/spec/unit/config_spec.rb
+++ b/chef-config/spec/unit/config_spec.rb
@@ -288,6 +288,104 @@ RSpec.describe ChefConfig::Config do
expect(ChefConfig::Config[:ssl_ca_path]).to be_nil
end
+ describe "ChefConfig::Config[:repo_mode]" do
+
+ context "when local mode is enabled" do
+
+ before { ChefConfig::Config[:local_mode] = true }
+
+ it "defaults to 'hosted_everything'" do
+ expect(ChefConfig::Config[:repo_mode]).to eq("hosted_everything")
+ end
+
+ context "and osc_compat is enabled" do
+
+ before { ChefConfig::Config.chef_zero.osc_compat = true }
+
+ it "defaults to 'everything'" do
+ expect(ChefConfig::Config[:repo_mode]).to eq("everything")
+ end
+ end
+ end
+
+ context "when local mode is not enabled" do
+
+ context "and the chef_server_url is multi-tenant" do
+
+ before { ChefConfig::Config[:chef_server_url] = "https://chef.example/organizations/example" }
+
+ it "defaults to 'hosted_everything'" do
+ expect(ChefConfig::Config[:repo_mode]).to eq("hosted_everything")
+ end
+
+ end
+
+ context "and the chef_server_url is not multi-tenant" do
+
+ before { ChefConfig::Config[:chef_server_url] = "https://chef.example/" }
+
+ it "defaults to 'everything'" do
+ expect(ChefConfig::Config[:repo_mode]).to eq("everything")
+ end
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:chef_repo_path]" do
+
+ context "when cookbook_path is set to a single path" do
+
+ before { ChefConfig::Config[:cookbook_path] = "/home/anne/repo/cookbooks" }
+
+ it "is set to a path one directory up from the cookbook_path" do
+ expected = File.expand_path("/home/anne/repo")
+ expect(ChefConfig::Config[:chef_repo_path]).to eq(expected)
+ end
+
+ end
+
+ context "when cookbook_path is set to multiple paths" do
+
+ before do
+ ChefConfig::Config[:cookbook_path] = [
+ "/home/anne/repo/cookbooks",
+ "/home/anne/other_repo/cookbooks",
+ ]
+ end
+
+ it "is set to an Array of paths one directory up from the cookbook_paths" do
+ expected = [ "/home/anne/repo", "/home/anne/other_repo"].map { |p| File.expand_path(p) }
+ expect(ChefConfig::Config[:chef_repo_path]).to eq(expected)
+ end
+
+ end
+
+ context "when cookbook_path is not set but cookbook_artifact_path is set" do
+
+ before do
+ ChefConfig::Config[:cookbook_path] = nil
+ ChefConfig::Config[:cookbook_artifact_path] = "/home/roxie/repo/cookbook_artifacts"
+ end
+
+ it "is set to a path one directory up from the cookbook_artifact_path" do
+ expected = File.expand_path("/home/roxie/repo")
+ expect(ChefConfig::Config[:chef_repo_path]).to eq(expected)
+ end
+
+ end
+
+ context "when cookbook_path is not set" do
+
+ before { ChefConfig::Config[:cookbook_path] = nil }
+
+ it "is set to the cache_path" do
+ expect(ChefConfig::Config[:chef_repo_path]).to eq(ChefConfig::Config[:cache_path])
+ end
+
+ end
+
+ end
+
# On Windows, we'll detect an omnibus build and set this to the
# cacert.pem included in the package, but it's nil if you're on Windows
# w/o omnibus (e.g., doing development on Windows, custom build, etc.)
@@ -309,6 +407,12 @@ RSpec.describe ChefConfig::Config do
expect(ChefConfig::Config[:environment_path]).to eq(environment_path)
end
+ it "ChefConfig::Config[:cookbook_artifact_path] defaults to /var/chef/cookbook_artifacts" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ environment_path = is_windows ? "#{primary_cache_path}\\cookbook_artifacts" : "#{primary_cache_path}/cookbook_artifacts"
+ expect(ChefConfig::Config[:cookbook_artifact_path]).to eq(environment_path)
+ end
+
describe "setting the config dir" do
context "when the config file is given with a relative path" do
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index 634faaec7e..59c2699cca 100644
--- a/lib/chef/chef_fs/chef_fs_data_store.rb
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -126,6 +126,24 @@ class Chef
# - `delete(association_requests/NAME)` -> `get(/invitations.json)`, remove name, `set(/invitations.json)`
#
class ChefFSDataStore
+
+ # The base directories in a Chef Repo; even when these don't exist, a
+ # matching GET for these objects will return an empty list instead of a
+ # 404.
+ BASE_DIRNAMES = %w{
+ clients
+ cookbooks
+ data
+ environments
+ nodes
+ roles
+ users
+ containers
+ groups
+ policy_groups
+ policies
+ }.freeze
+
#
# Create a new ChefFSDataStore
#
@@ -469,7 +487,11 @@ class Chef
# LIST /policies
elsif path == [ "policies" ]
with_entry([ path[0] ]) do |policies|
- policies.children.map { |policy| policy.name[0..-6].rpartition("-")[0] }.uniq
+ begin
+ policies.children.map { |policy| policy.name[0..-6].rpartition("-")[0] }.uniq
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ []
+ end
end
# LIST /policies/POLICY/revisions
@@ -741,7 +763,7 @@ class Chef
end
def path_always_exists?(path)
- return path.length == 1 && %w{clients cookbooks data environments nodes roles users}.include?(path[0])
+ return path.length == 1 && BASE_DIRNAMES.include?(path[0])
end
def with_entry(path)
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
index 07c014e2ab..a376c42cc5 100644
--- a/lib/chef/chef_fs/config.rb
+++ b/lib/chef/chef_fs/config.rb
@@ -47,6 +47,34 @@ class Chef
INFLECTIONS.each { |k,v| k.freeze; v.freeze }
INFLECTIONS.freeze
+ # ChefFS supports three modes of operation: "static", "everything", and
+ # "hosted_everything". These names are antiquated since Chef 12 moved
+ # multi-tenant and RBAC to the open source product. In practice, they
+ # mean:
+ #
+ # * static: just static objects that are included in a traditional
+ # chef-repo, with no support for anything introduced in Chef 12 or
+ # later.
+ # * everything: all of the objects supported by the open source Chef
+ # Server 11.x
+ # * hosted_everything: (the name comes from Hosted Chef) supports
+ # everything in Chef Server 12 and later, including RBAC objects and
+ # Policyfile objects.
+ #
+ # The "static" and "everything" modes are used for backup and
+ # upgrade/migration of older Chef Servers, so they should be considered
+ # frozen in time.
+
+ CHEF_11_OSS_STATIC_OBJECTS = %w{cookbooks cookbook_artifacts data_bags environments roles}.freeze
+ CHEF_11_OSS_DYNAMIC_OBJECTS = %w{clients nodes users}.freeze
+ RBAC_OBJECT_NAMES = %w{acls containers groups }.freeze
+ CHEF_12_OBJECTS = %w{ cookbook_artifacts policies policy_groups }.freeze
+
+ STATIC_MODE_OBJECT_NAMES = CHEF_11_OSS_STATIC_OBJECTS
+ EVERYTHING_MODE_OBJECT_NAMES = (CHEF_11_OSS_STATIC_OBJECTS + CHEF_11_OSS_DYNAMIC_OBJECTS).freeze
+ HOSTED_EVERYTHING_MODE_OBJECT_NAMES = (EVERYTHING_MODE_OBJECT_NAMES + RBAC_OBJECT_NAMES + CHEF_12_OBJECTS).freeze
+
+
#
# Create a new Config object which can produce a chef_fs and local_fs.
#
@@ -234,11 +262,11 @@ class Chef
result = {}
case @chef_config[:repo_mode]
when "static"
- object_names = %w{cookbooks data_bags environments roles}
+ object_names = STATIC_MODE_OBJECT_NAMES
when "hosted_everything"
- object_names = %w{acls clients cookbooks cookbook_artifacts containers data_bags environments groups nodes roles policies policy_groups}
+ object_names = HOSTED_EVERYTHING_MODE_OBJECT_NAMES
else
- object_names = %w{clients cookbooks data_bags environments nodes roles users}
+ object_names = EVERYTHING_MODE_OBJECT_NAMES
end
object_names.each do |object_name|
# cookbooks -> cookbook_path
diff --git a/lib/chef/local_mode.rb b/lib/chef/local_mode.rb
index 53234ec7d5..82d9cdee32 100644
--- a/lib/chef/local_mode.rb
+++ b/lib/chef/local_mode.rb
@@ -65,6 +65,8 @@ class Chef
server_options = {}
server_options[:data_store] = data_store
server_options[:log_level] = Chef::Log.level
+ server_options[:osc_compat] = Chef::Config.chef_zero.osc_compat
+ server_options[:single_org] = Chef::Config.chef_zero.single_org
server_options[:host] = Chef::Config.chef_zero.host
server_options[:port] = parse_port(Chef::Config.chef_zero.port)
diff --git a/lib/chef/provider/apt_update.rb b/lib/chef/provider/apt_update.rb
new file mode 100644
index 0000000000..00c3ad16bb
--- /dev/null
+++ b/lib/chef/provider/apt_update.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 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 "chef/resource"
+require "chef/dsl/declare_resource"
+
+class Chef
+ class Provider
+ class AptUpdate < Chef::Provider
+ include Chef::DSL::DeclareResource
+
+ provides :apt_update, os: "linux"
+
+ APT_CONF_DIR = "/etc/apt/apt.conf.d"
+ STAMP_DIR = "/var/lib/apt/periodic"
+
+ def whyrun_supported?
+ true
+ end
+
+ def load_current_resource
+ end
+
+ def action_periodic
+ if !apt_up_to_date?
+ converge_by "update new lists of packages" do
+ do_update
+ end
+ end
+ end
+
+ def action_update
+ converge_by "force update new lists of packages" do
+ do_update
+ end
+ end
+
+ private
+ # Determines whether we need to run `apt-get update`
+ #
+ # @return [Boolean]
+ def apt_up_to_date?
+ ::File.exist?("#{STAMP_DIR}/update-success-stamp") &&
+ ::File.mtime("#{STAMP_DIR}/update-success-stamp") > Time.now - new_resource.frequency
+ end
+
+ def do_update
+ [STAMP_DIR, APT_CONF_DIR].each do |d|
+ build_resource(:directory, d, caller[0]) do
+ recursive true
+ end.run_action(:create)
+ end
+
+ build_resource(:file, "#{APT_CONF_DIR}/15update-stamp", caller[0]) do
+ content "APT::Update::Post-Invoke-Success {\"touch #{STAMP_DIR}/update-success-stamp 2>/dev/null || true\";};"
+ end.run_action(:create_if_missing)
+
+ shell_out!("apt-get -q update")
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index b024be5b62..935933d326 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require "chef/provider/apt_update"
require "chef/provider/batch"
require "chef/provider/breakpoint"
require "chef/provider/cookbook_file"
diff --git a/lib/chef/resource/apt_update.rb b/lib/chef/resource/apt_update.rb
new file mode 100644
index 0000000000..df2033b063
--- /dev/null
+++ b/lib/chef/resource/apt_update.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 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 "chef/resource"
+
+class Chef
+ class Resource
+ class AptUpdate < Chef::Resource
+ resource_name :apt_update
+ provides :apt_update, os: "linux"
+
+ property :frequency, Integer, default: 86_400
+
+ default_action :periodic
+ allowed_actions :update, :periodic
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index dc1b195d25..647db5fc00 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -17,6 +17,7 @@
#
require "chef/resource/apt_package"
+require "chef/resource/apt_update"
require "chef/resource/bash"
require "chef/resource/batch"
require "chef/resource/breakpoint"
diff --git a/spec/integration/knife/chef_fs_data_store_spec.rb b/spec/integration/knife/chef_fs_data_store_spec.rb
index b4f2d4ca71..145adc3e4c 100644
--- a/spec/integration/knife/chef_fs_data_store_spec.rb
+++ b/spec/integration/knife/chef_fs_data_store_spec.rb
@@ -29,22 +29,54 @@ describe "ChefFSDataStore tests", :workstation do
let(:cookbook_x_100_metadata_rb) { cb_metadata("x", "1.0.0") }
let(:cookbook_z_100_metadata_rb) { cb_metadata("z", "1.0.0") }
- when_the_repository "has one of each thing" do
+ describe "with repo mode 'hosted_everything' (default)" do
before do
- file "clients/x.json", {}
- file "cookbooks/x/metadata.rb", cookbook_x_100_metadata_rb
- file "data_bags/x/y.json", {}
- file "environments/x.json", {}
- file "nodes/x.json", {}
- file "roles/x.json", {}
- file "users/x.json", {}
+ Chef::Config.chef_zero.osc_compat = false
end
- context "GET /TYPE" do
- it "knife list -z -R returns everything" do
- knife("list -z -Rfp /").should_succeed <<EOM
+ when_the_repository "has one of each thing" do
+ before do
+ file "clients/x.json", {}
+ file "cookbooks/x/metadata.rb", cookbook_x_100_metadata_rb
+ file "data_bags/x/y.json", {}
+ file "environments/x.json", {}
+ file "nodes/x.json", {}
+ file "roles/x.json", {}
+ # file "users/x.json", {}
+ file "containers/x.json", {}
+ file "groups/x.json", {}
+ file "containers/x.json", {}
+ file "groups/x.json", {}
+ file "policies/x.json", {}
+ file "policy_groups/x.json", {}
+ end
+
+ context "GET /TYPE" do
+ it "knife list -z -R returns everything" do
+ knife("list -z -Rfp /").should_succeed <<EOM
+/acls/
+/acls/clients/
+/acls/clients/x.json
+/acls/containers/
+/acls/containers/x.json
+/acls/cookbooks/
+/acls/cookbooks/x.json
+/acls/data_bags/
+/acls/data_bags/x.json
+/acls/environments/
+/acls/environments/x.json
+/acls/groups/
+/acls/groups/x.json
+/acls/nodes/
+/acls/nodes/x.json
+/acls/organization.json
+/acls/roles/
+/acls/roles/x.json
/clients/
/clients/x.json
+/containers/
+/containers/x.json
+/cookbook_artifacts/
/cookbooks/
/cookbooks/x/
/cookbooks/x/metadata.rb
@@ -53,317 +85,458 @@ describe "ChefFSDataStore tests", :workstation do
/data_bags/x/y.json
/environments/
/environments/x.json
+/groups/
+/groups/x.json
+/invitations.json
+/members.json
/nodes/
/nodes/x.json
+/org.json
+/policies/
+/policy_groups/
+/policy_groups/x.json
/roles/
/roles/x.json
-/users/
-/users/x.json
EOM
+ end
end
- end
- context "DELETE /TYPE/NAME" do
- it "knife delete -z /clients/x.json works" do
- knife("delete -z /clients/x.json").should_succeed "Deleted /clients/x.json\n"
- knife("list -z -Rfp /clients").should_succeed ""
- end
+ context "DELETE /TYPE/NAME" do
+ it "knife delete -z /clients/x.json works" do
+ knife("delete -z /clients/x.json").should_succeed "Deleted /clients/x.json\n"
+ knife("list -z -Rfp /clients").should_succeed ""
+ end
- it "knife delete -z -r /cookbooks/x works" do
- knife("delete -z -r /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
- knife("list -z -Rfp /cookbooks").should_succeed ""
- end
+ it "knife delete -z -r /cookbooks/x works" do
+ knife("delete -z -r /cookbooks/x").should_succeed "Deleted /cookbooks/x\n"
+ knife("list -z -Rfp /cookbooks").should_succeed ""
+ end
- it "knife delete -z -r /data_bags/x works" do
- knife("delete -z -r /data_bags/x").should_succeed "Deleted /data_bags/x\n"
- knife("list -z -Rfp /data_bags").should_succeed ""
- end
+ it "knife delete -z -r /data_bags/x works" do
+ knife("delete -z -r /data_bags/x").should_succeed "Deleted /data_bags/x\n"
+ knife("list -z -Rfp /data_bags").should_succeed ""
+ end
- it "knife delete -z /data_bags/x/y.json works" do
- knife("delete -z /data_bags/x/y.json").should_succeed "Deleted /data_bags/x/y.json\n"
- knife("list -z -Rfp /data_bags").should_succeed "/data_bags/x/\n"
- end
+ it "knife delete -z /data_bags/x/y.json works" do
+ knife("delete -z /data_bags/x/y.json").should_succeed "Deleted /data_bags/x/y.json\n"
+ knife("list -z -Rfp /data_bags").should_succeed "/data_bags/x/\n"
+ end
- it "knife delete -z /environments/x.json works" do
- knife("delete -z /environments/x.json").should_succeed "Deleted /environments/x.json\n"
- knife("list -z -Rfp /environments").should_succeed ""
- end
+ it "knife delete -z /environments/x.json works" do
+ knife("delete -z /environments/x.json").should_succeed "Deleted /environments/x.json\n"
+ knife("list -z -Rfp /environments").should_succeed ""
+ end
- it "knife delete -z /nodes/x.json works" do
- knife("delete -z /nodes/x.json").should_succeed "Deleted /nodes/x.json\n"
- knife("list -z -Rfp /nodes").should_succeed ""
- end
+ it "knife delete -z /nodes/x.json works" do
+ knife("delete -z /nodes/x.json").should_succeed "Deleted /nodes/x.json\n"
+ knife("list -z -Rfp /nodes").should_succeed ""
+ end
- it "knife delete -z /roles/x.json works" do
- knife("delete -z /roles/x.json").should_succeed "Deleted /roles/x.json\n"
- knife("list -z -Rfp /roles").should_succeed ""
- end
+ it "knife delete -z /roles/x.json works" do
+ knife("delete -z /roles/x.json").should_succeed "Deleted /roles/x.json\n"
+ knife("list -z -Rfp /roles").should_succeed ""
+ end
- it "knife delete -z /users/x.json works" do
- knife("delete -z /users/x.json").should_succeed "Deleted /users/x.json\n"
- knife("list -z -Rfp /users").should_succeed ""
end
- end
- context "GET /TYPE/NAME" do
- it "knife show -z /clients/x.json works" do
- knife("show -z /clients/x.json").should_succeed( /"x"/ )
- end
+ context "GET /TYPE/NAME" do
+ it "knife show -z /clients/x.json works" do
+ knife("show -z /clients/x.json").should_succeed( /"x"/ )
+ end
- it "knife show -z /cookbooks/x/metadata.rb works" do
- knife("show -z /cookbooks/x/metadata.rb").should_succeed "/cookbooks/x/metadata.rb:\n#{cookbook_x_100_metadata_rb}\n"
- end
+ it "knife show -z /cookbooks/x/metadata.rb works" do
+ knife("show -z /cookbooks/x/metadata.rb").should_succeed "/cookbooks/x/metadata.rb:\n#{cookbook_x_100_metadata_rb}\n"
+ end
- it "knife show -z /data_bags/x/y.json works" do
- knife("show -z /data_bags/x/y.json").should_succeed( /"y"/ )
- end
+ it "knife show -z /data_bags/x/y.json works" do
+ knife("show -z /data_bags/x/y.json").should_succeed( /"y"/ )
+ end
- it "knife show -z /environments/x.json works" do
- knife("show -z /environments/x.json").should_succeed( /"x"/ )
- end
+ it "knife show -z /environments/x.json works" do
+ knife("show -z /environments/x.json").should_succeed( /"x"/ )
+ end
- it "knife show -z /nodes/x.json works" do
- knife("show -z /nodes/x.json").should_succeed( /"x"/ )
- end
+ it "knife show -z /nodes/x.json works" do
+ knife("show -z /nodes/x.json").should_succeed( /"x"/ )
+ end
- it "knife show -z /roles/x.json works" do
- knife("show -z /roles/x.json").should_succeed( /"x"/ )
- end
+ it "knife show -z /roles/x.json works" do
+ knife("show -z /roles/x.json").should_succeed( /"x"/ )
+ end
- it "knife show -z /users/x.json works" do
- knife("show -z /users/x.json").should_succeed( /"x"/ )
end
- end
- context "PUT /TYPE/NAME" do
- before do
- file "empty.json", {}
- file "dummynode.json", { "name" => "x", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
- file "rolestuff.json", '{"description":"hi there","name":"x"}'
- file "cookbooks_to_upload/x/metadata.rb", cookbook_x_100_metadata_rb
- end
+ context "PUT /TYPE/NAME" do
+ before do
+ file "empty.json", {}
+ file "dummynode.json", { "name" => "x", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
+ file "rolestuff.json", '{"description":"hi there","name":"x"}'
+ file "cookbooks_to_upload/x/metadata.rb", cookbook_x_100_metadata_rb
+ end
- it "knife raw -z -i empty.json -m PUT /clients/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_succeed( /"x"/ )
- knife("list --local /clients").should_succeed "/clients/x.json\n"
- end
+ it "knife raw -z -i empty.json -m PUT /clients/x" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_succeed( /"x"/ )
+ knife("list --local /clients").should_succeed "/clients/x.json\n"
+ end
- it "knife cookbook upload works" do
- knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} x").should_succeed :stderr => <<EOM
+ it "knife cookbook upload works" do
+ knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} x").should_succeed :stderr => <<EOM
Uploading x [1.0.0]
Uploaded 1 cookbook.
EOM
- knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/x/\n/cookbooks/x/metadata.rb\n"
- end
-
- it "knife raw -z -i empty.json -m PUT /data/x/y" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /data/x/y").should_succeed( /"y"/ )
- knife("list --local -Rfp /data_bags").should_succeed "/data_bags/x/\n/data_bags/x/y.json\n"
- end
-
- it "knife raw -z -i empty.json -m PUT /environments/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /environments/x").should_succeed( /"x"/ )
- knife("list --local /environments").should_succeed "/environments/x.json\n"
- end
-
- it "knife raw -z -i dummynode.json -m PUT /nodes/x" do
- knife("raw -z -i #{path_to('dummynode.json')} -m PUT /nodes/x").should_succeed( /"x"/ )
- knife("list --local /nodes").should_succeed "/nodes/x.json\n"
- knife("show -z /nodes/x.json --verbose").should_succeed /"bar"/
- end
-
- it "knife raw -z -i empty.json -m PUT /roles/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /roles/x").should_succeed( /"x"/ )
- knife("list --local /roles").should_succeed "/roles/x.json\n"
- end
-
- it "knife raw -z -i empty.json -m PUT /users/x" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /users/x").should_succeed( /"x"/ )
- knife("list --local /users").should_succeed "/users/x.json\n"
- end
-
- it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
- knife("raw -z -i #{path_to('rolestuff.json')} -m PUT /roles/x").should_succeed( /"x"/ )
- expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
+ knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/x/\n/cookbooks/x/metadata.rb\n"
+ end
+
+ it "knife raw -z -i empty.json -m PUT /data/x/y" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /data/x/y").should_succeed( /"y"/ )
+ knife("list --local -Rfp /data_bags").should_succeed "/data_bags/x/\n/data_bags/x/y.json\n"
+ end
+
+ it "knife raw -z -i empty.json -m PUT /environments/x" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /environments/x").should_succeed( /"x"/ )
+ knife("list --local /environments").should_succeed "/environments/x.json\n"
+ end
+
+ it "knife raw -z -i dummynode.json -m PUT /nodes/x" do
+ knife("raw -z -i #{path_to('dummynode.json')} -m PUT /nodes/x").should_succeed( /"x"/ )
+ knife("list --local /nodes").should_succeed "/nodes/x.json\n"
+ knife("show -z /nodes/x.json --verbose").should_succeed(/"bar"/)
+ end
+
+ it "knife raw -z -i empty.json -m PUT /roles/x" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /roles/x").should_succeed( /"x"/ )
+ knife("list --local /roles").should_succeed "/roles/x.json\n"
+ end
+
+ it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
+ knife("raw -z -i #{path_to('rolestuff.json')} -m PUT /roles/x").should_succeed( /"x"/ )
+ expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
{
"name": "x",
"description": "hi there"
}
EOM
+ end
end
end
- end
- when_the_repository "is empty" do
- context "POST /TYPE/NAME" do
- before do
- file "empty.json", { "name" => "z" }
- file "dummynode.json", { "name" => "z", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
- file "empty_x.json", { "name" => "x" }
- file "empty_id.json", { "id" => "z" }
- file "rolestuff.json", '{"description":"hi there","name":"x"}'
- file "cookbooks_to_upload/z/metadata.rb", cookbook_z_100_metadata_rb
- end
-
- it "knife raw -z -i empty.json -m POST /clients" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /clients").should_succeed( /uri/ )
- knife("list --local /clients").should_succeed "/clients/z.json\n"
- end
-
- it "knife cookbook upload works" do
- knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} z").should_succeed :stderr => <<EOM
+ when_the_repository "is empty" do
+ context "POST /TYPE/NAME" do
+ before do
+ file "empty.json", { "name" => "z" }
+ file "dummynode.json", { "name" => "z", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
+ file "empty_x.json", { "name" => "x" }
+ file "empty_id.json", { "id" => "z" }
+ file "rolestuff.json", '{"description":"hi there","name":"x"}'
+ file "cookbooks_to_upload/z/metadata.rb", cookbook_z_100_metadata_rb
+ end
+
+ it "knife raw -z -i empty.json -m POST /clients" do
+ knife("raw -z -i #{path_to('empty.json')} -m POST /clients").should_succeed( /uri/ )
+ knife("list --local /clients").should_succeed "/clients/z.json\n"
+ end
+
+ it "knife cookbook upload works" do
+ knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} z").should_succeed :stderr => <<EOM
Uploading z [1.0.0]
Uploaded 1 cookbook.
EOM
- knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/z/\n/cookbooks/z/metadata.rb\n"
- end
-
- it "knife raw -z -i empty.json -m POST /data" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /data").should_succeed( /uri/ )
- knife("list --local -Rfp /data_bags").should_succeed "/data_bags/z/\n"
- end
-
- it "knife raw -z -i empty.json -m POST /data/x" do
- knife("raw -z -i #{path_to('empty_x.json')} -m POST /data").should_succeed( /uri/ )
- knife("raw -z -i #{path_to('empty_id.json')} -m POST /data/x").should_succeed( /"z"/ )
- knife("list --local -Rfp /data_bags").should_succeed "/data_bags/x/\n/data_bags/x/z.json\n"
- end
-
- it "knife raw -z -i empty.json -m POST /environments" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /environments").should_succeed( /uri/ )
- knife("list --local /environments").should_succeed "/environments/z.json\n"
- end
-
- it "knife raw -z -i dummynode.json -m POST /nodes" do
- knife("raw -z -i #{path_to('dummynode.json')} -m POST /nodes").should_succeed( /uri/ )
- knife("list --local /nodes").should_succeed "/nodes/z.json\n"
- knife("show -z /nodes/z.json").should_succeed /"bar"/
- end
-
- it "knife raw -z -i empty.json -m POST /roles" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /roles").should_succeed( /uri/ )
- knife("list --local /roles").should_succeed "/roles/z.json\n"
- end
-
- it "knife raw -z -i empty.json -m POST /users" do
- knife("raw -z -i #{path_to('empty.json')} -m POST /users").should_succeed( /uri/ )
- knife("list --local /users").should_succeed "/users/z.json\n"
- end
-
- it "After knife raw -z -i rolestuff.json -m POST /roles, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
- knife("raw -z -i #{path_to('rolestuff.json')} -m POST /roles").should_succeed( /uri/ )
- expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
+ knife("list --local -Rfp /cookbooks").should_succeed "/cookbooks/z/\n/cookbooks/z/metadata.rb\n"
+ end
+
+ it "knife raw -z -i empty.json -m POST /data" do
+ knife("raw -z -i #{path_to('empty.json')} -m POST /data").should_succeed( /uri/ )
+ knife("list --local -Rfp /data_bags").should_succeed "/data_bags/z/\n"
+ end
+
+ it "knife raw -z -i empty.json -m POST /data/x" do
+ knife("raw -z -i #{path_to('empty_x.json')} -m POST /data").should_succeed( /uri/ )
+ knife("raw -z -i #{path_to('empty_id.json')} -m POST /data/x").should_succeed( /"z"/ )
+ knife("list --local -Rfp /data_bags").should_succeed "/data_bags/x/\n/data_bags/x/z.json\n"
+ end
+
+ it "knife raw -z -i empty.json -m POST /environments" do
+ knife("raw -z -i #{path_to('empty.json')} -m POST /environments").should_succeed( /uri/ )
+ knife("list --local /environments").should_succeed "/environments/z.json\n"
+ end
+
+ it "knife raw -z -i dummynode.json -m POST /nodes" do
+ knife("raw -z -i #{path_to('dummynode.json')} -m POST /nodes").should_succeed( /uri/ )
+ knife("list --local /nodes").should_succeed "/nodes/z.json\n"
+ knife("show -z /nodes/z.json").should_succeed(/"bar"/)
+ end
+
+ it "knife raw -z -i empty.json -m POST /roles" do
+ knife("raw -z -i #{path_to('empty.json')} -m POST /roles").should_succeed( /uri/ )
+ knife("list --local /roles").should_succeed "/roles/z.json\n"
+ end
+
+ it "After knife raw -z -i rolestuff.json -m POST /roles, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
+ knife("raw -z -i #{path_to('rolestuff.json')} -m POST /roles").should_succeed( /uri/ )
+ expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
{
"name": "x",
"description": "hi there"
}
EOM
+ end
end
- end
- it "knife list -z -R returns nothing" do
- knife("list -z -Rfp /").should_succeed <<EOM
+ it "knife list -z -R returns nothing" do
+ knife("list -z -Rfp /").should_succeed <<EOM
+/acls/
+/acls/clients/
+/acls/containers/
+/acls/cookbooks/
+/acls/data_bags/
+/acls/environments/
+/acls/groups/
+/acls/nodes/
+/acls/organization.json
+/acls/roles/
/clients/
+/containers/
+/cookbook_artifacts/
/cookbooks/
/data_bags/
/environments/
+/groups/
+/invitations.json
+/members.json
/nodes/
+/org.json
+/policies/
+/policy_groups/
/roles/
-/users/
EOM
- end
-
- context "DELETE /TYPE/NAME" do
- it "knife delete -z /clients/x.json fails with an error" do
- knife("delete -z /clients/x.json").should_fail "ERROR: /clients/x.json: No such file or directory\n"
end
- it "knife delete -z -r /cookbooks/x fails with an error" do
- knife("delete -z -r /cookbooks/x").should_fail "ERROR: /cookbooks/x: No such file or directory\n"
- end
+ context "DELETE /TYPE/NAME" do
+ it "knife delete -z /clients/x.json fails with an error" do
+ knife("delete -z /clients/x.json").should_fail "ERROR: /clients/x.json: No such file or directory\n"
+ end
- it "knife delete -z -r /data_bags/x fails with an error" do
- knife("delete -z -r /data_bags/x").should_fail "ERROR: /data_bags/x: No such file or directory\n"
- end
+ it "knife delete -z -r /cookbooks/x fails with an error" do
+ knife("delete -z -r /cookbooks/x").should_fail "ERROR: /cookbooks/x: No such file or directory\n"
+ end
- it "knife delete -z /data_bags/x/y.json fails with an error" do
- knife("delete -z /data_bags/x/y.json").should_fail "ERROR: /data_bags/x/y.json: No such file or directory\n"
- end
+ it "knife delete -z -r /data_bags/x fails with an error" do
+ knife("delete -z -r /data_bags/x").should_fail "ERROR: /data_bags/x: No such file or directory\n"
+ end
- it "knife delete -z /environments/x.json fails with an error" do
- knife("delete -z /environments/x.json").should_fail "ERROR: /environments/x.json: No such file or directory\n"
- end
+ it "knife delete -z /data_bags/x/y.json fails with an error" do
+ knife("delete -z /data_bags/x/y.json").should_fail "ERROR: /data_bags/x/y.json: No such file or directory\n"
+ end
+
+ it "knife delete -z /environments/x.json fails with an error" do
+ knife("delete -z /environments/x.json").should_fail "ERROR: /environments/x.json: No such file or directory\n"
+ end
+
+ it "knife delete -z /nodes/x.json fails with an error" do
+ knife("delete -z /nodes/x.json").should_fail "ERROR: /nodes/x.json: No such file or directory\n"
+ end
+
+ it "knife delete -z /roles/x.json fails with an error" do
+ knife("delete -z /roles/x.json").should_fail "ERROR: /roles/x.json: No such file or directory\n"
+ end
- it "knife delete -z /nodes/x.json fails with an error" do
- knife("delete -z /nodes/x.json").should_fail "ERROR: /nodes/x.json: No such file or directory\n"
end
- it "knife delete -z /roles/x.json fails with an error" do
- knife("delete -z /roles/x.json").should_fail "ERROR: /roles/x.json: No such file or directory\n"
+ context "GET /TYPE/NAME" do
+ it "knife show -z /clients/x.json fails with an error" do
+ knife("show -z /clients/x.json").should_fail "ERROR: /clients/x.json: No such file or directory\n"
+ end
+
+ it "knife show -z /cookbooks/x/metadata.rb fails with an error" do
+ knife("show -z /cookbooks/x/metadata.rb").should_fail "ERROR: /cookbooks/x/metadata.rb: No such file or directory\n"
+ end
+
+ it "knife show -z /data_bags/x/y.json fails with an error" do
+ knife("show -z /data_bags/x/y.json").should_fail "ERROR: /data_bags/x/y.json: No such file or directory\n"
+ end
+
+ it "knife show -z /environments/x.json fails with an error" do
+ knife("show -z /environments/x.json").should_fail "ERROR: /environments/x.json: No such file or directory\n"
+ end
+
+ it "knife show -z /nodes/x.json fails with an error" do
+ knife("show -z /nodes/x.json").should_fail "ERROR: /nodes/x.json: No such file or directory\n"
+ end
+
+ it "knife show -z /roles/x.json fails with an error" do
+ knife("show -z /roles/x.json").should_fail "ERROR: /roles/x.json: No such file or directory\n"
+ end
+
end
- it "knife delete -z /users/x.json fails with an error" do
- knife("delete -z /users/x.json").should_fail "ERROR: /users/x.json: No such file or directory\n"
+ context "PUT /TYPE/NAME" do
+ before do
+ file "empty.json", {}
+ end
+
+ it "knife raw -z -i empty.json -m PUT /clients/x fails with 404" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_fail( /404/ )
+ end
+
+ it "knife raw -z -i empty.json -m PUT /data/x/y fails with 404" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /data/x/y").should_fail( /404/ )
+ end
+
+ it "knife raw -z -i empty.json -m PUT /environments/x fails with 404" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /environments/x").should_fail( /404/ )
+ end
+
+ it "knife raw -z -i empty.json -m PUT /nodes/x fails with 404" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /nodes/x").should_fail( /404/ )
+ end
+
+ it "knife raw -z -i empty.json -m PUT /roles/x fails with 404" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /roles/x").should_fail( /404/ )
+ end
+
end
end
+ end
- context "GET /TYPE/NAME" do
- it "knife show -z /clients/x.json fails with an error" do
- knife("show -z /clients/x.json").should_fail "ERROR: /clients/x.json: No such file or directory\n"
- end
+ # We have to configure Zero for Chef 11 mode in order to test users because:
+ # 1. local mode overrides your `chef_server_url` to something like "http://localhost:PORT"
+ # 2. single org mode maps requests like "https://localhost:PORT/users" so
+ # they're functionally equivalent to "https://localhost:PORT/organizations/DEFAULT/users"
+ # 3. Users are global objects in Chef 12, and should be accessed at URLs like
+ # "https://localhost:PORT/users" (there is an org-specific users endpoint,
+ # but it's for listing users in an org, not for managing users).
+ # 4. Therefore you can't hit the _real_ users endpoint in local mode when
+ # configured for Chef Server 12 mode.
+ #
+ # Because of this, we have to configure Zero for Chef 11 OSC mode in order to
+ # test the users part of the data store with local mode.
+ describe "with repo mode 'everything'" do
+ before do
+ Chef::Config.repo_mode = "everything"
+ Chef::Config.chef_zero.osc_compat = true
+ end
- it "knife show -z /cookbooks/x/metadata.rb fails with an error" do
- knife("show -z /cookbooks/x/metadata.rb").should_fail "ERROR: /cookbooks/x/metadata.rb: No such file or directory\n"
+ when_the_repository "has one of each thing" do
+ before do
+ file "clients/x.json", {}
+ file "cookbooks/x/metadata.rb", cookbook_x_100_metadata_rb
+ file "data_bags/x/y.json", {}
+ file "environments/x.json", {}
+ file "nodes/x.json", {}
+ file "roles/x.json", {}
+ file "users/x.json", {}
+ end
+
+ context "GET /TYPE" do
+ it "knife list -z -R returns everything" do
+ knife("list -z -Rfp /").should_succeed <<EOM
+/clients/
+/clients/x.json
+/cookbooks/
+/cookbooks/x/
+/cookbooks/x/metadata.rb
+/data_bags/
+/data_bags/x/
+/data_bags/x/y.json
+/environments/
+/environments/x.json
+/nodes/
+/nodes/x.json
+/roles/
+/roles/x.json
+/users/
+/users/x.json
+EOM
+ end
end
- it "knife show -z /data_bags/x/y.json fails with an error" do
- knife("show -z /data_bags/x/y.json").should_fail "ERROR: /data_bags/x/y.json: No such file or directory\n"
+ context "DELETE /TYPE/NAME" do
+ it "knife delete -z /users/x.json works" do
+ knife("delete -z /users/x.json").should_succeed "Deleted /users/x.json\n"
+ knife("list -z -Rfp /users").should_succeed ""
+ end
end
- it "knife show -z /environments/x.json fails with an error" do
- knife("show -z /environments/x.json").should_fail "ERROR: /environments/x.json: No such file or directory\n"
+ context "GET /TYPE/NAME" do
+ it "knife show -z /users/x.json works" do
+ knife("show -z /users/x.json").should_succeed( /"x"/ )
+ end
end
- it "knife show -z /nodes/x.json fails with an error" do
- knife("show -z /nodes/x.json").should_fail "ERROR: /nodes/x.json: No such file or directory\n"
- end
+ context "PUT /TYPE/NAME" do
+ before do
+ file "empty.json", {}
+ file "dummynode.json", { "name" => "x", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
+ file "rolestuff.json", '{"description":"hi there","name":"x"}'
+ file "cookbooks_to_upload/x/metadata.rb", cookbook_x_100_metadata_rb
+ end
- it "knife show -z /roles/x.json fails with an error" do
- knife("show -z /roles/x.json").should_fail "ERROR: /roles/x.json: No such file or directory\n"
- end
+ it "knife raw -z -i empty.json -m PUT /users/x" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /users/x").should_succeed( /"x"/ )
+ knife("list --local /users").should_succeed "/users/x.json\n"
+ end
- it "knife show -z /users/x.json fails with an error" do
- knife("show -z /users/x.json").should_fail "ERROR: /users/x.json: No such file or directory\n"
+ it "After knife raw -z -i rolestuff.json -m PUT /roles/x, the output is pretty", :skip => (RUBY_VERSION < "1.9") do
+ knife("raw -z -i #{path_to('rolestuff.json')} -m PUT /roles/x").should_succeed( /"x"/ )
+ expect(IO.read(path_to("roles/x.json"))).to eq <<EOM.strip
+{
+ "name": "x",
+ "description": "hi there"
+}
+EOM
+ end
end
end
- context "PUT /TYPE/NAME" do
- before do
- file "empty.json", {}
- end
+ when_the_repository "is empty" do
+ context "POST /TYPE/NAME" do
+ before do
+ file "empty.json", { "name" => "z" }
+ file "dummynode.json", { "name" => "z", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
+ file "empty_x.json", { "name" => "x" }
+ file "empty_id.json", { "id" => "z" }
+ file "rolestuff.json", '{"description":"hi there","name":"x"}'
+ file "cookbooks_to_upload/z/metadata.rb", cookbook_z_100_metadata_rb
+ end
- it "knife raw -z -i empty.json -m PUT /clients/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_fail( /404/ )
+ it "knife raw -z -i empty.json -m POST /users" do
+ knife("raw -z -i #{path_to('empty.json')} -m POST /users").should_succeed( /uri/ )
+ knife("list --local /users").should_succeed "/users/z.json\n"
+ end
end
- it "knife raw -z -i empty.json -m PUT /data/x/y fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /data/x/y").should_fail( /404/ )
+ it "knife list -z -R returns nothing" do
+ knife("list -z -Rfp /").should_succeed <<EOM
+/clients/
+/cookbooks/
+/data_bags/
+/environments/
+/nodes/
+/roles/
+/users/
+EOM
end
- it "knife raw -z -i empty.json -m PUT /environments/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /environments/x").should_fail( /404/ )
+ context "DELETE /TYPE/NAME" do
+ it "knife delete -z /users/x.json fails with an error" do
+ knife("delete -z /users/x.json").should_fail "ERROR: /users/x.json: No such file or directory\n"
+ end
end
- it "knife raw -z -i empty.json -m PUT /nodes/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /nodes/x").should_fail( /404/ )
+ context "GET /TYPE/NAME" do
+ it "knife show -z /users/x.json fails with an error" do
+ knife("show -z /users/x.json").should_fail "ERROR: /users/x.json: No such file or directory\n"
+ end
end
- it "knife raw -z -i empty.json -m PUT /roles/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /roles/x").should_fail( /404/ )
- end
+ context "PUT /TYPE/NAME" do
+ before do
+ file "empty.json", {}
+ end
- it "knife raw -z -i empty.json -m PUT /users/x fails with 404" do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /users/x").should_fail( /404/ )
+ it "knife raw -z -i empty.json -m PUT /users/x fails with 404" do
+ knife("raw -z -i #{path_to('empty.json')} -m PUT /users/x").should_fail( /404/ )
+ end
end
end
end
diff --git a/spec/unit/provider/apt_update_spec.rb b/spec/unit/provider/apt_update_spec.rb
new file mode 100644
index 0000000000..b72f7d9a76
--- /dev/null
+++ b/spec/unit/provider/apt_update_spec.rb
@@ -0,0 +1,113 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 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::Provider::AptUpdate do
+ let(:new_resource) { Chef::Resource::AptUpdate.new("update") }
+
+ let(:config_dir) { Dir.mktmpdir("apt_update_apt_conf_d") }
+ let(:config_file) { File.join(config_dir, "15update-stamp") }
+ let(:stamp_dir) { Dir.mktmpdir("apt_update_periodic") }
+
+ before do
+ stub_const("Chef::Provider::AptUpdate::APT_CONF_DIR", config_dir)
+ stub_const("Chef::Provider::AptUpdate::STAMP_DIR", stamp_dir)
+ end
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::AptUpdate.new(new_resource, run_context)
+ end
+
+ it "responds to load_current_resource" do
+ expect(provider).to respond_to(:load_current_resource)
+ end
+
+ context "when the apt config directory does not exist" do
+ before do
+ FileUtils.rmdir config_dir
+ expect(File.exist?(config_dir)).to be_falsey
+ allow(provider).to receive(:shell_out!).with("apt-get -q update")
+ end
+
+ it "should create the directory" do
+ provider.run_action(:update)
+ expect(File.exist?(config_dir)).to be_truthy
+ expect(File.directory?(config_dir)).to be_truthy
+ end
+
+ it "should create the config file" do
+ provider.run_action(:update)
+ expect(File.exist?(config_file)).to be_truthy
+ expect(File.read(config_file)).to match(/^APT::Update.*#{stamp_dir}/)
+ end
+ end
+
+ describe "#action_update" do
+ it "should update the apt cache" do
+ provider.load_current_resource
+ expect(provider).to receive(:shell_out!).with("apt-get -q update").and_return(double)
+ provider.run_action(:update)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ describe "#action_periodic" do
+ before do
+ allow(File).to receive(:exist?)
+ expect(File).to receive(:exist?).with("#{stamp_dir}/update-success-stamp").and_return(true)
+ end
+
+ it "should run if the time stamp is old" do
+ expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now - 86_500)
+ expect(provider).to receive(:shell_out!).with("apt-get -q update")
+ provider.run_action(:periodic)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should not run if the time stamp is new" do
+ expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now)
+ expect(provider).to_not receive(:shell_out!).with("apt-get -q update")
+ provider.run_action(:periodic)
+ expect(new_resource).to_not be_updated_by_last_action
+ end
+
+ context "with a different frequency" do
+ before do
+ new_resource.frequency(400)
+ end
+
+ it "should run if the time stamp is old" do
+ expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now - 500)
+ expect(provider).to receive(:shell_out!).with("apt-get -q update")
+ provider.run_action(:periodic)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should not run if the time stamp is new" do
+ expect(File).to receive(:mtime).with("#{stamp_dir}/update-success-stamp").and_return(Time.now - 300)
+ expect(provider).to_not receive(:shell_out!).with("apt-get -q update")
+ provider.run_action(:periodic)
+ expect(new_resource).to_not be_updated_by_last_action
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/apt_update_spec.rb b/spec/unit/resource/apt_update_spec.rb
new file mode 100644
index 0000000000..8015cb03b3
--- /dev/null
+++ b/spec/unit/resource/apt_update_spec.rb
@@ -0,0 +1,38 @@
+#
+# Author:: Thom May (<thom@chef.io>)
+# Copyright:: Copyright (c) 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::AptUpdate do
+
+ let(:resource) { Chef::Resource::AptUpdate.new("update") }
+
+ it "should create a new Chef::Resource::AptUpdate" do
+ expect(resource).to be_a_kind_of(Chef::Resource)
+ expect(resource).to be_a_kind_of(Chef::Resource::AptUpdate)
+ end
+
+ it "the default frequency should be 1 day" do
+ expect(resource.frequency).to eql(86_400)
+ end
+
+ it "the frequency should accept integers" do
+ resource.frequency(400)
+ expect(resource.frequency).to eql(400)
+ end
+end