crowbar/crowbar-ha

View on GitHub
chef/cookbooks/pacemaker/spec/providers/primitive_spec.rb

Summary

Maintainability
A
45 mins
Test Coverage
require "spec_helper"

require_relative "../helpers/runnable_resource"
require_relative "../fixtures/keystone_primitive"

describe "Chef::Provider::PacemakerPrimitive" do
  # for use inside examples:
  let(:fixture) { Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE }
  # for use outside examples (e.g. when invoking shared_examples)
  fixture = Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE

  def lwrp_name
    "primitive"
  end

  include_context "a Pacemaker LWRP with artificially constructed resource"

  before(:each) do
    @resource.agent fixture.agent
    @resource.params Hash[fixture.params]
    @resource.meta Hash[fixture.meta]
    @resource.op Hash[fixture.op]
  end

  def cib_object_class
    Pacemaker::Resource::Primitive
  end

  include Chef::RSpec::Pacemaker::Mocks

  shared_examples "an updateable resource" do
    include Chef::RSpec::Pacemaker::CIBObject

    it "should modify the primitive if it has different params" do
      expected_configure_cmd_args = [
        %'--set-parameter os_password ' +
          '--parameter-value \$ne\!w\%pa\\\'ss\&wo\"rd',
        %'--delete-parameter os_tenant_name'
      ].map { |args| "crm_resource --resource #{fixture.name} #{args}" }
      test_modify(expected_configure_cmd_args) do
        new_params = Hash[fixture.params].merge(
          "os_password" => %{$ne!w%pa'ss&wo"rd})
        new_params.delete("os_tenant_name")
        @resource.params new_params
        @resource.meta Hash[fixture.meta].merge("target-role" => "Stopped")
      end
    end

    def assert_no_modifications(recipe_agent, existing_agent)
      @resource.name "vip"
      @resource.agent (recipe_agent)
      @resource.params({ "ip" => "192.168.138.84" })
      @resource.meta ({})
      @resource.op ({ "monitor" => { "interval" => "10s" }})

      # Set what crm configure show returns for the "existing" 'vip' primitive
      existing_definition = <<EOF.chomp
primitive vip #{existing_agent} \\
        params ip=192.168.138.84 \\
        meta \\
        op monitor interval=10s
EOF
      mock_existing_cib_object("vip", existing_definition)

      provider.run_action :create

      expect(@chef_run).not_to run_execute(/.*/)
      expect(@resource).not_to be_updated
    end

    it "should not modify primitive if crmsh drops ocf:heartbeat: prefix" do
      assert_no_modifications("ocf:heartbeat:IPaddr2", "IPaddr2")
    end

    it "should not modify primitive if recipe drops ocf:heartbeat: prefix" do
      assert_no_modifications("IPaddr2", "ocf:heartbeat:IPaddr2")
    end

    it "should modify the primitive if it has different meta" do
      expected_configure_cmd_args = [
        %'--set-parameter is-managed --parameter-value false --meta'
      ].map { |args| "crm_resource --resource #{fixture.name} #{args}" }
      test_modify(expected_configure_cmd_args) do
        @resource.params Hash[fixture.params]
        @resource.meta Hash[fixture.meta].merge("is-managed" => "false")
      end
    end

    it "should modify the primitive if it has different params and meta" do
      expected_configure_cmd_args = [
        %'--set-parameter os_password --parameter-value newpasswd',
        %'--delete-parameter os_tenant_name',
        %'--set-parameter is-managed --parameter-value false --meta'
      ].map { |args| "crm_resource --resource #{fixture.name} #{args}" }
      test_modify(expected_configure_cmd_args) do
        new_params = Hash[fixture.params].merge("os_password" => "newpasswd")
        new_params.delete("os_tenant_name")
        @resource.params new_params
        @resource.meta Hash[fixture.meta].merge("is-managed" => "false")
      end
    end

    it "should modify the primitive if it has different op values" do
      expected_configure_cmd_args = [
        fixture.reconfigure_command.gsub("240", "120")
      ]
      test_modify(expected_configure_cmd_args) do
        new_op = Hash[fixture.op]
        # Ensure we're not modifying our expectation as well as the input
        new_op["start"] = new_op["start"].dup
        new_op["start"]["timeout"] = "120"
        @resource.op new_op
      end
    end
  end

  describe ":update action" do
    let(:action) { :update }
    it_should_behave_like "an updateable resource"
  end

  describe ":create action" do
    let(:action) { :create }
    it_should_behave_like "an updateable resource"

    context "creation from scratch" do
      include_context "stopped resource"

      it "should create a primitive if it doesn't already exist" do
        # The first time, Mixlib::ShellOut needs to return an empty definition.
        # Then the resource gets created so the second time it needs to return
        # the definition used for creation.
        mock_nonexistent_cib_object(fixture.name)
        mock_existing_cib_object_from_fixture(fixture)

        provider.run_action :create

        expect(@chef_run).to run_execute(stopped_fixture.configure_command)
        expect(@resource).to be_updated
      end

      it "should barf if crm fails to create the primitive" do
        double1 = nonexistent_cib_object_double(fixture.name)
        double2 = shellout_double(
          command: fixture.configure_command,
          stderr: "ERROR: crm configure failed",
          exitstatus: 1
        )
        stub_shellouts(double1, double2)

        expect { provider.run_action :create }.to \
          raise_error(RuntimeError, "Failed to create #{fixture}")

        expect(@chef_run).to run_execute(stopped_fixture.configure_command)
        expect(@resource).not_to be_updated
      end

      # This scenario seems rather artificial and unlikely, but it doesn't
      # do any harm to test it.
      it "should barf if crm creates a primitive with empty definition" do
        mock_nonexistent_cib_object(fixture.name)
        mock_nonexistent_cib_object(fixture.name)

        expect { provider.run_action :create }.to \
          raise_error(RuntimeError, "Failed to create #{fixture}")

        expect(@chef_run).to run_execute(stopped_fixture.configure_command)
        expect(@resource).not_to be_updated
      end
    end

    it "should barf if the primitive is already defined with the wrong agent" do
      existing_agent = "ocf:openstack:something-else"
      definition = fixture.definition.sub(fixture.agent, existing_agent)
      mock_existing_cib_object(fixture.name, definition)

      expected_error = \
        "Existing #{fixture} has agent '#{existing_agent}' " \
        "but recipe wanted '#{@resource.agent}'"
      expect { provider.run_action :create }.to \
        raise_error(RuntimeError, expected_error)

      expect(@resource).not_to be_updated
    end
  end

  it_should_behave_like "a runnable resource", fixture
end