chef-dk 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/chef-dk/builtin_commands.rb +10 -0
- data/lib/chef-dk/command/base.rb +2 -2
- data/lib/chef-dk/command/clean_policy_cookbooks.rb +116 -0
- data/lib/chef-dk/command/clean_policy_revisions.rb +113 -0
- data/lib/chef-dk/command/delete_policy.rb +122 -0
- data/lib/chef-dk/command/delete_policy_group.rb +122 -0
- data/lib/chef-dk/command/export.rb +3 -3
- data/lib/chef-dk/command/generate.rb +8 -0
- data/lib/chef-dk/command/generator_commands/app.rb +1 -1
- data/lib/chef-dk/command/generator_commands/cookbook.rb +1 -1
- data/lib/chef-dk/command/generator_commands/policyfile.rb +1 -1
- data/lib/chef-dk/command/generator_commands/repo.rb +1 -1
- data/lib/chef-dk/command/install.rb +22 -5
- data/lib/chef-dk/command/provision.rb +0 -4
- data/lib/chef-dk/command/push.rb +1 -2
- data/lib/chef-dk/command/shell_init.rb +65 -6
- data/lib/chef-dk/command/show_policy.rb +1 -2
- data/lib/chef-dk/command/undelete.rb +155 -0
- data/lib/chef-dk/command/update.rb +5 -5
- data/lib/chef-dk/command/verify.rb +61 -17
- data/lib/chef-dk/completions/bash.sh.erb +5 -0
- data/lib/chef-dk/completions/chef.fish.erb +10 -0
- data/lib/chef-dk/completions/zsh.zsh.erb +21 -0
- data/lib/chef-dk/exceptions.rb +12 -0
- data/lib/chef-dk/helpers.rb +17 -0
- data/lib/chef-dk/policyfile/community_cookbook_source.rb +0 -3
- data/lib/chef-dk/policyfile/lister.rb +3 -1
- data/lib/chef-dk/policyfile/undo_record.rb +142 -0
- data/lib/chef-dk/policyfile/undo_stack.rb +130 -0
- data/lib/chef-dk/policyfile_lock.rb +30 -0
- data/lib/chef-dk/policyfile_services/clean_policies.rb +5 -4
- data/lib/chef-dk/policyfile_services/clean_policy_cookbooks.rb +125 -0
- data/lib/chef-dk/policyfile_services/rm_policy.rb +142 -0
- data/lib/chef-dk/policyfile_services/rm_policy_group.rb +86 -0
- data/lib/chef-dk/policyfile_services/show_policy.rb +1 -1
- data/lib/chef-dk/policyfile_services/undelete.rb +108 -0
- data/lib/chef-dk/service_exceptions.rb +11 -0
- data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +6 -2
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +1 -1
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/attributes/default.rb +1 -1
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/recipes/default.rb +1 -1
- data/lib/chef-dk/version.rb +1 -1
- data/lib/kitchen/provisioner/policyfile_zero.rb +4 -1
- data/spec/unit/command/base_spec.rb +26 -1
- data/spec/unit/command/clean_policy_cookbooks_spec.rb +181 -0
- data/spec/unit/command/clean_policy_revisions_spec.rb +181 -0
- data/spec/unit/command/delete_policy_group_spec.rb +207 -0
- data/spec/unit/command/delete_policy_spec.rb +207 -0
- data/spec/unit/command/generate_spec.rb +41 -1
- data/spec/unit/command/generator_commands/cookbook_spec.rb +1 -1
- data/spec/unit/command/generator_commands/policyfile_spec.rb +1 -1
- data/spec/unit/command/install_spec.rb +24 -0
- data/spec/unit/command/shell_init_spec.rb +176 -5
- data/spec/unit/command/undelete_spec.rb +246 -0
- data/spec/unit/helpers_spec.rb +24 -0
- data/spec/unit/policyfile/lister_spec.rb +16 -0
- data/spec/unit/policyfile/undo_record_spec.rb +260 -0
- data/spec/unit/policyfile/undo_stack_spec.rb +266 -0
- data/spec/unit/policyfile_lock_serialization_spec.rb +41 -0
- data/spec/unit/policyfile_services/clean_policy_cookbooks_spec.rb +275 -0
- data/spec/unit/policyfile_services/rm_policy_group_spec.rb +241 -0
- data/spec/unit/policyfile_services/rm_policy_spec.rb +266 -0
- data/spec/unit/policyfile_services/show_policy_spec.rb +52 -2
- data/spec/unit/policyfile_services/undelete_spec.rb +304 -0
- metadata +43 -91
| @@ -0,0 +1,266 @@ | |
| 1 | 
            +
            #
         | 
| 2 | 
            +
            # Copyright:: Copyright (c) 2015 Chef Software Inc.
         | 
| 3 | 
            +
            # License:: Apache License, Version 2.0
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 6 | 
            +
            # you may not use this file except in compliance with the License.
         | 
| 7 | 
            +
            # You may obtain a copy of the License at
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #     http://www.apache.org/licenses/LICENSE-2.0
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Unless required by applicable law or agreed to in writing, software
         | 
| 12 | 
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 13 | 
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 14 | 
            +
            # See the License for the specific language governing permissions and
         | 
| 15 | 
            +
            # limitations under the License.
         | 
| 16 | 
            +
            #
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            require 'spec_helper'
         | 
| 19 | 
            +
            require 'chef-dk/policyfile_services/rm_policy'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            describe ChefDK::PolicyfileServices::RmPolicy do
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              let(:policy_name) { "appserver" }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              let(:policy_revisions_data) do
         | 
| 28 | 
            +
                {
         | 
| 29 | 
            +
                  "revisions" => {
         | 
| 30 | 
            +
                    "2222222222222222222222222222222222222222222222222222222222222222" => {},
         | 
| 31 | 
            +
                  }
         | 
| 32 | 
            +
                }
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              let(:ui) { TestHelpers::TestUI.new }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              let(:chef_config) do
         | 
| 38 | 
            +
                double("Chef::Config",
         | 
| 39 | 
            +
                       chef_server_url: "https://localhost:10443",
         | 
| 40 | 
            +
                       client_key: "/path/to/client/key.pem",
         | 
| 41 | 
            +
                       node_name: "deuce")
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              subject(:rm_policy_service) do
         | 
| 45 | 
            +
                described_class.new(policy_name: policy_name, ui: ui, config: chef_config)
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              let(:undo_record) do
         | 
| 49 | 
            +
                rm_policy_service.undo_record
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              let(:undo_stack) do
         | 
| 53 | 
            +
                rm_policy_service.undo_stack
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
             | 
| 57 | 
            +
              it "configures an HTTP client" do
         | 
| 58 | 
            +
                expect(ChefDK::AuthenticatedHTTP).to receive(:new).with("https://localhost:10443",
         | 
| 59 | 
            +
                                                                   signing_key_filename: "/path/to/client/key.pem",
         | 
| 60 | 
            +
                                                                   client_name: "deuce")
         | 
| 61 | 
            +
                rm_policy_service.http_client
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              context "when the server returns an error fetching the policy data" do
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                let(:response) do
         | 
| 67 | 
            +
                  Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
         | 
| 68 | 
            +
                    r.instance_variable_set(:@body, "oops")
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                let(:http_exception) do
         | 
| 73 | 
            +
                  begin
         | 
| 74 | 
            +
                    response.error!
         | 
| 75 | 
            +
                  rescue => e
         | 
| 76 | 
            +
                    e
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                before do
         | 
| 81 | 
            +
                  allow(rm_policy_service).to receive(:http_client).and_return(http_client)
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                describe "when getting an error fetching policy revisions" do
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  before do
         | 
| 87 | 
            +
                    expect(http_client).to receive(:get).with("/policies/appserver").and_return(policy_revisions_data)
         | 
| 88 | 
            +
                    expect(http_client).to receive(:get).
         | 
| 89 | 
            +
                      with("/policies/appserver/revisions/2222222222222222222222222222222222222222222222222222222222222222").
         | 
| 90 | 
            +
                      and_raise(http_exception)
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  it "re-raises the error with a standardized exception class" do
         | 
| 94 | 
            +
                    expect { rm_policy_service.run }.to raise_error(ChefDK::DeletePolicyError)
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
             | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              context "when the given policy doesn't exist" do
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                let(:policy_group) { "incorrect_policy_group_name" }
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                let(:response) do
         | 
| 107 | 
            +
                  Net::HTTPResponse.send(:response_class, "404").new("1.0", "404", "Not Found").tap do |r|
         | 
| 108 | 
            +
                    r.instance_variable_set(:@body, "not found")
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                let(:http_exception) do
         | 
| 113 | 
            +
                  begin
         | 
| 114 | 
            +
                    response.error!
         | 
| 115 | 
            +
                  rescue => e
         | 
| 116 | 
            +
                    e
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                before do
         | 
| 121 | 
            +
                  allow(rm_policy_service).to receive(:http_client).and_return(http_client)
         | 
| 122 | 
            +
                  expect(http_client).to receive(:get).with("/policies/appserver").and_raise(http_exception)
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                it "prints a message stating that the policy doesn't exist" do
         | 
| 126 | 
            +
                  expect { rm_policy_service.run }.to_not raise_error
         | 
| 127 | 
            +
                  expect(ui.output).to eq("Policy 'appserver' does not exist on the server\n")
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
              end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
              # This case makes undo impossible to implement, because there are no APIs to
         | 
| 133 | 
            +
              # create a policy name without creating a revision (i.e., there is no
         | 
| 134 | 
            +
              # `POST /policies`). Because of the separation between CLI and service
         | 
| 135 | 
            +
              # objects, running `chef delete-policy empty-policy` will still tell the user
         | 
| 136 | 
            +
              # they can undo that action by running `chef undelete --last`, but that isn't
         | 
| 137 | 
            +
              # true. That said, a policy with no revisions is invisible to `chef-client`
         | 
| 138 | 
            +
              # and `chef show-policy`; the only way to create that state with the CLI is to
         | 
| 139 | 
            +
              # `chef delete-policy-group` all the groups that policy was applied to and
         | 
| 140 | 
            +
              # then run `chef clean-policy-revisions`, which can be undone by running
         | 
| 141 | 
            +
              # `chef undelete` multiple times. So we'll test this scenario to make sure we
         | 
| 142 | 
            +
              # don't crash, but not worry about the slightly incorrect behavior.
         | 
| 143 | 
            +
              context "when the policy exists but has no revisions" do
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                let(:empty_policy_data) { {} }
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                before do
         | 
| 148 | 
            +
                  allow(rm_policy_service).to receive(:http_client).and_return(http_client)
         | 
| 149 | 
            +
                  expect(http_client).to receive(:get).with("/policies/appserver").and_return(empty_policy_data)
         | 
| 150 | 
            +
                  expect(http_client).to receive(:delete).with("/policies/appserver")
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                  expect(undo_stack).to receive(:push).with(undo_record)
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                it "removes the policy" do
         | 
| 156 | 
            +
                  rm_policy_service.run
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
              end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
              context "when the policy has several revisions" do
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                let(:policy_appserver_2) do
         | 
| 164 | 
            +
                  {
         | 
| 165 | 
            +
                    "name" => "appserver",
         | 
| 166 | 
            +
                    "revision_id" => "2222222222222222222222222222222222222222222222222222222222222222"
         | 
| 167 | 
            +
                  }
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                let(:policy_group_data) do
         | 
| 171 | 
            +
                  {
         | 
| 172 | 
            +
                    "dev" => {
         | 
| 173 | 
            +
                      "uri" => "https://chef.example/organizations/testorg/policy_groups/dev"
         | 
| 174 | 
            +
                    },
         | 
| 175 | 
            +
                    "preprod" => {
         | 
| 176 | 
            +
                      "uri" => "https://chef.example/organizations/testorg/policy_groups/preprod"
         | 
| 177 | 
            +
                    }
         | 
| 178 | 
            +
                  }
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                before do
         | 
| 182 | 
            +
                  allow(rm_policy_service).to receive(:http_client).and_return(http_client)
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                  expect(http_client).to receive(:get).with("/policies/appserver").and_return(policy_revisions_data)
         | 
| 185 | 
            +
                  expect(http_client).to receive(:get).with("/policy_groups").and_return(policy_group_data)
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                  expect(http_client).to receive(:get).
         | 
| 188 | 
            +
                    with("/policies/appserver/revisions/2222222222222222222222222222222222222222222222222222222222222222").
         | 
| 189 | 
            +
                    and_return(policy_appserver_2)
         | 
| 190 | 
            +
                  expect(http_client).to receive(:delete).with("/policies/appserver")
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                  expect(undo_stack).to receive(:push).with(undo_record)
         | 
| 193 | 
            +
                end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                it "removes the policy" do
         | 
| 196 | 
            +
                  rm_policy_service.run
         | 
| 197 | 
            +
                  expect(ui.output).to include("Removed policy 'appserver'.")
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                it "stores the policy revisions in the restore file" do
         | 
| 201 | 
            +
                  rm_policy_service.run
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                  expect(undo_record.description).to eq("delete-policy appserver")
         | 
| 204 | 
            +
                  expect(undo_record.policy_groups).to eq( [ ] )
         | 
| 205 | 
            +
                  expect(undo_record.policy_revisions.size).to eq(1)
         | 
| 206 | 
            +
                  stored_revision_info = undo_record.policy_revisions.first
         | 
| 207 | 
            +
                  expect(stored_revision_info.policy_group).to be_nil
         | 
| 208 | 
            +
                  expect(stored_revision_info.policy_name).to eq("appserver")
         | 
| 209 | 
            +
                  expect(stored_revision_info.data).to eq(policy_appserver_2)
         | 
| 210 | 
            +
                end
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                context "and some policy revisions are associated to policy groups" do
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  let(:policy_group_data) do
         | 
| 215 | 
            +
                    {
         | 
| 216 | 
            +
                      "dev" => {
         | 
| 217 | 
            +
                        "uri" => "https://chef.example/organizations/testorg/policy_groups/dev",
         | 
| 218 | 
            +
                        "policies" => {
         | 
| 219 | 
            +
                          "appserver" => { "revision_id" => "2222222222222222222222222222222222222222222222222222222222222222" },
         | 
| 220 | 
            +
                          "load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
         | 
| 221 | 
            +
                          "db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
         | 
| 222 | 
            +
                        }
         | 
| 223 | 
            +
                      },
         | 
| 224 | 
            +
                      "preprod" => {
         | 
| 225 | 
            +
                        "uri" => "https://chef.example/organizations/testorg/policy_groups/preprod",
         | 
| 226 | 
            +
                        "policies" => {
         | 
| 227 | 
            +
                          "appserver" => { "revision_id" => "2222222222222222222222222222222222222222222222222222222222222222" },
         | 
| 228 | 
            +
                          "load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
         | 
| 229 | 
            +
                          "db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
         | 
| 230 | 
            +
                        }
         | 
| 231 | 
            +
                      },
         | 
| 232 | 
            +
                      "prod" => {
         | 
| 233 | 
            +
                        "uri" => "https://chef.example/organizations/testorg/policy_groups/prod",
         | 
| 234 | 
            +
                        "policies" => {
         | 
| 235 | 
            +
                          "appserver" => { "revision_id" => "1111111111111111111111111111111111111111111111111111111111111111" },
         | 
| 236 | 
            +
                          "load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
         | 
| 237 | 
            +
                          "db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
         | 
| 238 | 
            +
                        }
         | 
| 239 | 
            +
                      }
         | 
| 240 | 
            +
                    }
         | 
| 241 | 
            +
                  end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                  it "maps the policy revisions to their groups in the restore file" do
         | 
| 244 | 
            +
                    rm_policy_service.run
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                    expect(undo_record.description).to eq("delete-policy appserver")
         | 
| 247 | 
            +
                    expect(undo_record.policy_groups).to eq( [ ] )
         | 
| 248 | 
            +
                    expect(undo_record.policy_revisions.size).to eq(2)
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                    stored_revision_info_1 = undo_record.policy_revisions.first
         | 
| 251 | 
            +
                    expect(stored_revision_info_1.policy_group).to eq("dev")
         | 
| 252 | 
            +
                    expect(stored_revision_info_1.policy_name).to eq("appserver")
         | 
| 253 | 
            +
                    expect(stored_revision_info_1.data).to eq(policy_appserver_2)
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                    stored_revision_info_2 = undo_record.policy_revisions.last
         | 
| 256 | 
            +
                    expect(stored_revision_info_2.policy_group).to eq("preprod")
         | 
| 257 | 
            +
                    expect(stored_revision_info_2.policy_name).to eq("appserver")
         | 
| 258 | 
            +
                    expect(stored_revision_info_2.data).to eq(policy_appserver_2)
         | 
| 259 | 
            +
                  end
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                end
         | 
| 262 | 
            +
             | 
| 263 | 
            +
              end
         | 
| 264 | 
            +
             | 
| 265 | 
            +
            end
         | 
| 266 | 
            +
             | 
| @@ -499,7 +499,7 @@ OUTPUT | |
| 499 499 | 
             
                    show_policy_service.run
         | 
| 500 500 | 
             
                  end
         | 
| 501 501 |  | 
| 502 | 
            -
                  context "when there are no  | 
| 502 | 
            +
                  context "when there are no policies or groups on the server" do
         | 
| 503 503 |  | 
| 504 504 | 
             
                    let(:policies_by_name) do
         | 
| 505 505 | 
             
                      {}
         | 
| @@ -516,6 +516,56 @@ appserver | |
| 516 516 |  | 
| 517 517 | 
             
            No policies named 'appserver' are associated with a policy group
         | 
| 518 518 |  | 
| 519 | 
            +
            OUTPUT
         | 
| 520 | 
            +
             | 
| 521 | 
            +
                      expect(ui.output).to eq(expected_output)
         | 
| 522 | 
            +
                    end
         | 
| 523 | 
            +
                  end
         | 
| 524 | 
            +
             | 
| 525 | 
            +
                  context "when there are no revisions of the policy on the server" do
         | 
| 526 | 
            +
             | 
| 527 | 
            +
                    let(:policies_by_name) do
         | 
| 528 | 
            +
                      {
         | 
| 529 | 
            +
                        "load-balancer" => {
         | 
| 530 | 
            +
                          "5555555555555555555555555555555555555555555555555555555555555555" => {},
         | 
| 531 | 
            +
                          "6666666666666666666666666666666666666666666666666666666666666666" => {},
         | 
| 532 | 
            +
                        },
         | 
| 533 | 
            +
                        "db" => {
         | 
| 534 | 
            +
                          "9999999999999999999999999999999999999999999999999999999999999999" => {},
         | 
| 535 | 
            +
                          "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}
         | 
| 536 | 
            +
                        },
         | 
| 537 | 
            +
                        "memcache" => {
         | 
| 538 | 
            +
                          "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" => {}
         | 
| 539 | 
            +
                        }
         | 
| 540 | 
            +
                      }
         | 
| 541 | 
            +
                    end
         | 
| 542 | 
            +
             | 
| 543 | 
            +
                    let(:policies_by_group) do
         | 
| 544 | 
            +
                      {
         | 
| 545 | 
            +
                        "dev" => {
         | 
| 546 | 
            +
                          "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555",
         | 
| 547 | 
            +
                          "db" => "9999999999999999999999999999999999999999999999999999999999999999",
         | 
| 548 | 
            +
                          "memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
         | 
| 549 | 
            +
                        },
         | 
| 550 | 
            +
                        "staging" => {
         | 
| 551 | 
            +
                          "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555",
         | 
| 552 | 
            +
                          "db" => "9999999999999999999999999999999999999999999999999999999999999999",
         | 
| 553 | 
            +
                          "memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
         | 
| 554 | 
            +
                        },
         | 
| 555 | 
            +
                        "prod" => {
         | 
| 556 | 
            +
                          "load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666",
         | 
| 557 | 
            +
                          "db" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         | 
| 558 | 
            +
                        }
         | 
| 559 | 
            +
                      }
         | 
| 560 | 
            +
                    end
         | 
| 561 | 
            +
             | 
| 562 | 
            +
                    it "prints a message to stderr that there are no copies of the policy on the server" do
         | 
| 563 | 
            +
                      expected_output = <<-OUTPUT
         | 
| 564 | 
            +
            appserver
         | 
| 565 | 
            +
            =========
         | 
| 566 | 
            +
             | 
| 567 | 
            +
            No policies named 'appserver' are associated with a policy group
         | 
| 568 | 
            +
             | 
| 519 569 | 
             
            OUTPUT
         | 
| 520 570 |  | 
| 521 571 | 
             
                      expect(ui.output).to eq(expected_output)
         | 
| @@ -782,7 +832,7 @@ OUTPUT | |
| 782 832 | 
             
                  end
         | 
| 783 833 |  | 
| 784 834 | 
             
                  before do
         | 
| 785 | 
            -
                     | 
| 835 | 
            +
                    expect(http_client).to receive(:get).with("policy_groups/dev/policies/appserver").and_raise(http_exception)
         | 
| 786 836 | 
             
                  end
         | 
| 787 837 |  | 
| 788 838 | 
             
                  it "prints a message saying there is no policy assigned" do
         | 
| @@ -0,0 +1,304 @@ | |
| 1 | 
            +
            #
         | 
| 2 | 
            +
            # Copyright:: Copyright (c) 2015 Chef Software Inc.
         | 
| 3 | 
            +
            # License:: Apache License, Version 2.0
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 6 | 
            +
            # you may not use this file except in compliance with the License.
         | 
| 7 | 
            +
            # You may obtain a copy of the License at
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #     http://www.apache.org/licenses/LICENSE-2.0
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Unless required by applicable law or agreed to in writing, software
         | 
| 12 | 
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 13 | 
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 14 | 
            +
            # See the License for the specific language governing permissions and
         | 
| 15 | 
            +
            # limitations under the License.
         | 
| 16 | 
            +
            #
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            require 'spec_helper'
         | 
| 19 | 
            +
            require 'chef-dk/policyfile_services/undelete'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            describe ChefDK::PolicyfileServices::Undelete do
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              let(:chef_config) { double("Chef::Config") }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              let(:ui) { TestHelpers::TestUI.new }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              let(:policy_name) { nil }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              let(:policy_group) { nil }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              let(:show_orphans) { false }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              let(:summary_diff) { false }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              let(:undo_dir) { tempdir }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              let(:undo_record_id) { nil }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              subject(:undelete_service) do
         | 
| 40 | 
            +
                described_class.new(config: chef_config,
         | 
| 41 | 
            +
                                    ui: ui,
         | 
| 42 | 
            +
                                    undo_record_id: undo_record_id)
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              describe "listing undo operations" do
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                before do
         | 
| 48 | 
            +
                  allow(undelete_service.undo_stack).to receive(:undo_dir).and_return(undo_dir)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                after do
         | 
| 52 | 
            +
                  clear_tempdir
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                context "when the undo dir doesn't exist" do
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  let(:undo_dir) { File.join(tempdir, "this", "isnt", "here") }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  it "prints a message saying there aren't any things to undo to stderr" do
         | 
| 60 | 
            +
                    undelete_service.list
         | 
| 61 | 
            +
                    expect(ui.output).to eq("Nothing to undo.\n")
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                context "when the undo dir exists, but it empty" do
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  it "prints a message saying there aren't any things to undo to stderr" do
         | 
| 69 | 
            +
                    undelete_service.list
         | 
| 70 | 
            +
                    expect(ui.output).to eq("Nothing to undo.\n")
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                context "when the undo dir exists and there are undo records in it" do
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  let(:policy_revision) do
         | 
| 78 | 
            +
                    {
         | 
| 79 | 
            +
                      "name" => "appserver",
         | 
| 80 | 
            +
                      "revision_id" => "1111111111111111111111111111111111111111111111111111111111111111"
         | 
| 81 | 
            +
                    }
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  let(:undo_record1) do
         | 
| 85 | 
            +
                    ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
         | 
| 86 | 
            +
                      undo_record.description = "delete-policy-group example1"
         | 
| 87 | 
            +
                      undo_record.add_policy_group("example1")
         | 
| 88 | 
            +
                      undo_record.add_policy_revision("appserver", "example1", policy_revision)
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  let(:undo_record2) do
         | 
| 93 | 
            +
                    ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
         | 
| 94 | 
            +
                      undo_record.description = "delete-policy-group example2"
         | 
| 95 | 
            +
                      undo_record.add_policy_group("example2")
         | 
| 96 | 
            +
                      undo_record.add_policy_revision("appserver", "example2", policy_revision)
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  let(:undo_record3) do
         | 
| 101 | 
            +
                    ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
         | 
| 102 | 
            +
                      undo_record.description = "delete-policy-group example3"
         | 
| 103 | 
            +
                      undo_record.add_policy_group("example3")
         | 
| 104 | 
            +
                      undo_record.add_policy_revision("appserver", "example3", policy_revision)
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  # `Time.new` is stubbed later on, need to force it to be evaluated before
         | 
| 109 | 
            +
                  # then.
         | 
| 110 | 
            +
                  let!(:start_time) { Time.new }
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  def next_time
         | 
| 113 | 
            +
                    @increment ||= 0
         | 
| 114 | 
            +
                    @increment += 1
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    start_time + @increment
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  let(:times) { [] }
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  before do
         | 
| 122 | 
            +
                    allow(Time).to receive(:new) do
         | 
| 123 | 
            +
                      t = next_time
         | 
| 124 | 
            +
                      times << t
         | 
| 125 | 
            +
                      t
         | 
| 126 | 
            +
                    end
         | 
| 127 | 
            +
                    undo_stack = ChefDK::Policyfile::UndoStack.new
         | 
| 128 | 
            +
                    allow(undo_stack).to receive(:undo_dir).and_return(undo_dir)
         | 
| 129 | 
            +
                    undo_stack.push(undo_record1).push(undo_record2).push(undo_record3)
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  it "prints the items in reverse chronological order" do
         | 
| 133 | 
            +
                    undelete_service.list
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    timestamps = times.map { |t| t.utc.strftime("%Y%m%d%H%M%S") }
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    expected_output = <<-OUTPUT
         | 
| 138 | 
            +
            #{timestamps[2]}: delete-policy-group example3
         | 
| 139 | 
            +
            #{timestamps[1]}: delete-policy-group example2
         | 
| 140 | 
            +
            #{timestamps[0]}: delete-policy-group example1
         | 
| 141 | 
            +
            OUTPUT
         | 
| 142 | 
            +
                    expect(ui.output).to eq(expected_output)
         | 
| 143 | 
            +
                  end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
              end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
              describe "undoing a policy group delete" do
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                let(:policy_revision) do
         | 
| 152 | 
            +
                  {
         | 
| 153 | 
            +
                    "name" => "appserver",
         | 
| 154 | 
            +
                    "revision_id" => "1111111111111111111111111111111111111111111111111111111111111111"
         | 
| 155 | 
            +
                  }
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                let(:undo_record1) do
         | 
| 159 | 
            +
                  ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
         | 
| 160 | 
            +
                    undo_record.description = "delete-policy-group example1"
         | 
| 161 | 
            +
                    undo_record.add_policy_group("example1")
         | 
| 162 | 
            +
                    undo_record.add_policy_revision("appserver", "example1", policy_revision)
         | 
| 163 | 
            +
                  end
         | 
| 164 | 
            +
                end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                let(:undo_stack) do
         | 
| 167 | 
            +
                  instance_double(ChefDK::Policyfile::UndoStack).tap do |s|
         | 
| 168 | 
            +
                    allow(s).to receive(:pop).and_yield(undo_record1)
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                before do
         | 
| 175 | 
            +
                  allow(undelete_service).to receive(:http_client).and_return(http_client)
         | 
| 176 | 
            +
                  allow(undelete_service).to receive(:undo_stack).and_return(undo_stack)
         | 
| 177 | 
            +
                end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                describe "when an error occurs posting data to the server" do
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  let(:response) do
         | 
| 182 | 
            +
                    Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
         | 
| 183 | 
            +
                      r.instance_variable_set(:@body, "oops")
         | 
| 184 | 
            +
                    end
         | 
| 185 | 
            +
                  end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                  let(:http_exception) do
         | 
| 188 | 
            +
                    begin
         | 
| 189 | 
            +
                      response.error!
         | 
| 190 | 
            +
                    rescue => e
         | 
| 191 | 
            +
                      e
         | 
| 192 | 
            +
                    end
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  before do
         | 
| 196 | 
            +
                    expect(http_client).to receive(:put).
         | 
| 197 | 
            +
                      with("/policy_groups/example1/policies/appserver", policy_revision).
         | 
| 198 | 
            +
                      and_raise(http_exception)
         | 
| 199 | 
            +
                  end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                  it "raises an error" do
         | 
| 202 | 
            +
                    expect { undelete_service.run }.to raise_error(ChefDK::UndeleteError)
         | 
| 203 | 
            +
                  end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                context "when the undelete is successful" do
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                  before do
         | 
| 210 | 
            +
                    expect(http_client).to receive(:put).
         | 
| 211 | 
            +
                      with("/policy_groups/example1/policies/appserver", policy_revision)
         | 
| 212 | 
            +
                  end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  it "uploads all policies to the server" do
         | 
| 215 | 
            +
                    undelete_service.run
         | 
| 216 | 
            +
                    expect(ui.output).to eq("Restored policy 'appserver'\nRestored policy group 'example1'\n")
         | 
| 217 | 
            +
                  end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                context "when given a specific undo record id to undo" do
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                  let(:undo_record_id) { "20150827172127" }
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                  context "and the id doesn't exist" do
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    before do
         | 
| 228 | 
            +
                      expect(undo_stack).to receive(:has_id?).with(undo_record_id).and_return(false)
         | 
| 229 | 
            +
                    end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    it "prints an error message that the id doesn't exist" do
         | 
| 232 | 
            +
                      undelete_service.run
         | 
| 233 | 
            +
                      expect(ui.output).to eq("No undo record with id '#{undo_record_id}' exists\n")
         | 
| 234 | 
            +
                    end
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                  end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                  context "and the id exists" do
         | 
| 239 | 
            +
                    before do
         | 
| 240 | 
            +
                      expect(undo_stack).to receive(:has_id?).with(undo_record_id).and_return(true)
         | 
| 241 | 
            +
                      expect(undo_stack).to receive(:delete).with(undo_record_id).and_yield(undo_record1)
         | 
| 242 | 
            +
                      expect(http_client).to receive(:put).
         | 
| 243 | 
            +
                        with("/policy_groups/example1/policies/appserver", policy_revision)
         | 
| 244 | 
            +
                    end
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                    it "uploads all policies to the server" do
         | 
| 247 | 
            +
                      undelete_service.run
         | 
| 248 | 
            +
                      expect(ui.output).to eq("Restored policy 'appserver'\nRestored policy group 'example1'\n")
         | 
| 249 | 
            +
                    end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                  end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
              end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
              describe "undoing a policy delete" do
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                let(:policy_revision) do
         | 
| 260 | 
            +
                  {
         | 
| 261 | 
            +
                    "name" => "appserver",
         | 
| 262 | 
            +
                    "revision_id" => "1111111111111111111111111111111111111111111111111111111111111111"
         | 
| 263 | 
            +
                  }
         | 
| 264 | 
            +
                end
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                let(:undo_record1) do
         | 
| 267 | 
            +
                  ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
         | 
| 268 | 
            +
                    undo_record.description = "delete-policy-group example1"
         | 
| 269 | 
            +
                    undo_record.add_policy_revision("appserver", nil, policy_revision)
         | 
| 270 | 
            +
                  end
         | 
| 271 | 
            +
                end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                let(:undo_stack) do
         | 
| 274 | 
            +
                  instance_double(ChefDK::Policyfile::UndoStack).tap do |s|
         | 
| 275 | 
            +
                    allow(s).to receive(:pop).and_yield(undo_record1)
         | 
| 276 | 
            +
                  end
         | 
| 277 | 
            +
                end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                before do
         | 
| 282 | 
            +
                  allow(undelete_service).to receive(:http_client).and_return(http_client)
         | 
| 283 | 
            +
                  allow(undelete_service).to receive(:undo_stack).and_return(undo_stack)
         | 
| 284 | 
            +
                end
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                context "when the revision to create doesn't exist" do
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                  before do
         | 
| 289 | 
            +
                    expect(http_client).to receive(:post).
         | 
| 290 | 
            +
                      with("/policies/appserver/revisions", policy_revision)
         | 
| 291 | 
            +
                  end
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                  it "uploads all policies to the server" do
         | 
| 294 | 
            +
                    undelete_service.run
         | 
| 295 | 
            +
                    expect(ui.output).to eq("Restored policy 'appserver'\n")
         | 
| 296 | 
            +
                  end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                end
         | 
| 299 | 
            +
             | 
| 300 | 
            +
              end
         | 
| 301 | 
            +
             | 
| 302 | 
            +
             | 
| 303 | 
            +
            end
         | 
| 304 | 
            +
             |