# # Author:: John Keiser () # Copyright:: Copyright 2013-2016, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "support/shared/integration/integration_helper" require "chef/knife/download" require "chef/knife/diff" describe "knife download", :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 download downloads everything" do knife("download /").should_succeed < 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 download makes no changes" do knife("download /").should_succeed "" knife("diff --name-status /").should_succeed "" end it "knife download --purge makes no changes" do knife("download --purge /").should_succeed "" knife("diff --name-status /").should_succeed "" end context "except the role file" do before do file "roles/x.json", < ChefZero::PUBLIC_KEY } file "cookbooks/x/blah.rb", "" file "cookbooks/y/metadata.rb", cb_metadata("x", "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 download does nothing" do knife("download /").should_succeed "" knife("diff --name-status /").should_succeed < /USAGE/ end end end end # Test download of an item when the other end doesn't even have the container when_the_repository "is empty" do when_the_chef_server "has two data bag items" do before do data_bag "x", { "y" => {}, "z" => {} } end it "knife download of one data bag item itself succeeds" do knife("download /data_bags/x/y.json").should_succeed < {}, "modified" => { "foo" => "bar" }, "unmodified" => {}, } end it "knife download of the modified file succeeds" do knife("download /data_bags/x/modified.json").should_succeed < /USAGE/ end it "knife download --purge . downloads everything" do knife("download --purge .").should_succeed < cb_metadata("x", "1.0.0", "#extra content"), "y.rb" => "hi" } end it "knife download of a modified file succeeds" do knife("download /cookbooks/x/metadata.rb").should_succeed "Updated /cookbooks/x/metadata.rb\n" knife("diff --name-status /cookbooks").should_succeed < "" } cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" } end it "knife download /cookbooks/x downloads the latest version" do knife("download --purge /cookbooks/x").should_succeed < "" } cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" } end it "knife download /cookbooks/x downloads the updated file" do knife("download --purge /cookbooks/x").should_succeed < "hi" } end it "knife download /cookbooks/x downloads the latest version" do knife("download --purge /cookbooks/x").should_succeed < "hi" } end it "knife download /cookbooks/x downloads the old version" do knife("download --purge /cookbooks/x").should_succeed < "WARNING: /roles/x.rb cannot be updated (can't safely update ruby files).\n" knife("diff --name-status /roles/x.json").should_succeed "M\t/roles/x.rb\n" 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 download succeeds" do warning = <<-EOH WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF { (right here) ------^ EOH knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n", :stderr => warning knife("diff --name-status /environments/x.json").should_succeed "" 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 download succeeds" do knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n" knife("diff --name-status /environments/x.json").should_succeed "" 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 download succeeds" do knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n" knife("diff --name-status /environments/x.json").should_succeed "" end end end end # without versioned cookbooks with_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 download downloads everything" do knife("download /").should_succeed < 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 download makes no changes" do knife("download /").should_succeed "" knife("diff --name-status /").should_succeed "" end it "knife download --purge makes no changes" do knife("download --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 download changes the role" do knife("download /").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", < ChefZero::PUBLIC_KEY } file "cookbooks/x-1.0.0/blah.rb", "" file "cookbooks/x-2.0.0/metadata.rb", 'version "2.0.0"' file "cookbooks/y-1.0.0/metadata.rb", 'version "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 download does nothing" do knife("download /").should_succeed "" knife("diff --name-status /").should_succeed < /USAGE/ end end end end # Test download of an item when the other end doesn't even have the container when_the_repository "is empty" do when_the_chef_server "has two data bag items" do before do data_bag "x", { "y" => {}, "z" => {} } end it "knife download of one data bag item itself succeeds" do knife("download /data_bags/x/y.json").should_succeed < {}, "modified" => { "foo" => "bar" }, "unmodified" => {}, } end it "knife download of the modified file succeeds" do knife("download /data_bags/x/modified.json").should_succeed < /USAGE/ end it "knife download --purge . downloads everything" do knife("download --purge .").should_succeed < "hi" } end it "knife download of a modified file succeeds" do knife("download /cookbooks/x-1.0.0/metadata.rb").should_succeed "Updated /cookbooks/x-1.0.0/metadata.rb\n" knife("diff --name-status /cookbooks").should_succeed < "" } cookbook "x", "1.0.1", { "onlyin1.0.1.rb" => "hi" } end it "knife download /cookbooks/x downloads the latest version" do knife("download --purge /cookbooks").should_succeed < "" } cookbook "x", "0.9.9", { "onlyin0.9.9.rb" => "hi" } end it "knife download /cookbooks downloads the updated file" do knife("download --purge /cookbooks").should_succeed < "hi" } end it "knife download /cookbooks/x downloads the latest version" do knife("download --purge /cookbooks").should_succeed < "hi" } end it "knife download --purge /cookbooks downloads the old version and deletes the new version" do knife("download --purge /cookbooks").should_succeed < "y" } end it "knife download succeeds" do knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n" knife("diff --name-status /environments/x.json").should_succeed "" 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 download succeeds" do knife("download /environments/x.json").should_succeed "Updated /environments/x.json\n" knife("diff --name-status /environments/x.json").should_succeed "" end end end end # with versioned cookbooks when_the_chef_server "has a cookbook" do before do cookbook "x", "1.0.0" end when_the_repository "is empty" do it "knife download /cookbooks/x signs all requests" do # Check that BasicClient.request() always gets called with X-OPS-USERID original_new = Chef::HTTP::BasicClient.method(:new) expect(Chef::HTTP::BasicClient).to receive(:new) { |args| new_result = original_new.call(*args) original_request = new_result.method(:request) expect(new_result).to receive(:request) { |method, url, body, headers, &response_handler| expect(headers["X-OPS-USERID"]).not_to be_nil original_request.call(method, url, body, headers, &response_handler) }.at_least(:once) new_result }.at_least(:once) knife("download /cookbooks/x").should_succeed < 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 when_the_repository "has all the default stuff" do before do knife("download /").should_succeed < 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 before do knife("download /acls /groups/clients.json /groups/users.json").should_succeed <<-EOM Created /acls/clients/x.json Created /acls/containers/x.json Created /acls/cookbook_artifacts/x.json Created /acls/cookbooks/x.json Created /acls/data_bags/x.json Created /acls/environments/x.json Created /acls/groups/x.json Created /acls/nodes/x.json Created /acls/policies/blah.json Created /acls/policies/x.json Created /acls/policy_groups/x.json Created /acls/roles/x.json Updated /groups/clients.json Updated /groups/users.json EOM end it "knife download / downloads everything" do knife("download /").should_succeed < 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 "nodes/x.json", { "normal" => { "tags" => [] } } file "org.json", { "full_name" => "Something" } file "policies/x-1.0.0.json", {} file "policies/blah-1.0.0.json", {} 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 download makes no changes" do knife("download /").should_succeed "" end end context "and the repository has a slightly different copy of each thing" do before do # acl_for %w(organizations foo groups blah) file "clients/x.json", { "validator" => true } file "containers/x.json", {} file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.1") file "cookbook_artifacts/x-1x1/metadata.rb", cb_metadata("x", "1.0.1") file "data_bags/x/y.json", { "a" => "b" } file "environments/x.json", { "description" => "foo" } file "groups/x.json", { "description" => "foo" } file "groups/x.json", { "groups" => [ "admin" ] } file "nodes/x.json", { "normal" => { "tags" => [] }, "run_list" => [ "blah" ] } file "org.json", { "full_name" => "Something Else " } file "policies/x-1.0.0.json", { "run_list" => [ "blah" ] } file "policy_groups/x.json", { "policies" => { "x" => { "revision_id" => "1.0.1" }, "y" => { "revision_id" => "1.0.0" }, }, } file "roles/x.json", { "run_list" => [ "blah" ] } end it "knife download updates everything" do knife("download /").should_succeed <