spec/integration/upload_spec.rb in knife-18.0.185 vs spec/integration/upload_spec.rb in knife-18.1.0
- old
+ new
@@ -1,1616 +1,1616 @@
-#
-# Author:: John Keiser (<jkeiser@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 "knife_spec_helper"
-require "support/shared/integration/integration_helper"
-require "chef/knife/upload"
-require "chef/knife/diff"
-require "chef/knife/raw"
-require "chef/json_compat"
-
-describe "knife upload", :workstation do
- include IntegrationSupport
- include KnifeSupport
-
- context "without versioned cookbooks" do
-
- when_the_chef_server "has one of each thing" do
-
- before do
- client "x", {}
- cookbook "x", "1.0.0"
- data_bag "x", { "y" => {} }
- environment "x", {}
- node "x", {}
- role "x", {}
- user "x", {}
- end
-
- when_the_repository "has only top-level directories" do
- before do
- directory "clients"
- directory "cookbooks"
- directory "data_bags"
- directory "environments"
- directory "nodes"
- directory "roles"
- directory "users"
- end
-
- it "knife upload does nothing" do
- knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/clients/chef-validator.json
- D\t/clients/chef-webui.json
- D\t/clients/x.json
- D\t/cookbooks/x
- D\t/data_bags/x
- D\t/environments/_default.json
- D\t/environments/x.json
- D\t/nodes/x.json
- D\t/roles/x.json
- D\t/users/admin.json
- D\t/users/x.json
- EOM
- end
-
- it "knife upload --purge deletes everything" do
- knife("upload --purge /").should_succeed(<<~EOM, stderr: "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
- Deleted extra entry /clients/chef-validator.json (purge is on)
- Deleted extra entry /clients/chef-webui.json (purge is on)
- Deleted extra entry /clients/x.json (purge is on)
- Deleted extra entry /cookbooks/x (purge is on)
- Deleted extra entry /data_bags/x (purge is on)
- Deleted extra entry /environments/x.json (purge is on)
- Deleted extra entry /nodes/x.json (purge is on)
- Deleted extra entry /roles/x.json (purge is on)
- Deleted extra entry /users/admin.json (purge is on)
- Deleted extra entry /users/x.json (purge is on)
- EOM
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/environments/_default.json
- EOM
- end
- end
-
- when_the_repository "has an identical copy of each thing" do
-
- before do
- file "clients/chef-validator.json", { "validator" => true, "public_key" => ChefZero::PUBLIC_KEY }
- file "clients/chef-webui.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
- file "clients/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- file "data_bags/x/y.json", {}
- file "environments/_default.json", { "description" => "The default Chef environment" }
- file "environments/x.json", {}
- file "nodes/x.json", { "normal" => { "tags" => [] } }
- file "roles/x.json", {}
- file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
- file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
- end
-
- it "knife upload makes no changes" do
- knife("upload /cookbooks/x").should_succeed ""
- knife("diff --name-status /").should_succeed ""
- end
-
- it "knife upload --purge makes no changes" do
- knife("upload --purge /").should_succeed ""
- knife("diff --name-status /").should_succeed ""
- end
-
- context "except the role file" do
- before do
- file "roles/x.json", { "description" => "blarghle" }
- end
-
- it "knife upload changes the role" do
- knife("upload /").should_succeed "Updated /roles/x.json\n"
- knife("diff --name-status /").should_succeed ""
- end
- it "knife upload --no-diff does not change the role" do
- knife("upload --no-diff /").should_succeed ""
- knife("diff --name-status /").should_succeed "M\t/roles/x.json\n"
- end
- end
-
- context "except the role file is textually different, but not ACTUALLY different" do
- before do
- file "roles/x.json", <<~EOM
- {
- "chef_type": "role",
- "default_attributes": {
- },
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "description": "",
- "override_attributes": {
- },
- "run_list": [
-
- ]
- }
- EOM
- end
-
- it "knife upload / does not change anything" do
- knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed ""
- end
- end
-
- context "the role is in ruby" do
- before do
- file "roles/x.rb", <<~EOM
- name "x"
- description "blargle"
- EOM
- end
-
- it "knife upload changes the role" do
- knife("upload /").should_succeed "Updated /roles/x.json\n"
- knife("diff --name-status /").should_succeed ""
- end
-
- it "knife upload --no-diff does not change the role" do
- knife("upload --no-diff /").should_succeed ""
- knife("diff --name-status /").should_succeed "M\t/roles/x.rb\n"
- end
- end
-
- context "when cookbook metadata has a self-dependency" do
- before do
- file "cookbooks/x/metadata.rb", "name 'x'; version '1.0.0'; depends 'x'"
- end
-
- it "fails with RuntimeError" do
- expect { knife("upload /cookbooks") }.to raise_error RuntimeError, /Cookbook depends on itself/
- end
- end
-
- context "as well as one extra copy of each thing" do
- before do
- file "clients/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
- file "cookbooks/x/blah.rb", ""
- file "cookbooks/y/metadata.rb", cb_metadata("y", "1.0.0")
- file "data_bags/x/z.json", {}
- file "data_bags/y/zz.json", {}
- file "environments/y.json", {}
- file "nodes/y.json", {}
- file "roles/y.json", {}
- file "users/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
- end
-
- it "knife upload adds the new files" do
- knife("upload /").should_succeed <<~EOM
- Created /clients/y.json
- Updated /cookbooks/x
- Created /cookbooks/y
- Created /data_bags/x/z.json
- Created /data_bags/y
- Created /data_bags/y/zz.json
- Created /environments/y.json
- Created /nodes/y.json
- Created /roles/y.json
- Created /users/y.json
- EOM
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- D\t/cookbooks/y/metadata.json
- EOM
- end
-
- it "knife upload --no-diff adds the new files" do
- knife("upload --no-diff /").should_succeed <<~EOM
- Created /clients/y.json
- Updated /cookbooks/x
- Created /cookbooks/y
- Created /data_bags/x/z.json
- Created /data_bags/y
- Created /data_bags/y/zz.json
- Created /environments/y.json
- Created /nodes/y.json
- Created /roles/y.json
- Created /users/y.json
- EOM
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- D\t/cookbooks/y/metadata.json
- EOM
- end
- end
- end
-
- when_the_repository "is empty" do
- it "knife upload does nothing" do
- knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/clients
- D\t/cookbooks
- D\t/data_bags
- D\t/environments
- D\t/nodes
- D\t/roles
- D\t/users
- EOM
- end
-
- it "knife upload --purge deletes nothing" do
- knife("upload --purge /").should_fail <<~EOM
- ERROR: /clients cannot be deleted.
- ERROR: /cookbooks cannot be deleted.
- ERROR: /data_bags cannot be deleted.
- ERROR: /environments cannot be deleted.
- ERROR: /nodes cannot be deleted.
- ERROR: /roles cannot be deleted.
- ERROR: /users cannot be deleted.
- EOM
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/clients
- D\t/cookbooks
- D\t/data_bags
- D\t/environments
- D\t/nodes
- D\t/roles
- D\t/users
- EOM
- end
-
- context "when current directory is top level" do
- before do
- cwd "."
- end
-
- it "knife upload with no parameters reports an error" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
- end
- end
- end
- end
-
- when_the_chef_server "is empty" do
- when_the_repository "has a data bag item" do
-
- before do
- file "data_bags/x/y.json", { "foo" => "bar" }
- end
-
- it "knife upload of the data bag uploads only the values in the data bag item and no other" do
- knife("upload /data_bags/x/y.json").should_succeed <<~EOM
- Created /data_bags/x
- Created /data_bags/x/y.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- EOM
- expect(Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, create_additions: false).keys.sort).to eq(%w{foo id})
- end
-
- it "knife upload /data_bags/x /data_bags/x/y.json uploads x once" do
- knife("upload /data_bags/x /data_bags/x/y.json").should_succeed <<~EOM
- Created /data_bags/x
- Created /data_bags/x/y.json
- EOM
- end
- end
-
- when_the_repository "has a data bag item with keys chef_type and data_bag" do
-
- before do
- file "data_bags/x/y.json", { "chef_type" => "aaa", "data_bag" => "bbb" }
- end
-
- it "upload preserves chef_type and data_bag" do
- knife("upload /data_bags/x/y.json").should_succeed <<~EOM
- Created /data_bags/x
- Created /data_bags/x/y.json
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- result = Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, create_additions: false)
- expect(result.keys.sort).to eq(%w{chef_type data_bag id})
- expect(result["chef_type"]).to eq("aaa")
- expect(result["data_bag"]).to eq("bbb")
- end
- end
-
- # Test upload of an item when the other end doesn't even have the container
- when_the_repository "has two data bag items" do
- before do
- file "data_bags/x/y.json", {}
- file "data_bags/x/z.json", {}
- end
- it "knife upload of one data bag item itself succeeds" do
- knife("upload /data_bags/x/y.json").should_succeed <<~EOM
- Created /data_bags/x
- Created /data_bags/x/y.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- A\t/data_bags/x/z.json
- EOM
- end
- end
- end
-
- when_the_chef_server "has three data bag items" do
-
- before do
- data_bag "x", { "deleted" => {}, "modified" => {}, "unmodified" => {} }
- end
-
- when_the_repository "has a modified, unmodified, added and deleted data bag item" do
- before do
- file "data_bags/x/added.json", {}
- file "data_bags/x/modified.json", { "foo" => "bar" }
- file "data_bags/x/unmodified.json", {}
- end
-
- it "knife upload of the modified file succeeds" do
- knife("upload /data_bags/x/modified.json").should_succeed <<~EOM
- Updated /data_bags/x/modified.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload of the unmodified file does nothing" do
- knife("upload /data_bags/x/unmodified.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- M\t/data_bags/x/modified.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload of the added file succeeds" do
- knife("upload /data_bags/x/added.json").should_succeed <<~EOM
- Created /data_bags/x/added.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- M\t/data_bags/x/modified.json
- EOM
- end
- it "knife upload of the deleted file does nothing" do
- knife("upload /data_bags/x/deleted.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- M\t/data_bags/x/modified.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload --purge of the deleted file deletes it" do
- knife("upload --purge /data_bags/x/deleted.json").should_succeed <<~EOM
- Deleted extra entry /data_bags/x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- M\t/data_bags/x/modified.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload of the entire data bag uploads everything" do
- knife("upload /data_bags/x").should_succeed <<~EOM
- Created /data_bags/x/added.json
- Updated /data_bags/x/modified.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- EOM
- end
- it "knife upload --purge of the entire data bag uploads everything" do
- knife("upload --purge /data_bags/x").should_succeed <<~EOM
- Created /data_bags/x/added.json
- Updated /data_bags/x/modified.json
- Deleted extra entry /data_bags/x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- end
- context "when cwd is the /data_bags directory" do
-
- before do
- cwd "data_bags"
- end
-
- it "knife upload fails" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
- end
-
- it "knife upload --purge . uploads everything" do
- knife("upload --purge .").should_succeed <<~EOM
- Created x/added.json
- Updated x/modified.json
- Deleted extra entry x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- end
- it "knife upload --purge * uploads everything" do
- knife("upload --purge *").should_succeed <<~EOM
- Created x/added.json
- Updated x/modified.json
- Deleted extra entry x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- end
- end
- end
- end
-
- # Cookbook upload is a funny thing ... direct cookbook upload works, but
- # upload of a file is designed not to work at present. Make sure that is the
- # case.
- when_the_chef_server "has a cookbook" do
- before do
- cookbook "x", "1.0.0", { "z.rb" => "" }
- end
-
- when_the_repository "does not have metadata file" do
- before do
- file "cookbooks/x/y.rb", "hi"
- end
-
- it "raises MetadataNotFound exception" do
- expect { knife("upload /cookbooks/x") }.to raise_error(Chef::Exceptions::MetadataNotFound)
- end
- end
-
- when_the_repository "does not have valid metadata" do
- before do
- file "cookbooks/x/metadata.rb", cb_metadata(nil, "1.0.0")
- end
-
- it "raises exception for invalid metadata" do
- expect { knife("upload /cookbooks/x") }.to raise_error(Chef::Exceptions::MetadataNotValid)
- end
- end
-
- when_the_repository "has a modified, extra and missing file for the cookbook" do
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
- file "cookbooks/x/y.rb", "hi"
- end
-
- it "knife upload of any individual file fails" do
- knife("upload /cookbooks/x/metadata.rb").should_fail "ERROR: /cookbooks/x/metadata.rb cannot be updated.\n"
- knife("upload /cookbooks/x/y.rb").should_fail "ERROR: /cookbooks/x cannot have a child created under it.\n"
- knife("upload --purge /cookbooks/x/z.rb").should_fail "ERROR: /cookbooks/x/z.rb cannot be deleted.\n"
- end
-
- # TODO this is a bit of an inconsistency: if we didn't specify --purge,
- # technically we shouldn't have deleted missing files. But ... cookbooks
- # are a special case.
- it "knife upload of the cookbook itself succeeds" do
- knife("upload /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
-
- it "knife upload --purge of the cookbook itself succeeds" do
- knife("upload /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
- end
- when_the_repository "has a missing file for the cookbook" do
-
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- end
-
- it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
- end
- when_the_repository "has an extra file for the cookbook" do
-
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- file "cookbooks/x/z.rb", ""
- file "cookbooks/x/blah.rb", ""
- end
-
- it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
- end
-
- when_the_repository "has a different file in the cookbook" do
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- end
-
- it "knife upload --freeze freezes the cookbook" do
- knife("upload --freeze /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- # Modify a file and attempt to upload
- file "cookbooks/x/metadata.rb", 'name "x"; version "1.0.0"#different'
- knife("upload /cookbooks/x").should_fail "ERROR: /cookbooks failed to write: Cookbook x is frozen\n"
- end
- end
- end
-
- when_the_chef_server "has a frozen cookbook" do
- before do
- cookbook "frozencook", "1.0.0", {}, frozen: true
- end
-
- when_the_repository "has an update to said cookbook" do
-
- before do
- file "cookbooks/frozencook/metadata.rb", cb_metadata("frozencook", "1.0.0", "# This is different")
- end
-
- it "knife upload fails to upload the frozen cookbook" do
- knife("upload /cookbooks/frozencook").should_fail "ERROR: /cookbooks failed to write: Cookbook frozencook is frozen\n"
- end
- it "knife upload --force uploads the frozen cookbook" do
- knife("upload --force /cookbooks/frozencook").should_succeed <<~EOM
- Updated /cookbooks/frozencook
- EOM
- end
- end
- end
-
- when_the_repository "has a cookbook" do
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- file "cookbooks/x/metadata.json", { name: "x", version: "1.0.0" }
- file "cookbooks/x/onlyin1.0.0.rb", "old_text"
- end
-
- when_the_chef_server "has a later version for the cookbook" do
- before do
- cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
- cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x/metadata.rb
- D\t/cookbooks/x/onlyin1.0.1.rb
- A\t/cookbooks/x/metadata.json
- A\t/cookbooks/x/onlyin1.0.0.rb
- EOM
- knife("upload --purge /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x/metadata.rb
- D\t/cookbooks/x/onlyin1.0.1.rb
- A\t/cookbooks/x/metadata.json
- A\t/cookbooks/x/onlyin1.0.0.rb
- EOM
- end
- end
- end
-
- when_the_repository "has a cookbook" do
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- file "cookbooks/x/onlyin1.0.0.rb", "old_text"
- end
-
- when_the_chef_server "has a later version for the cookbook" do
- before do
- cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
- cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the local version and generates metadata.json from metadata.rb and uploads it." do
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x/metadata.rb
- D\t/cookbooks/x/onlyin1.0.1.rb
- A\t/cookbooks/x/onlyin1.0.0.rb
- EOM
- knife("upload --purge /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x/metadata.rb
- D\t/cookbooks/x/onlyin1.0.1.rb
- A\t/cookbooks/x/onlyin1.0.0.rb
- EOM
- end
- end
-
- when_the_chef_server "has an earlier version for the cookbook" do
- before do
- cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
- cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the local version generates metadata.json and uploads it." do
- knife("upload --purge /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
- end
-
- when_the_chef_server "has a later version for the cookbook, and no current version" do
- before do
- cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the local version and generates metadata.json before upload and uploads it." do
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x/metadata.rb
- D\t/cookbooks/x/onlyin1.0.1.rb
- A\t/cookbooks/x/onlyin1.0.0.rb
- EOM
- knife("upload --purge /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x/metadata.rb
- D\t/cookbooks/x/onlyin1.0.1.rb
- A\t/cookbooks/x/onlyin1.0.0.rb
- EOM
- end
- end
-
- when_the_chef_server "has an earlier version for the cookbook, and no current version" do
- before do
- cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the new version" do
- knife("upload --purge /cookbooks/x").should_succeed <<~EOM
- Updated /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
- end
- end
-
- when_the_chef_server "has an environment" do
- before do
- environment "x", {}
- end
-
- when_the_repository "has an environment with bad JSON" do
- before do
- file "environments/x.json", "{"
- end
-
- it "knife upload tries and fails" do
- error1 = <<~EOH
- WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
- {
- (right here) ------^
-
- ERROR: /environments/x.json failed to write: Parse error reading JSON: parse error: premature EOF
- {
- (right here) ------^
- EOH
-
- warn = <<~EOH
- WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
- {
- (right here) ------^
-
- EOH
- knife("upload /environments/x.json").should_fail(error1)
- knife("diff --name-status /environments/x.json").should_succeed("M\t/environments/x.json\n", stderr: warn)
- end
- end
-
- when_the_repository "has the same environment with the wrong name in the file" do
- before do
- file "environments/x.json", { "name" => "y" }
- end
- it "knife upload fails" do
- knife("upload /environments/x.json").should_fail "ERROR: /environments/x.json failed to write: Name must be 'x' (is 'y')\n"
- knife("diff --name-status /environments/x.json").should_succeed "M\t/environments/x.json\n"
- end
- end
-
- when_the_repository "has the same environment with no name in the file" do
- before do
- file "environments/x.json", { "description" => "hi" }
- end
- it "knife upload succeeds" do
- knife("upload /environments/x.json").should_succeed "Updated /environments/x.json\n"
- knife("diff --name-status /environments/x.json").should_succeed ""
- end
- end
- end
-
- when_the_chef_server "is empty" do
-
- when_the_repository "has an environment with the wrong name in the file" do
- before do
- file "environments/x.json", { "name" => "y" }
- end
- it "knife upload fails" do
- knife("upload /environments/x.json").should_fail "ERROR: /environments failed to create_child: Error creating 'x.json': Name must be 'x' (is 'y')\n"
- knife("diff --name-status /environments/x.json").should_succeed "A\t/environments/x.json\n"
- end
- end
-
- when_the_repository "has an environment with no name in the file" do
-
- before do
- file "environments/x.json", { "description" => "hi" }
- end
- it "knife upload succeeds" do
- knife("upload /environments/x.json").should_succeed "Created /environments/x.json\n"
- knife("diff --name-status /environments/x.json").should_succeed ""
- end
- end
-
- when_the_repository "has a data bag with no id in the file" do
- before do
- file "data_bags/bag/x.json", { "foo" => "bar" }
- end
- it "knife upload succeeds" do
- knife("upload /data_bags/bag/x.json").should_succeed "Created /data_bags/bag\nCreated /data_bags/bag/x.json\n"
- knife("diff --name-status /data_bags/bag/x.json").should_succeed ""
- end
- end
- end
- when_the_chef_server "is empty" do
- when_the_repository "has a cookbook with an invalid chef_version constraint in it" do
- before do
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
- end
- it "knife upload succeeds" do
- knife("upload /cookbooks/x").should_succeed <<~EOM
- Created /cookbooks/x
- EOM
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x/metadata.json
- EOM
- end
- end
- end
- end # without versioned cookbooks
-
- context "with versioned cookbooks" do
- before { Chef::Config[:versioned_cookbooks] = true }
-
- when_the_chef_server "has one of each thing" do
-
- before do
- client "x", {}
- cookbook "x", "1.0.0"
- data_bag "x", { "y" => {} }
- environment "x", {}
- node "x", {}
- role "x", {}
- user "x", {}
- end
-
- when_the_repository "has only top-level directories" do
- before do
- directory "clients"
- directory "cookbooks"
- directory "data_bags"
- directory "environments"
- directory "nodes"
- directory "roles"
- directory "users"
- end
-
- it "knife upload does nothing" do
- knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/clients/chef-validator.json
- D\t/clients/chef-webui.json
- D\t/clients/x.json
- D\t/cookbooks/x-1.0.0
- D\t/data_bags/x
- D\t/environments/_default.json
- D\t/environments/x.json
- D\t/nodes/x.json
- D\t/roles/x.json
- D\t/users/admin.json
- D\t/users/x.json
- EOM
- end
-
- it "knife upload --purge deletes everything" do
- knife("upload --purge /").should_succeed(<<~EOM, stderr: "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
- Deleted extra entry /clients/chef-validator.json (purge is on)
- Deleted extra entry /clients/chef-webui.json (purge is on)
- Deleted extra entry /clients/x.json (purge is on)
- Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
- Deleted extra entry /data_bags/x (purge is on)
- Deleted extra entry /environments/x.json (purge is on)
- Deleted extra entry /nodes/x.json (purge is on)
- Deleted extra entry /roles/x.json (purge is on)
- Deleted extra entry /users/admin.json (purge is on)
- Deleted extra entry /users/x.json (purge is on)
- EOM
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/environments/_default.json
- EOM
- end
- end
-
- when_the_repository "has an identical copy of each thing" do
- before do
- file "clients/chef-validator.json", { "validator" => true, "public_key" => ChefZero::PUBLIC_KEY }
- file "clients/chef-webui.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
- file "clients/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
- file "data_bags/x/y.json", {}
- file "environments/_default.json", { "description" => "The default Chef environment" }
- file "environments/x.json", {}
- file "nodes/x.json", { "normal" => { "tags" => [] } }
- file "roles/x.json", {}
- file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
- file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
- end
-
- it "knife upload makes no changes" do
- knife("upload /cookbooks/x-1.0.0").should_succeed ""
- knife("diff --name-status /").should_succeed ""
- end
-
- it "knife upload --purge makes no changes" do
- knife("upload --purge /").should_succeed ""
- knife("diff --name-status /").should_succeed ""
- end
-
- context "except the role file" do
- before do
- file "roles/x.json", { "description" => "blarghle" }
- end
-
- it "knife upload changes the role" do
- knife("upload /").should_succeed "Updated /roles/x.json\n"
- knife("diff --name-status /").should_succeed ""
- end
- end
-
- context "except the role file is textually different, but not ACTUALLY different" do
-
- before do
- file "roles/x.json", <<~EOM
- {
- "chef_type": "role",
- "default_attributes": {
- },
- "env_run_lists": {
- },
- "json_class": "Chef::Role",
- "name": "x",
- "description": "",
- "override_attributes": {
- },
- "run_list": [
-
- ]
- }
- EOM
- end
-
- it "knife upload / does not change anything" do
- knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed ""
- end
- end
-
- context "as well as one extra copy of each thing" do
- before do
- file "clients/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
- file "cookbooks/x-1.0.0/blah.rb", ""
- file "cookbooks/x-2.0.0/metadata.rb", cb_metadata("x", "2.0.0")
- file "cookbooks/y-1.0.0/metadata.rb", cb_metadata("y", "1.0.0")
- file "data_bags/x/z.json", {}
- file "data_bags/y/zz.json", {}
- file "environments/y.json", {}
- file "nodes/y.json", {}
- file "roles/y.json", {}
- file "users/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
- end
-
- it "knife upload adds the new files" do
- knife("upload /").should_succeed <<~EOM
- Created /clients/y.json
- Updated /cookbooks/x-1.0.0
- Created /cookbooks/x-2.0.0
- Created /cookbooks/y-1.0.0
- Created /data_bags/x/z.json
- Created /data_bags/y
- Created /data_bags/y/zz.json
- Created /environments/y.json
- Created /nodes/y.json
- Created /roles/y.json
- Created /users/y.json
- EOM
- knife("diff --name-status /").should_succeed ""
- end
- end
- end
-
- when_the_repository "is empty" do
- it "knife upload does nothing" do
- knife("upload /").should_succeed ""
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/clients
- D\t/cookbooks
- D\t/data_bags
- D\t/environments
- D\t/nodes
- D\t/roles
- D\t/users
- EOM
- end
-
- it "knife upload --purge deletes nothing" do
- knife("upload --purge /").should_fail <<~EOM
- ERROR: /clients cannot be deleted.
- ERROR: /cookbooks cannot be deleted.
- ERROR: /data_bags cannot be deleted.
- ERROR: /environments cannot be deleted.
- ERROR: /nodes cannot be deleted.
- ERROR: /roles cannot be deleted.
- ERROR: /users cannot be deleted.
- EOM
- knife("diff --name-status /").should_succeed <<~EOM
- D\t/clients
- D\t/cookbooks
- D\t/data_bags
- D\t/environments
- D\t/nodes
- D\t/roles
- D\t/users
- EOM
- end
-
- context "when current directory is top level" do
- before do
- cwd "."
- end
- it "knife upload with no parameters reports an error" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
- end
- end
- end
- end
-
- # Test upload of an item when the other end doesn't even have the container
- when_the_chef_server "is empty" do
- when_the_repository "has two data bag items" do
- before do
- file "data_bags/x/y.json", {}
- file "data_bags/x/z.json", {}
- end
-
- it "knife upload of one data bag item itself succeeds" do
- knife("upload /data_bags/x/y.json").should_succeed <<~EOM
- Created /data_bags/x
- Created /data_bags/x/y.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- A\t/data_bags/x/z.json
- EOM
- end
- end
- end
-
- when_the_chef_server "has three data bag items" do
- before do
- data_bag "x", { "deleted" => {}, "modified" => {}, "unmodified" => {} }
- end
- when_the_repository "has a modified, unmodified, added and deleted data bag item" do
- before do
- file "data_bags/x/added.json", {}
- file "data_bags/x/modified.json", { "foo" => "bar" }
- file "data_bags/x/unmodified.json", {}
- end
-
- it "knife upload of the modified file succeeds" do
- knife("upload /data_bags/x/modified.json").should_succeed <<~EOM
- Updated /data_bags/x/modified.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload of the unmodified file does nothing" do
- knife("upload /data_bags/x/unmodified.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- M\t/data_bags/x/modified.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload of the added file succeeds" do
- knife("upload /data_bags/x/added.json").should_succeed <<~EOM
- Created /data_bags/x/added.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- M\t/data_bags/x/modified.json
- EOM
- end
- it "knife upload of the deleted file does nothing" do
- knife("upload /data_bags/x/deleted.json").should_succeed ""
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- M\t/data_bags/x/modified.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload --purge of the deleted file deletes it" do
- knife("upload --purge /data_bags/x/deleted.json").should_succeed <<~EOM
- Deleted extra entry /data_bags/x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- M\t/data_bags/x/modified.json
- A\t/data_bags/x/added.json
- EOM
- end
- it "knife upload of the entire data bag uploads everything" do
- knife("upload /data_bags/x").should_succeed <<~EOM
- Created /data_bags/x/added.json
- Updated /data_bags/x/modified.json
- EOM
- knife("diff --name-status /data_bags").should_succeed <<~EOM
- D\t/data_bags/x/deleted.json
- EOM
- end
- it "knife upload --purge of the entire data bag uploads everything" do
- knife("upload --purge /data_bags/x").should_succeed <<~EOM
- Created /data_bags/x/added.json
- Updated /data_bags/x/modified.json
- Deleted extra entry /data_bags/x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- end
- context "when cwd is the /data_bags directory" do
- before do
- cwd "data_bags"
- end
- it "knife upload fails" do
- knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
- end
- it "knife upload --purge . uploads everything" do
- knife("upload --purge .").should_succeed <<~EOM
- Created x/added.json
- Updated x/modified.json
- Deleted extra entry x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- end
- it "knife upload --purge * uploads everything" do
- knife("upload --purge *").should_succeed <<~EOM
- Created x/added.json
- Updated x/modified.json
- Deleted extra entry x/deleted.json (purge is on)
- EOM
- knife("diff --name-status /data_bags").should_succeed ""
- end
- end
- end
- end
-
- # Cookbook upload is a funny thing ... direct cookbook upload works, but
- # upload of a file is designed not to work at present. Make sure that is the
- # case.
- when_the_chef_server "has a cookbook" do
- before do
- cookbook "x", "1.0.0", { "z.rb" => "" }
- end
-
- when_the_repository "has a modified, extra and missing file for the cookbook" do
- before do
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
- file "cookbooks/x-1.0.0/y.rb", "hi"
- end
-
- it "knife upload of any individual file fails" do
- knife("upload /cookbooks/x-1.0.0/metadata.rb").should_fail "ERROR: /cookbooks/x-1.0.0/metadata.rb cannot be updated.\n"
- knife("upload /cookbooks/x-1.0.0/y.rb").should_fail "ERROR: /cookbooks/x-1.0.0 cannot have a child created under it.\n"
- knife("upload --purge /cookbooks/x-1.0.0/z.rb").should_fail "ERROR: /cookbooks/x-1.0.0/z.rb cannot be deleted.\n"
- end
-
- # TODO this is a bit of an inconsistency: if we didn't specify --purge,
- # technically we shouldn't have deleted missing files. But ... cookbooks
- # are a special case.
- it "knife upload of the cookbook itself succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
- Updated /cookbooks/x-1.0.0
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
-
- it "knife upload --purge of the cookbook itself succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
- Updated /cookbooks/x-1.0.0
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
-
- when_the_repository "has a missing file for the cookbook" do
- before do
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
- end
-
- it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
- Updated /cookbooks/x-1.0.0
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
-
- when_the_repository "has an extra file for the cookbook" do
- before do
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
- file "cookbooks/x-1.0.0/z.rb", ""
- file "cookbooks/x-1.0.0/blah.rb", ""
- end
-
- it "knife upload of the cookbook succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
- Updated /cookbooks/x-1.0.0
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
- end
-
- when_the_repository "has a cookbook" do
- before do
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
- file "cookbooks/x-1.0.0/onlyin1.0.0.rb", "old_text"
- end
-
- when_the_chef_server "has a later version for the cookbook" do
- before do
- cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
- cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
- end
-
- it "knife upload /cookbooks uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- M\t/cookbooks/x-1.0.0/onlyin1.0.0.rb
- D\t/cookbooks/x-1.0.1
- EOM
- knife("upload --purge /cookbooks").should_succeed <<~EOM
- Updated /cookbooks/x-1.0.0
- Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
-
- when_the_chef_server "has an earlier version for the cookbook" do
- before do
- cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
- cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
- end
- it "knife upload /cookbooks uploads the local version" do
- knife("upload --purge /cookbooks").should_succeed <<~EOM
- Updated /cookbooks/x-1.0.0
- Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
-
- when_the_chef_server "has a later version for the cookbook, and no current version" do
- before do
- cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the local version" do
- knife("diff --name-status /cookbooks").should_succeed <<~EOM
- D\t/cookbooks/x-1.0.1
- A\t/cookbooks/x-1.0.0
- EOM
- knife("upload --purge /cookbooks").should_succeed <<~EOM
- Created /cookbooks/x-1.0.0
- Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
-
- when_the_chef_server "has an earlier version for the cookbook, and no current version" do
- before do
- cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
- end
-
- it "knife upload /cookbooks/x uploads the new version" do
- knife("upload --purge /cookbooks").should_succeed <<~EOM
- Created /cookbooks/x-1.0.0
- Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
- end
-
- when_the_chef_server "has an environment" do
- before do
- environment "x", {}
- end
-
- when_the_repository "has the same environment with the wrong name in the file" do
- before do
- file "environments/x.json", { "name" => "y" }
- end
- it "knife upload fails" do
- knife("upload /environments/x.json").should_fail "ERROR: /environments/x.json failed to write: Name must be 'x' (is 'y')\n"
- knife("diff --name-status /environments/x.json").should_succeed "M\t/environments/x.json\n"
- end
- end
-
- when_the_repository "has the same environment with no name in the file" do
- before do
- file "environments/x.json", { "description" => "hi" }
- end
- it "knife upload succeeds" do
- knife("upload /environments/x.json").should_succeed "Updated /environments/x.json\n"
- knife("diff --name-status /environments/x.json").should_succeed ""
- end
- end
- end
-
- when_the_chef_server "is empty" do
-
- when_the_repository "has an environment with the wrong name in the file" do
- before do
- file "environments/x.json", { "name" => "y" }
- end
- it "knife upload fails" do
- knife("upload /environments/x.json").should_fail "ERROR: /environments failed to create_child: Error creating 'x.json': Name must be 'x' (is 'y')\n"
- knife("diff --name-status /environments/x.json").should_succeed "A\t/environments/x.json\n"
- end
- end
-
- when_the_repository "has an environment with no name in the file" do
- before do
- file "environments/x.json", { "description" => "hi" }
- end
- it "knife upload succeeds" do
- knife("upload /environments/x.json").should_succeed "Created /environments/x.json\n"
- knife("diff --name-status /environments/x.json").should_succeed ""
- end
- end
-
- when_the_repository "has a data bag with no id in the file" do
- before do
- file "data_bags/bag/x.json", { "foo" => "bar" }
- end
- it "knife upload succeeds" do
- knife("upload /data_bags/bag/x.json").should_succeed "Created /data_bags/bag\nCreated /data_bags/bag/x.json\n"
- knife("diff --name-status /data_bags/bag/x.json").should_succeed ""
- end
- end
- end
-
- when_the_chef_server "is empty" do
- when_the_repository "has a cookbook with an invalid chef_version constraint in it" do
- before do
- file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
- end
- it "knife upload succeeds" do
- knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
- Created /cookbooks/x-1.0.0
- EOM
- knife("diff --name-status /cookbooks").should_succeed ""
- end
- end
- end
- end # with versioned cookbooks
-
- when_the_chef_server "has a user" do
- before do
- user "x", {}
- end
-
- when_the_repository "has the same user with json_class in it" do
- before do
- file "users/x.json", { "admin" => true, "json_class" => "Chef::WebUIUser" }
- end
- it "knife upload /users/x.json succeeds" do
- knife("upload /users/x.json").should_succeed "Updated /users/x.json\n"
- end
- end
- end
-
- when_the_chef_server "is in Enterprise mode", osc_compat: false, single_org: false do
- before do
- user "foo", {}
- user "bar", {}
- user "foobar", {}
- organization "foo", { "full_name" => "Something" }
- end
-
- before :each do
- Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, "/organizations/foo")
- end
-
- context "and has nothing but a single group named blah" do
- group "blah", {}
-
- when_the_repository "has at least one of each thing" do
-
- before do
- # TODO We have to upload acls for an existing group due to a lack of
- # dependency detection during upload. Fix that!
- file "acls/groups/blah.json", {}
- file "clients/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
- file "containers/x.json", {}
- file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
- file "cookbook_artifacts/x-1x1/metadata.rb", cb_metadata("x", "1.0.0")
- file "data_bags/x/y.json", {}
- file "environments/x.json", {}
- file "groups/x.json", {}
- file "invitations.json", [ "foo" ]
- file "members.json", [ "bar" ]
- file "org.json", { "full_name" => "wootles" }
- file "nodes/x.json", { "normal" => { "tags" => [] } }
- file "policies/x-1.0.0.json", { "policy_group_list" => [ "x" ] }
- file "policies/blah-1.0.0.json", { "policy_group_list" => [ "x" ] }
- file "policy_groups/x.json", { "policies" => { "x" => { "revision_id" => "1.0.0" }, "blah" => { "revision_id" => "1.0.0" } } }
- file "roles/x.json", {}
- end
-
- it "knife upload / uploads everything" do
- knife("upload /").should_succeed <<~EOM
- Updated /acls/groups/blah.json
- Created /clients/x.json
- Created /containers/x.json
- Created /cookbook_artifacts/x-1x1
- Created /cookbooks/x
- Created /data_bags/x
- Created /data_bags/x/y.json
- Created /environments/x.json
- Created /groups/x.json
- Updated /invitations.json
- Updated /members.json
- Created /nodes/x.json
- Updated /org.json
- Created /policies/blah-1.0.0.json
- Created /policies/x-1.0.0.json
- Created /policy_groups/x.json
- Created /roles/x.json
- EOM
- expect(api.get("association_requests").map { |a| a["username"] }).to eq([ "foo" ])
- expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
- knife("diff --name-status --diff-filter=AMT /").should_succeed ""
- end
-
- context "When the chef server has an identical copy of each thing" do
- before do
- file "invitations.json", [ "foo" ]
- file "members.json", [ "bar" ]
- file "org.json", { "full_name" => "Something" }
-
- # acl_for %w(organizations foo groups blah)
- client "x", {}
- cookbook "x", "1.0.0"
- cookbook_artifact "x", "1x1", "metadata.rb" => cb_metadata("x", "1.0.0")
- container "x", {}
- data_bag "x", { "y" => {} }
- environment "x", {}
- group "x", {}
- org_invite "foo"
- org_member "bar"
- node "x", {}
- policy "x", "1.0.0", {}
- policy "blah", "1.0.0", {}
- policy_group "x", {
- "policies" => {
- "x" => { "revision_id" => "1.0.0" },
- "blah" => { "revision_id" => "1.0.0" },
- },
- }
- role "x", {}
- end
-
- it "knife upload makes no changes" do
- knife("upload /").should_succeed <<~EOM
- Updated /acls/groups/blah.json
- EOM
- end
- end
-
- context "When the chef server has a slightly different copy of the policy revision" do
- before do
- policy "x", "1.0.0", { "run_list" => [ "blah" ] }
- end
-
- it "should fail because policies are not updateable" do
- knife("upload /policies/x-1.0.0.json").should_fail <<~EOM
- ERROR: /policies/x-1.0.0.json cannot be updated: policy revisions are immutable once uploaded. If you want to change the policy, create a new revision with your changes.
- EOM
- end
- end
-
- context "When the chef server has a slightly different copy of the cookbook artifact" do
- before do
- cookbook_artifact "x", "1x1", { "recipes" => { "default.rb" => "" } }
- end
-
- it "should fail because cookbook_artifacts cannot be updated" do
- knife("upload /cookbook_artifacts/x-1x1").should_fail <<~EOM
- ERROR: /cookbook_artifacts/x-1x1 cannot be updated: cookbook artifacts are immutable once uploaded.
- EOM
- end
- end
-
- context "When the chef server has a slightly different copy of each thing (except policy revisions)" do
- before do
- # acl_for %w(organizations foo groups blah)
- client "x", { "validator" => true }
- container "x", {}
- cookbook "x", "1.0.0", { "recipes" => { "default.rb" => "" } }
- cookbook_artifact "x", "1x1", { "metadata.rb" => cb_metadata("x", "1.0.0") }
- data_bag "x", { "y" => { "a" => "b" } }
- environment "x", { "description" => "foo" }
- group "x", { "groups" => [ "admin" ] }
- node "x", { "run_list" => [ "blah" ] }
- policy "x", "1.0.0", { "policy_group_list" => [ "x" ] }
- policy "y", "1.0.0", { "policy_group_list" => [ "x" ] }
- policy_group "x", {
- "policies" => {
- "x" => { "revision_id" => "1.0.0" },
- "y" => { "revision_id" => "1.0.0" },
- },
- }
- role "x", { "run_list" => [ "blah" ] }
- end
-
- it "knife upload updates everything" do
- knife("upload /").should_succeed <<~EOM
- Updated /acls/groups/blah.json
- Updated /clients/x.json
- Updated /cookbooks/x
- Updated /data_bags/x/y.json
- Updated /environments/x.json
- Updated /groups/x.json
- Updated /invitations.json
- Updated /members.json
- Updated /nodes/x.json
- Updated /org.json
- Created /policies/blah-1.0.0.json
- Updated /policy_groups/x.json
- Updated /roles/x.json
- EOM
- knife("diff --name-status --diff-filter=AMT /").should_succeed ""
- end
- end
- end
-
- when_the_repository "has an org.json that does not change full_name" do
- before do
- file "org.json", { "full_name" => "Something" }
- end
-
- it "knife upload / emits a warning for bar and adds foo and foobar" do
- knife("upload /").should_succeed ""
- expect(api.get("/")["full_name"]).to eq("Something")
- end
- end
-
- when_the_repository "has an org.json that changes full_name" do
- before do
- file "org.json", { "full_name" => "Something Else" }
- end
-
- it "knife upload / emits a warning for bar and adds foo and foobar" do
- knife("upload /").should_succeed "Updated /org.json\n"
- expect(api.get("/")["full_name"]).to eq("Something Else")
- end
- end
-
- context "and has invited foo and bar is already a member" do
- org_invite "foo"
- org_member "bar"
-
- when_the_repository "wants to invite foo, bar and foobar" do
- before do
- file "invitations.json", %w{foo bar foobar}
- end
-
- it "knife upload / emits a warning for bar and invites foobar" do
- knife("upload /").should_succeed "Updated /invitations.json\n", stderr: "WARN: Could not invite bar to organization foo: User bar is already in organization foo\n"
- expect(api.get("association_requests").map { |a| a["username"] }).to eq(%w{foo foobar})
- expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
- end
- end
-
- when_the_repository "wants to make foo, bar and foobar members" do
- before do
- file "members.json", %w{foo bar foobar}
- end
-
- it "knife upload / emits a warning for bar and adds foo and foobar" do
- knife("upload /").should_succeed "Updated /members.json\n"
- expect(api.get("association_requests").map { |a| a["username"] }).to eq([ ])
- expect(api.get("users").map { |a| a["user"]["username"] }).to eq(%w{bar foo foobar})
- end
- end
-
- when_the_repository "wants to invite foo and have bar as a member" do
- before do
- file "invitations.json", [ "foo" ]
- file "members.json", [ "bar" ]
- end
-
- it "knife upload / does nothing" do
- knife("upload /").should_succeed ""
- expect(api.get("association_requests").map { |a| a["username"] }).to eq([ "foo" ])
- expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
- end
- end
- end
-
- context "and has invited bar and foo" do
- org_invite "bar", "foo"
-
- when_the_repository "wants to invite foo and bar (different order)" do
- before do
- file "invitations.json", %w{foo bar}
- end
-
- it "knife upload / does nothing" do
- knife("upload /").should_succeed ""
- expect(api.get("association_requests").map { |a| a["username"] }).to eq(%w{bar foo})
- expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ ])
- end
- end
- end
-
- context "and has already added bar and foo as members of the org" do
- org_member "bar", "foo"
-
- when_the_repository "wants to add foo and bar (different order)" do
- before do
- file "members.json", %w{foo bar}
- end
-
- it "knife upload / does nothing" do
- knife("upload /").should_succeed ""
- expect(api.get("association_requests").map { |a| a["username"] }).to eq([ ])
- expect(api.get("users").map { |a| a["user"]["username"] }).to eq(%w{bar foo})
- end
- end
- end
- end
- end
-end
+#
+# Author:: John Keiser (<jkeiser@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 "knife_spec_helper"
+require "support/shared/integration/integration_helper"
+require "chef/knife/upload"
+require "chef/knife/diff"
+require "chef/knife/raw"
+require "chef/json_compat"
+
+describe "knife upload", :workstation do
+ include IntegrationSupport
+ include KnifeSupport
+
+ context "without versioned cookbooks" do
+
+ when_the_chef_server "has one of each thing" do
+
+ before do
+ client "x", {}
+ cookbook "x", "1.0.0"
+ data_bag "x", { "y" => {} }
+ environment "x", {}
+ node "x", {}
+ role "x", {}
+ user "x", {}
+ end
+
+ when_the_repository "has only top-level directories" do
+ before do
+ directory "clients"
+ directory "cookbooks"
+ directory "data_bags"
+ directory "environments"
+ directory "nodes"
+ directory "roles"
+ directory "users"
+ end
+
+ it "knife upload does nothing" do
+ knife("upload /").should_succeed ""
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients/chef-validator.json
+ D\t/clients/chef-webui.json
+ D\t/clients/x.json
+ D\t/cookbooks/x
+ D\t/data_bags/x
+ D\t/environments/_default.json
+ D\t/environments/x.json
+ D\t/nodes/x.json
+ D\t/roles/x.json
+ D\t/users/admin.json
+ D\t/users/x.json
+ EOM
+ end
+
+ it "knife upload --purge deletes everything" do
+ knife("upload --purge /").should_succeed(<<~EOM, stderr: "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
+ Deleted extra entry /clients/chef-validator.json (purge is on)
+ Deleted extra entry /clients/chef-webui.json (purge is on)
+ Deleted extra entry /clients/x.json (purge is on)
+ Deleted extra entry /cookbooks/x (purge is on)
+ Deleted extra entry /data_bags/x (purge is on)
+ Deleted extra entry /environments/x.json (purge is on)
+ Deleted extra entry /nodes/x.json (purge is on)
+ Deleted extra entry /roles/x.json (purge is on)
+ Deleted extra entry /users/admin.json (purge is on)
+ Deleted extra entry /users/x.json (purge is on)
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/environments/_default.json
+ EOM
+ end
+ end
+
+ when_the_repository "has an identical copy of each thing" do
+
+ before do
+ file "clients/chef-validator.json", { "validator" => true, "public_key" => ChefZero::PUBLIC_KEY }
+ file "clients/chef-webui.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
+ file "clients/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "data_bags/x/y.json", {}
+ file "environments/_default.json", { "description" => "The default Chef environment" }
+ file "environments/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
+ file "roles/x.json", {}
+ file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
+ file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ end
+
+ it "knife upload makes no changes" do
+ knife("upload /cookbooks/x").should_succeed ""
+ knife("diff --name-status /").should_succeed ""
+ end
+
+ it "knife upload --purge makes no changes" do
+ knife("upload --purge /").should_succeed ""
+ knife("diff --name-status /").should_succeed ""
+ end
+
+ context "except the role file" do
+ before do
+ file "roles/x.json", { "description" => "blarghle" }
+ end
+
+ it "knife upload changes the role" do
+ knife("upload /").should_succeed "Updated /roles/x.json\n"
+ knife("diff --name-status /").should_succeed ""
+ end
+ it "knife upload --no-diff does not change the role" do
+ knife("upload --no-diff /").should_succeed ""
+ knife("diff --name-status /").should_succeed "M\t/roles/x.json\n"
+ end
+ end
+
+ context "except the role file is textually different, but not ACTUALLY different" do
+ before do
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "description": "",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
+ end
+
+ it "knife upload / does not change anything" do
+ knife("upload /").should_succeed ""
+ knife("diff --name-status /").should_succeed ""
+ end
+ end
+
+ context "the role is in ruby" do
+ before do
+ file "roles/x.rb", <<~EOM
+ name "x"
+ description "blargle"
+ EOM
+ end
+
+ it "knife upload changes the role" do
+ knife("upload /").should_succeed "Updated /roles/x.json\n"
+ knife("diff --name-status /").should_succeed ""
+ end
+
+ it "knife upload --no-diff does not change the role" do
+ knife("upload --no-diff /").should_succeed ""
+ knife("diff --name-status /").should_succeed "M\t/roles/x.rb\n"
+ end
+ end
+
+ context "when cookbook metadata has a self-dependency" do
+ before do
+ file "cookbooks/x/metadata.rb", "name 'x'; version '1.0.0'; depends 'x'"
+ end
+
+ it "fails with RuntimeError" do
+ expect { knife("upload /cookbooks") }.to raise_error RuntimeError, /Cookbook depends on itself/
+ end
+ end
+
+ context "as well as one extra copy of each thing" do
+ before do
+ file "clients/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ file "cookbooks/x/blah.rb", ""
+ file "cookbooks/y/metadata.rb", cb_metadata("y", "1.0.0")
+ file "data_bags/x/z.json", {}
+ file "data_bags/y/zz.json", {}
+ file "environments/y.json", {}
+ file "nodes/y.json", {}
+ file "roles/y.json", {}
+ file "users/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ end
+
+ it "knife upload adds the new files" do
+ knife("upload /").should_succeed <<~EOM
+ Created /clients/y.json
+ Updated /cookbooks/x
+ Created /cookbooks/y
+ Created /data_bags/x/z.json
+ Created /data_bags/y
+ Created /data_bags/y/zz.json
+ Created /environments/y.json
+ Created /nodes/y.json
+ Created /roles/y.json
+ Created /users/y.json
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ D\t/cookbooks/y/metadata.json
+ EOM
+ end
+
+ it "knife upload --no-diff adds the new files" do
+ knife("upload --no-diff /").should_succeed <<~EOM
+ Created /clients/y.json
+ Updated /cookbooks/x
+ Created /cookbooks/y
+ Created /data_bags/x/z.json
+ Created /data_bags/y
+ Created /data_bags/y/zz.json
+ Created /environments/y.json
+ Created /nodes/y.json
+ Created /roles/y.json
+ Created /users/y.json
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ D\t/cookbooks/y/metadata.json
+ EOM
+ end
+ end
+ end
+
+ when_the_repository "is empty" do
+ it "knife upload does nothing" do
+ knife("upload /").should_succeed ""
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
+ end
+
+ it "knife upload --purge deletes nothing" do
+ knife("upload --purge /").should_fail <<~EOM
+ ERROR: /clients cannot be deleted.
+ ERROR: /cookbooks cannot be deleted.
+ ERROR: /data_bags cannot be deleted.
+ ERROR: /environments cannot be deleted.
+ ERROR: /nodes cannot be deleted.
+ ERROR: /roles cannot be deleted.
+ ERROR: /users cannot be deleted.
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
+ end
+
+ context "when current directory is top level" do
+ before do
+ cwd "."
+ end
+
+ it "knife upload with no parameters reports an error" do
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
+ end
+ end
+ end
+ end
+
+ when_the_chef_server "is empty" do
+ when_the_repository "has a data bag item" do
+
+ before do
+ file "data_bags/x/y.json", { "foo" => "bar" }
+ end
+
+ it "knife upload of the data bag uploads only the values in the data bag item and no other" do
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ EOM
+ expect(Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, create_additions: false).keys.sort).to eq(%w{foo id})
+ end
+
+ it "knife upload /data_bags/x /data_bags/x/y.json uploads x once" do
+ knife("upload /data_bags/x /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ end
+ end
+
+ when_the_repository "has a data bag item with keys chef_type and data_bag" do
+
+ before do
+ file "data_bags/x/y.json", { "chef_type" => "aaa", "data_bag" => "bbb" }
+ end
+
+ it "upload preserves chef_type and data_bag" do
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ result = Chef::JSONCompat.parse(knife("raw /data/x/y").stdout, create_additions: false)
+ expect(result.keys.sort).to eq(%w{chef_type data_bag id})
+ expect(result["chef_type"]).to eq("aaa")
+ expect(result["data_bag"]).to eq("bbb")
+ end
+ end
+
+ # Test upload of an item when the other end doesn't even have the container
+ when_the_repository "has two data bag items" do
+ before do
+ file "data_bags/x/y.json", {}
+ file "data_bags/x/z.json", {}
+ end
+ it "knife upload of one data bag item itself succeeds" do
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ A\t/data_bags/x/z.json
+ EOM
+ end
+ end
+ end
+
+ when_the_chef_server "has three data bag items" do
+
+ before do
+ data_bag "x", { "deleted" => {}, "modified" => {}, "unmodified" => {} }
+ end
+
+ when_the_repository "has a modified, unmodified, added and deleted data bag item" do
+ before do
+ file "data_bags/x/added.json", {}
+ file "data_bags/x/modified.json", { "foo" => "bar" }
+ file "data_bags/x/unmodified.json", {}
+ end
+
+ it "knife upload of the modified file succeeds" do
+ knife("upload /data_bags/x/modified.json").should_succeed <<~EOM
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload of the unmodified file does nothing" do
+ knife("upload /data_bags/x/unmodified.json").should_succeed ""
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload of the added file succeeds" do
+ knife("upload /data_bags/x/added.json").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ EOM
+ end
+ it "knife upload of the deleted file does nothing" do
+ knife("upload /data_bags/x/deleted.json").should_succeed ""
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload --purge of the deleted file deletes it" do
+ knife("upload --purge /data_bags/x/deleted.json").should_succeed <<~EOM
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload of the entire data bag uploads everything" do
+ knife("upload /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ EOM
+ end
+ it "knife upload --purge of the entire data bag uploads everything" do
+ knife("upload --purge /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ end
+ context "when cwd is the /data_bags directory" do
+
+ before do
+ cwd "data_bags"
+ end
+
+ it "knife upload fails" do
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
+ end
+
+ it "knife upload --purge . uploads everything" do
+ knife("upload --purge .").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ end
+ it "knife upload --purge * uploads everything" do
+ knife("upload --purge *").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ end
+ end
+ end
+ end
+
+ # Cookbook upload is a funny thing ... direct cookbook upload works, but
+ # upload of a file is designed not to work at present. Make sure that is the
+ # case.
+ when_the_chef_server "has a cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "z.rb" => "" }
+ end
+
+ when_the_repository "does not have metadata file" do
+ before do
+ file "cookbooks/x/y.rb", "hi"
+ end
+
+ it "raises MetadataNotFound exception" do
+ expect { knife("upload /cookbooks/x") }.to raise_error(Chef::Exceptions::MetadataNotFound)
+ end
+ end
+
+ when_the_repository "does not have valid metadata" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata(nil, "1.0.0")
+ end
+
+ it "raises exception for invalid metadata" do
+ expect { knife("upload /cookbooks/x") }.to raise_error(Chef::Exceptions::MetadataNotValid)
+ end
+ end
+
+ when_the_repository "has a modified, extra and missing file for the cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
+ file "cookbooks/x/y.rb", "hi"
+ end
+
+ it "knife upload of any individual file fails" do
+ knife("upload /cookbooks/x/metadata.rb").should_fail "ERROR: /cookbooks/x/metadata.rb cannot be updated.\n"
+ knife("upload /cookbooks/x/y.rb").should_fail "ERROR: /cookbooks/x cannot have a child created under it.\n"
+ knife("upload --purge /cookbooks/x/z.rb").should_fail "ERROR: /cookbooks/x/z.rb cannot be deleted.\n"
+ end
+
+ # TODO this is a bit of an inconsistency: if we didn't specify --purge,
+ # technically we shouldn't have deleted missing files. But ... cookbooks
+ # are a special case.
+ it "knife upload of the cookbook itself succeeds" do
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+
+ it "knife upload --purge of the cookbook itself succeeds" do
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+ end
+ when_the_repository "has a missing file for the cookbook" do
+
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ end
+
+ it "knife upload of the cookbook succeeds" do
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+ end
+ when_the_repository "has an extra file for the cookbook" do
+
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x/z.rb", ""
+ file "cookbooks/x/blah.rb", ""
+ end
+
+ it "knife upload of the cookbook succeeds" do
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+ end
+
+ when_the_repository "has a different file in the cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ end
+
+ it "knife upload --freeze freezes the cookbook" do
+ knife("upload --freeze /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ # Modify a file and attempt to upload
+ file "cookbooks/x/metadata.rb", 'name "x"; version "1.0.0"#different'
+ knife("upload /cookbooks/x").should_fail "ERROR: /cookbooks failed to write: Cookbook x is frozen\n"
+ end
+ end
+ end
+
+ when_the_chef_server "has a frozen cookbook" do
+ before do
+ cookbook "frozencook", "1.0.0", {}, frozen: true
+ end
+
+ when_the_repository "has an update to said cookbook" do
+
+ before do
+ file "cookbooks/frozencook/metadata.rb", cb_metadata("frozencook", "1.0.0", "# This is different")
+ end
+
+ it "knife upload fails to upload the frozen cookbook" do
+ knife("upload /cookbooks/frozencook").should_fail "ERROR: /cookbooks failed to write: Cookbook frozencook is frozen\n"
+ end
+ it "knife upload --force uploads the frozen cookbook" do
+ knife("upload --force /cookbooks/frozencook").should_succeed <<~EOM
+ Updated /cookbooks/frozencook
+ EOM
+ end
+ end
+ end
+
+ when_the_repository "has a cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x/metadata.json", { name: "x", version: "1.0.0" }
+ file "cookbooks/x/onlyin1.0.0.rb", "old_text"
+ end
+
+ when_the_chef_server "has a later version for the cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
+ cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the local version" do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/metadata.json
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/metadata.json
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ end
+ end
+ end
+
+ when_the_repository "has a cookbook" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x/onlyin1.0.0.rb", "old_text"
+ end
+
+ when_the_chef_server "has a later version for the cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
+ cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the local version and generates metadata.json from metadata.rb and uploads it." do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ end
+ end
+
+ when_the_chef_server "has an earlier version for the cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
+ cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the local version generates metadata.json and uploads it." do
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+ end
+
+ when_the_chef_server "has a later version for the cookbook, and no current version" do
+ before do
+ cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the local version and generates metadata.json before upload and uploads it." do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x/metadata.rb
+ D\t/cookbooks/x/onlyin1.0.1.rb
+ A\t/cookbooks/x/onlyin1.0.0.rb
+ EOM
+ end
+ end
+
+ when_the_chef_server "has an earlier version for the cookbook, and no current version" do
+ before do
+ cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the new version" do
+ knife("upload --purge /cookbooks/x").should_succeed <<~EOM
+ Updated /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+ end
+ end
+
+ when_the_chef_server "has an environment" do
+ before do
+ environment "x", {}
+ end
+
+ when_the_repository "has an environment with bad JSON" do
+ before do
+ file "environments/x.json", "{"
+ end
+
+ it "knife upload tries and fails" do
+ error1 = <<~EOH
+ WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
+ {
+ (right here) ------^
+
+ ERROR: /environments/x.json failed to write: Parse error reading JSON: parse error: premature EOF
+ {
+ (right here) ------^
+ EOH
+
+ warn = <<~EOH
+ WARN: Parse error reading #{path_to("environments/x.json")} as JSON: parse error: premature EOF
+ {
+ (right here) ------^
+
+ EOH
+ knife("upload /environments/x.json").should_fail(error1)
+ knife("diff --name-status /environments/x.json").should_succeed("M\t/environments/x.json\n", stderr: warn)
+ end
+ end
+
+ when_the_repository "has the same environment with the wrong name in the file" do
+ before do
+ file "environments/x.json", { "name" => "y" }
+ end
+ it "knife upload fails" do
+ knife("upload /environments/x.json").should_fail "ERROR: /environments/x.json failed to write: Name must be 'x' (is 'y')\n"
+ knife("diff --name-status /environments/x.json").should_succeed "M\t/environments/x.json\n"
+ end
+ end
+
+ when_the_repository "has the same environment with no name in the file" do
+ before do
+ file "environments/x.json", { "description" => "hi" }
+ end
+ it "knife upload succeeds" do
+ knife("upload /environments/x.json").should_succeed "Updated /environments/x.json\n"
+ knife("diff --name-status /environments/x.json").should_succeed ""
+ end
+ end
+ end
+
+ when_the_chef_server "is empty" do
+
+ when_the_repository "has an environment with the wrong name in the file" do
+ before do
+ file "environments/x.json", { "name" => "y" }
+ end
+ it "knife upload fails" do
+ knife("upload /environments/x.json").should_fail "ERROR: /environments failed to create_child: Error creating 'x.json': Name must be 'x' (is 'y')\n"
+ knife("diff --name-status /environments/x.json").should_succeed "A\t/environments/x.json\n"
+ end
+ end
+
+ when_the_repository "has an environment with no name in the file" do
+
+ before do
+ file "environments/x.json", { "description" => "hi" }
+ end
+ it "knife upload succeeds" do
+ knife("upload /environments/x.json").should_succeed "Created /environments/x.json\n"
+ knife("diff --name-status /environments/x.json").should_succeed ""
+ end
+ end
+
+ when_the_repository "has a data bag with no id in the file" do
+ before do
+ file "data_bags/bag/x.json", { "foo" => "bar" }
+ end
+ it "knife upload succeeds" do
+ knife("upload /data_bags/bag/x.json").should_succeed "Created /data_bags/bag\nCreated /data_bags/bag/x.json\n"
+ knife("diff --name-status /data_bags/bag/x.json").should_succeed ""
+ end
+ end
+ end
+ when_the_chef_server "is empty" do
+ when_the_repository "has a cookbook with an invalid chef_version constraint in it" do
+ before do
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
+ end
+ it "knife upload succeeds" do
+ knife("upload /cookbooks/x").should_succeed <<~EOM
+ Created /cookbooks/x
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x/metadata.json
+ EOM
+ end
+ end
+ end
+ end # without versioned cookbooks
+
+ context "with versioned cookbooks" do
+ before { Chef::Config[:versioned_cookbooks] = true }
+
+ when_the_chef_server "has one of each thing" do
+
+ before do
+ client "x", {}
+ cookbook "x", "1.0.0"
+ data_bag "x", { "y" => {} }
+ environment "x", {}
+ node "x", {}
+ role "x", {}
+ user "x", {}
+ end
+
+ when_the_repository "has only top-level directories" do
+ before do
+ directory "clients"
+ directory "cookbooks"
+ directory "data_bags"
+ directory "environments"
+ directory "nodes"
+ directory "roles"
+ directory "users"
+ end
+
+ it "knife upload does nothing" do
+ knife("upload /").should_succeed ""
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients/chef-validator.json
+ D\t/clients/chef-webui.json
+ D\t/clients/x.json
+ D\t/cookbooks/x-1.0.0
+ D\t/data_bags/x
+ D\t/environments/_default.json
+ D\t/environments/x.json
+ D\t/nodes/x.json
+ D\t/roles/x.json
+ D\t/users/admin.json
+ D\t/users/x.json
+ EOM
+ end
+
+ it "knife upload --purge deletes everything" do
+ knife("upload --purge /").should_succeed(<<~EOM, stderr: "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n")
+ Deleted extra entry /clients/chef-validator.json (purge is on)
+ Deleted extra entry /clients/chef-webui.json (purge is on)
+ Deleted extra entry /clients/x.json (purge is on)
+ Deleted extra entry /cookbooks/x-1.0.0 (purge is on)
+ Deleted extra entry /data_bags/x (purge is on)
+ Deleted extra entry /environments/x.json (purge is on)
+ Deleted extra entry /nodes/x.json (purge is on)
+ Deleted extra entry /roles/x.json (purge is on)
+ Deleted extra entry /users/admin.json (purge is on)
+ Deleted extra entry /users/x.json (purge is on)
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/environments/_default.json
+ EOM
+ end
+ end
+
+ when_the_repository "has an identical copy of each thing" do
+ before do
+ file "clients/chef-validator.json", { "validator" => true, "public_key" => ChefZero::PUBLIC_KEY }
+ file "clients/chef-webui.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
+ file "clients/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
+ file "data_bags/x/y.json", {}
+ file "environments/_default.json", { "description" => "The default Chef environment" }
+ file "environments/x.json", {}
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
+ file "roles/x.json", {}
+ file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
+ file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ end
+
+ it "knife upload makes no changes" do
+ knife("upload /cookbooks/x-1.0.0").should_succeed ""
+ knife("diff --name-status /").should_succeed ""
+ end
+
+ it "knife upload --purge makes no changes" do
+ knife("upload --purge /").should_succeed ""
+ knife("diff --name-status /").should_succeed ""
+ end
+
+ context "except the role file" do
+ before do
+ file "roles/x.json", { "description" => "blarghle" }
+ end
+
+ it "knife upload changes the role" do
+ knife("upload /").should_succeed "Updated /roles/x.json\n"
+ knife("diff --name-status /").should_succeed ""
+ end
+ end
+
+ context "except the role file is textually different, but not ACTUALLY different" do
+
+ before do
+ file "roles/x.json", <<~EOM
+ {
+ "chef_type": "role",
+ "default_attributes": {
+ },
+ "env_run_lists": {
+ },
+ "json_class": "Chef::Role",
+ "name": "x",
+ "description": "",
+ "override_attributes": {
+ },
+ "run_list": [
+
+ ]
+ }
+ EOM
+ end
+
+ it "knife upload / does not change anything" do
+ knife("upload /").should_succeed ""
+ knife("diff --name-status /").should_succeed ""
+ end
+ end
+
+ context "as well as one extra copy of each thing" do
+ before do
+ file "clients/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ file "cookbooks/x-1.0.0/blah.rb", ""
+ file "cookbooks/x-2.0.0/metadata.rb", cb_metadata("x", "2.0.0")
+ file "cookbooks/y-1.0.0/metadata.rb", cb_metadata("y", "1.0.0")
+ file "data_bags/x/z.json", {}
+ file "data_bags/y/zz.json", {}
+ file "environments/y.json", {}
+ file "nodes/y.json", {}
+ file "roles/y.json", {}
+ file "users/y.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ end
+
+ it "knife upload adds the new files" do
+ knife("upload /").should_succeed <<~EOM
+ Created /clients/y.json
+ Updated /cookbooks/x-1.0.0
+ Created /cookbooks/x-2.0.0
+ Created /cookbooks/y-1.0.0
+ Created /data_bags/x/z.json
+ Created /data_bags/y
+ Created /data_bags/y/zz.json
+ Created /environments/y.json
+ Created /nodes/y.json
+ Created /roles/y.json
+ Created /users/y.json
+ EOM
+ knife("diff --name-status /").should_succeed ""
+ end
+ end
+ end
+
+ when_the_repository "is empty" do
+ it "knife upload does nothing" do
+ knife("upload /").should_succeed ""
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
+ end
+
+ it "knife upload --purge deletes nothing" do
+ knife("upload --purge /").should_fail <<~EOM
+ ERROR: /clients cannot be deleted.
+ ERROR: /cookbooks cannot be deleted.
+ ERROR: /data_bags cannot be deleted.
+ ERROR: /environments cannot be deleted.
+ ERROR: /nodes cannot be deleted.
+ ERROR: /roles cannot be deleted.
+ ERROR: /users cannot be deleted.
+ EOM
+ knife("diff --name-status /").should_succeed <<~EOM
+ D\t/clients
+ D\t/cookbooks
+ D\t/data_bags
+ D\t/environments
+ D\t/nodes
+ D\t/roles
+ D\t/users
+ EOM
+ end
+
+ context "when current directory is top level" do
+ before do
+ cwd "."
+ end
+ it "knife upload with no parameters reports an error" do
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
+ end
+ end
+ end
+ end
+
+ # Test upload of an item when the other end doesn't even have the container
+ when_the_chef_server "is empty" do
+ when_the_repository "has two data bag items" do
+ before do
+ file "data_bags/x/y.json", {}
+ file "data_bags/x/z.json", {}
+ end
+
+ it "knife upload of one data bag item itself succeeds" do
+ knife("upload /data_bags/x/y.json").should_succeed <<~EOM
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ A\t/data_bags/x/z.json
+ EOM
+ end
+ end
+ end
+
+ when_the_chef_server "has three data bag items" do
+ before do
+ data_bag "x", { "deleted" => {}, "modified" => {}, "unmodified" => {} }
+ end
+ when_the_repository "has a modified, unmodified, added and deleted data bag item" do
+ before do
+ file "data_bags/x/added.json", {}
+ file "data_bags/x/modified.json", { "foo" => "bar" }
+ file "data_bags/x/unmodified.json", {}
+ end
+
+ it "knife upload of the modified file succeeds" do
+ knife("upload /data_bags/x/modified.json").should_succeed <<~EOM
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload of the unmodified file does nothing" do
+ knife("upload /data_bags/x/unmodified.json").should_succeed ""
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload of the added file succeeds" do
+ knife("upload /data_bags/x/added.json").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ EOM
+ end
+ it "knife upload of the deleted file does nothing" do
+ knife("upload /data_bags/x/deleted.json").should_succeed ""
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload --purge of the deleted file deletes it" do
+ knife("upload --purge /data_bags/x/deleted.json").should_succeed <<~EOM
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ M\t/data_bags/x/modified.json
+ A\t/data_bags/x/added.json
+ EOM
+ end
+ it "knife upload of the entire data bag uploads everything" do
+ knife("upload /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ EOM
+ knife("diff --name-status /data_bags").should_succeed <<~EOM
+ D\t/data_bags/x/deleted.json
+ EOM
+ end
+ it "knife upload --purge of the entire data bag uploads everything" do
+ knife("upload --purge /data_bags/x").should_succeed <<~EOM
+ Created /data_bags/x/added.json
+ Updated /data_bags/x/modified.json
+ Deleted extra entry /data_bags/x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ end
+ context "when cwd is the /data_bags directory" do
+ before do
+ cwd "data_bags"
+ end
+ it "knife upload fails" do
+ knife("upload").should_fail "FATAL: You must specify at least one argument. If you want to upload everything in this directory, run \"knife upload .\"\n", stdout: /USAGE/
+ end
+ it "knife upload --purge . uploads everything" do
+ knife("upload --purge .").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ end
+ it "knife upload --purge * uploads everything" do
+ knife("upload --purge *").should_succeed <<~EOM
+ Created x/added.json
+ Updated x/modified.json
+ Deleted extra entry x/deleted.json (purge is on)
+ EOM
+ knife("diff --name-status /data_bags").should_succeed ""
+ end
+ end
+ end
+ end
+
+ # Cookbook upload is a funny thing ... direct cookbook upload works, but
+ # upload of a file is designed not to work at present. Make sure that is the
+ # case.
+ when_the_chef_server "has a cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "z.rb" => "" }
+ end
+
+ when_the_repository "has a modified, extra and missing file for the cookbook" do
+ before do
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
+ file "cookbooks/x-1.0.0/y.rb", "hi"
+ end
+
+ it "knife upload of any individual file fails" do
+ knife("upload /cookbooks/x-1.0.0/metadata.rb").should_fail "ERROR: /cookbooks/x-1.0.0/metadata.rb cannot be updated.\n"
+ knife("upload /cookbooks/x-1.0.0/y.rb").should_fail "ERROR: /cookbooks/x-1.0.0 cannot have a child created under it.\n"
+ knife("upload --purge /cookbooks/x-1.0.0/z.rb").should_fail "ERROR: /cookbooks/x-1.0.0/z.rb cannot be deleted.\n"
+ end
+
+ # TODO this is a bit of an inconsistency: if we didn't specify --purge,
+ # technically we shouldn't have deleted missing files. But ... cookbooks
+ # are a special case.
+ it "knife upload of the cookbook itself succeeds" do
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+
+ it "knife upload --purge of the cookbook itself succeeds" do
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+
+ when_the_repository "has a missing file for the cookbook" do
+ before do
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
+ end
+
+ it "knife upload of the cookbook succeeds" do
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+
+ when_the_repository "has an extra file for the cookbook" do
+ before do
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x-1.0.0/z.rb", ""
+ file "cookbooks/x-1.0.0/blah.rb", ""
+ end
+
+ it "knife upload of the cookbook succeeds" do
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+ end
+
+ when_the_repository "has a cookbook" do
+ before do
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbooks/x-1.0.0/onlyin1.0.0.rb", "old_text"
+ end
+
+ when_the_chef_server "has a later version for the cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
+ cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks uploads the local version" do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ M\t/cookbooks/x-1.0.0/onlyin1.0.0.rb
+ D\t/cookbooks/x-1.0.1
+ EOM
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+
+ when_the_chef_server "has an earlier version for the cookbook" do
+ before do
+ cookbook "x", "1.0.0", { "onlyin1.0.0.rb" => "" }
+ cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
+ end
+ it "knife upload /cookbooks uploads the local version" do
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Updated /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+
+ when_the_chef_server "has a later version for the cookbook, and no current version" do
+ before do
+ cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the local version" do
+ knife("diff --name-status /cookbooks").should_succeed <<~EOM
+ D\t/cookbooks/x-1.0.1
+ A\t/cookbooks/x-1.0.0
+ EOM
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-1.0.1 (purge is on)
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+
+ when_the_chef_server "has an earlier version for the cookbook, and no current version" do
+ before do
+ cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" }
+ end
+
+ it "knife upload /cookbooks/x uploads the new version" do
+ knife("upload --purge /cookbooks").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.0
+ Deleted extra entry /cookbooks/x-0.9.9 (purge is on)
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+ end
+
+ when_the_chef_server "has an environment" do
+ before do
+ environment "x", {}
+ end
+
+ when_the_repository "has the same environment with the wrong name in the file" do
+ before do
+ file "environments/x.json", { "name" => "y" }
+ end
+ it "knife upload fails" do
+ knife("upload /environments/x.json").should_fail "ERROR: /environments/x.json failed to write: Name must be 'x' (is 'y')\n"
+ knife("diff --name-status /environments/x.json").should_succeed "M\t/environments/x.json\n"
+ end
+ end
+
+ when_the_repository "has the same environment with no name in the file" do
+ before do
+ file "environments/x.json", { "description" => "hi" }
+ end
+ it "knife upload succeeds" do
+ knife("upload /environments/x.json").should_succeed "Updated /environments/x.json\n"
+ knife("diff --name-status /environments/x.json").should_succeed ""
+ end
+ end
+ end
+
+ when_the_chef_server "is empty" do
+
+ when_the_repository "has an environment with the wrong name in the file" do
+ before do
+ file "environments/x.json", { "name" => "y" }
+ end
+ it "knife upload fails" do
+ knife("upload /environments/x.json").should_fail "ERROR: /environments failed to create_child: Error creating 'x.json': Name must be 'x' (is 'y')\n"
+ knife("diff --name-status /environments/x.json").should_succeed "A\t/environments/x.json\n"
+ end
+ end
+
+ when_the_repository "has an environment with no name in the file" do
+ before do
+ file "environments/x.json", { "description" => "hi" }
+ end
+ it "knife upload succeeds" do
+ knife("upload /environments/x.json").should_succeed "Created /environments/x.json\n"
+ knife("diff --name-status /environments/x.json").should_succeed ""
+ end
+ end
+
+ when_the_repository "has a data bag with no id in the file" do
+ before do
+ file "data_bags/bag/x.json", { "foo" => "bar" }
+ end
+ it "knife upload succeeds" do
+ knife("upload /data_bags/bag/x.json").should_succeed "Created /data_bags/bag\nCreated /data_bags/bag/x.json\n"
+ knife("diff --name-status /data_bags/bag/x.json").should_succeed ""
+ end
+ end
+ end
+
+ when_the_chef_server "is empty" do
+ when_the_repository "has a cookbook with an invalid chef_version constraint in it" do
+ before do
+ file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "\nchef_version '~> 999.0'")
+ end
+ it "knife upload succeeds" do
+ knife("upload /cookbooks/x-1.0.0").should_succeed <<~EOM
+ Created /cookbooks/x-1.0.0
+ EOM
+ knife("diff --name-status /cookbooks").should_succeed ""
+ end
+ end
+ end
+ end # with versioned cookbooks
+
+ when_the_chef_server "has a user" do
+ before do
+ user "x", {}
+ end
+
+ when_the_repository "has the same user with json_class in it" do
+ before do
+ file "users/x.json", { "admin" => true, "json_class" => "Chef::WebUIUser" }
+ end
+ it "knife upload /users/x.json succeeds" do
+ knife("upload /users/x.json").should_succeed "Updated /users/x.json\n"
+ end
+ end
+ end
+
+ when_the_chef_server "is in Enterprise mode", osc_compat: false, single_org: false do
+ before do
+ user "foo", {}
+ user "bar", {}
+ user "foobar", {}
+ organization "foo", { "full_name" => "Something" }
+ end
+
+ before :each do
+ Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, "/organizations/foo")
+ end
+
+ context "and has nothing but a single group named blah" do
+ group "blah", {}
+
+ when_the_repository "has at least one of each thing" do
+
+ before do
+ # TODO We have to upload acls for an existing group due to a lack of
+ # dependency detection during upload. Fix that!
+ file "acls/groups/blah.json", {}
+ file "clients/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
+ file "containers/x.json", {}
+ file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+ file "cookbook_artifacts/x-1x1/metadata.rb", cb_metadata("x", "1.0.0")
+ file "data_bags/x/y.json", {}
+ file "environments/x.json", {}
+ file "groups/x.json", {}
+ file "invitations.json", [ "foo" ]
+ file "members.json", [ "bar" ]
+ file "org.json", { "full_name" => "wootles" }
+ file "nodes/x.json", { "normal" => { "tags" => [] } }
+ file "policies/x-1.0.0.json", { "policy_group_list" => [ "x" ] }
+ file "policies/blah-1.0.0.json", { "policy_group_list" => [ "x" ] }
+ file "policy_groups/x.json", { "policies" => { "x" => { "revision_id" => "1.0.0" }, "blah" => { "revision_id" => "1.0.0" } } }
+ file "roles/x.json", {}
+ end
+
+ it "knife upload / uploads everything" do
+ knife("upload /").should_succeed <<~EOM
+ Updated /acls/groups/blah.json
+ Created /clients/x.json
+ Created /containers/x.json
+ Created /cookbook_artifacts/x-1x1
+ Created /cookbooks/x
+ Created /data_bags/x
+ Created /data_bags/x/y.json
+ Created /environments/x.json
+ Created /groups/x.json
+ Updated /invitations.json
+ Updated /members.json
+ Created /nodes/x.json
+ Updated /org.json
+ Created /policies/blah-1.0.0.json
+ Created /policies/x-1.0.0.json
+ Created /policy_groups/x.json
+ Created /roles/x.json
+ EOM
+ expect(api.get("association_requests").map { |a| a["username"] }).to eq([ "foo" ])
+ expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
+ knife("diff --name-status --diff-filter=AMT /").should_succeed ""
+ end
+
+ context "When the chef server has an identical copy of each thing" do
+ before do
+ file "invitations.json", [ "foo" ]
+ file "members.json", [ "bar" ]
+ file "org.json", { "full_name" => "Something" }
+
+ # acl_for %w(organizations foo groups blah)
+ client "x", {}
+ cookbook "x", "1.0.0"
+ cookbook_artifact "x", "1x1", "metadata.rb" => cb_metadata("x", "1.0.0")
+ container "x", {}
+ data_bag "x", { "y" => {} }
+ environment "x", {}
+ group "x", {}
+ org_invite "foo"
+ org_member "bar"
+ node "x", {}
+ policy "x", "1.0.0", {}
+ policy "blah", "1.0.0", {}
+ policy_group "x", {
+ "policies" => {
+ "x" => { "revision_id" => "1.0.0" },
+ "blah" => { "revision_id" => "1.0.0" },
+ },
+ }
+ role "x", {}
+ end
+
+ it "knife upload makes no changes" do
+ knife("upload /").should_succeed <<~EOM
+ Updated /acls/groups/blah.json
+ EOM
+ end
+ end
+
+ context "When the chef server has a slightly different copy of the policy revision" do
+ before do
+ policy "x", "1.0.0", { "run_list" => [ "blah" ] }
+ end
+
+ it "should fail because policies are not updateable" do
+ knife("upload /policies/x-1.0.0.json").should_fail <<~EOM
+ ERROR: /policies/x-1.0.0.json cannot be updated: policy revisions are immutable once uploaded. If you want to change the policy, create a new revision with your changes.
+ EOM
+ end
+ end
+
+ context "When the chef server has a slightly different copy of the cookbook artifact" do
+ before do
+ cookbook_artifact "x", "1x1", { "recipes" => { "default.rb" => "" } }
+ end
+
+ it "should fail because cookbook_artifacts cannot be updated" do
+ knife("upload /cookbook_artifacts/x-1x1").should_fail <<~EOM
+ ERROR: /cookbook_artifacts/x-1x1 cannot be updated: cookbook artifacts are immutable once uploaded.
+ EOM
+ end
+ end
+
+ context "When the chef server has a slightly different copy of each thing (except policy revisions)" do
+ before do
+ # acl_for %w(organizations foo groups blah)
+ client "x", { "validator" => true }
+ container "x", {}
+ cookbook "x", "1.0.0", { "recipes" => { "default.rb" => "" } }
+ cookbook_artifact "x", "1x1", { "metadata.rb" => cb_metadata("x", "1.0.0") }
+ data_bag "x", { "y" => { "a" => "b" } }
+ environment "x", { "description" => "foo" }
+ group "x", { "groups" => [ "admin" ] }
+ node "x", { "run_list" => [ "blah" ] }
+ policy "x", "1.0.0", { "policy_group_list" => [ "x" ] }
+ policy "y", "1.0.0", { "policy_group_list" => [ "x" ] }
+ policy_group "x", {
+ "policies" => {
+ "x" => { "revision_id" => "1.0.0" },
+ "y" => { "revision_id" => "1.0.0" },
+ },
+ }
+ role "x", { "run_list" => [ "blah" ] }
+ end
+
+ it "knife upload updates everything" do
+ knife("upload /").should_succeed <<~EOM
+ Updated /acls/groups/blah.json
+ Updated /clients/x.json
+ Updated /cookbooks/x
+ Updated /data_bags/x/y.json
+ Updated /environments/x.json
+ Updated /groups/x.json
+ Updated /invitations.json
+ Updated /members.json
+ Updated /nodes/x.json
+ Updated /org.json
+ Created /policies/blah-1.0.0.json
+ Updated /policy_groups/x.json
+ Updated /roles/x.json
+ EOM
+ knife("diff --name-status --diff-filter=AMT /").should_succeed ""
+ end
+ end
+ end
+
+ when_the_repository "has an org.json that does not change full_name" do
+ before do
+ file "org.json", { "full_name" => "Something" }
+ end
+
+ it "knife upload / emits a warning for bar and adds foo and foobar" do
+ knife("upload /").should_succeed ""
+ expect(api.get("/")["full_name"]).to eq("Something")
+ end
+ end
+
+ when_the_repository "has an org.json that changes full_name" do
+ before do
+ file "org.json", { "full_name" => "Something Else" }
+ end
+
+ it "knife upload / emits a warning for bar and adds foo and foobar" do
+ knife("upload /").should_succeed "Updated /org.json\n"
+ expect(api.get("/")["full_name"]).to eq("Something Else")
+ end
+ end
+
+ context "and has invited foo and bar is already a member" do
+ org_invite "foo"
+ org_member "bar"
+
+ when_the_repository "wants to invite foo, bar and foobar" do
+ before do
+ file "invitations.json", %w{foo bar foobar}
+ end
+
+ it "knife upload / emits a warning for bar and invites foobar" do
+ knife("upload /").should_succeed "Updated /invitations.json\n", stderr: "WARN: Could not invite bar to organization foo: User bar is already in organization foo\n"
+ expect(api.get("association_requests").map { |a| a["username"] }).to eq(%w{foo foobar})
+ expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
+ end
+ end
+
+ when_the_repository "wants to make foo, bar and foobar members" do
+ before do
+ file "members.json", %w{foo bar foobar}
+ end
+
+ it "knife upload / emits a warning for bar and adds foo and foobar" do
+ knife("upload /").should_succeed "Updated /members.json\n"
+ expect(api.get("association_requests").map { |a| a["username"] }).to eq([ ])
+ expect(api.get("users").map { |a| a["user"]["username"] }).to eq(%w{bar foo foobar})
+ end
+ end
+
+ when_the_repository "wants to invite foo and have bar as a member" do
+ before do
+ file "invitations.json", [ "foo" ]
+ file "members.json", [ "bar" ]
+ end
+
+ it "knife upload / does nothing" do
+ knife("upload /").should_succeed ""
+ expect(api.get("association_requests").map { |a| a["username"] }).to eq([ "foo" ])
+ expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ "bar" ])
+ end
+ end
+ end
+
+ context "and has invited bar and foo" do
+ org_invite "bar", "foo"
+
+ when_the_repository "wants to invite foo and bar (different order)" do
+ before do
+ file "invitations.json", %w{foo bar}
+ end
+
+ it "knife upload / does nothing" do
+ knife("upload /").should_succeed ""
+ expect(api.get("association_requests").map { |a| a["username"] }).to eq(%w{bar foo})
+ expect(api.get("users").map { |a| a["user"]["username"] }).to eq([ ])
+ end
+ end
+ end
+
+ context "and has already added bar and foo as members of the org" do
+ org_member "bar", "foo"
+
+ when_the_repository "wants to add foo and bar (different order)" do
+ before do
+ file "members.json", %w{foo bar}
+ end
+
+ it "knife upload / does nothing" do
+ knife("upload /").should_succeed ""
+ expect(api.get("association_requests").map { |a| a["username"] }).to eq([ ])
+ expect(api.get("users").map { |a| a["user"]["username"] }).to eq(%w{bar foo})
+ end
+ end
+ end
+ end
+ end
+end