Module: CemAcpt::SpecHelperAcceptance

Defined in:
lib/cem_acpt/spec_helper_acceptance.rb

Overview

This module provides methods used in accpetance tests.

Instance Method Summary collapse

Instance Method Details

#apply_manifest(manifest, opts = {}) ⇒ Object

This method runs puppet apply on the test node using the provided manifest.



157
158
159
160
161
162
163
164
165
166
# File 'lib/cem_acpt/spec_helper_acceptance.rb', line 157

def apply_manifest(manifest, opts = {})
  host = RSpec.configuration.acpt_test_data[:node_name]
  opts = {
    apply: puppet_apply_options(opts),
    ssh_opts: RSpec.configuration.acpt_test_data[:node_data][:ssh_opts],
    puppet_path: RSpec.configuration.acpt_test_data[:puppet_path],
  }
  result = RSpec.configuration.acpt_test_data[:platform].apply_manifest(host, manifest, opts)
  handle_puppet_apply_output(result, opts[:apply])
end

#handle_puppet_apply_output(result, apply_opts) {|result| ... } ⇒ Object

This methods handles formatting the output from Puppet apply.

Yields:

  • (result)


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/cem_acpt/spec_helper_acceptance.rb', line 112

def handle_puppet_apply_output(result, apply_opts)
  exit_code = result.exitstatus
  output = result.to_s
  if apply_opts[:catch_changes] && !apply_opts[:acceptable_exit_codes].include?(exit_code)
    failure = <<~ERROR
      Apply manifest expected no changes. Puppet Apply returned exit code #{exit_code}
      ====== Start output of Puppet apply with unexpected changes ======
      #{output}
      ====== End output of Puppet apply with unexpected changes ======
    ERROR
    raise failure
  elsif !apply_opts[:acceptable_exit_codes].include?(exit_code)
    failure = <<~ERROR
      Apply manifest failed with exit code #{exit_code} (expected: #{apply_opts[:acceptable_exit_codes]})
      ====== Start output of failed Puppet apply ======
      #{output}
      ====== End output of failed Puppet apply ======
    ERROR
    raise failure
  end

  yield result if block_given?

  if ENV['RSPEC_DEBUG']
    run_result = <<~RUNRES
      apply manifest succeded with status #{exit_code}
      ===== Start output of successful Puppet apply ======
      #{output}
      ===== End output of successful Puppet apply ======
    RUNRES
    puts run_result
  end
  result
end

#idempotent_apply(manifest, opts = {}) ⇒ Object

This method runs puppet apply on the test node using the provided manifest twice and asserts that the second run has no changes.



170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/cem_acpt/spec_helper_acceptance.rb', line 170

def idempotent_apply(manifest, opts = {})
  opts.reject! { |k, _| %i[catch_changes expect_changes catch_failures expect_failures].include?(k) }
  begin
    apply_manifest(manifest, opts.merge({ catch_failures: true }))
  rescue StandardError => e
    raise "Idempotent apply failed during first apply: #{e.message}\n#{e.backtrace.join("\n")}"
  end
  begin
    apply_manifest(manifest, opts.merge({ catch_changes: true }))
  rescue StandardError => e
    raise "Idempotent apply failed during second apply: #{e.message}\n#{e.backtrace.join("\n")}"
  end
end

#initialize_test_environment!Object

This method must be used inside of a `describe` block as the first statement. This prepares the ServerSpec configuration for the test node and sets up the test data.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/cem_acpt/spec_helper_acceptance.rb', line 32

def initialize_test_environment!
  test_file = caller_locations.first.path

  node_name, node_data = RSpec.configuration.acpt_node_inventory.get_by_property('test_data.test_file', test_file)
  raise "Failed to get node data for node #{node_name}" unless node_data
  raise "Node data format is incorrect: #{node_data}" unless node_data.is_a?(Hash)

  backend = nil
  host = nil
  ssh_options = nil
  puppet_path = nil

  # Set remote communication variables based on transport type
  case node_data[:transport]
  when /ssh/
    backend = :ssh
    host = node_data[:node_name]
    ssh_options = node_data[:ssh_opts]
    sudo_options = '-n -u root -i'
    puppet_path = '/opt/puppetlabs/bin/puppet'
  when /winrm/
    backend = :winrm
    puppet_path = 'C:\Program Files\Puppet Labs\Puppet\bin\puppet.bat'
  else
    raise "Unknown transport: #{node[:transport]}"
  end

  # Set serverspec transport options and host for remote communication.
  set :backend, backend
  set :host, host
  set(:ssh_options, ssh_options) if ssh_options
  set(:sudo_options, sudo_options) if sudo_options
  set(:os, family: 'windows') if backend == :winrm

  # Get the command provider from the node's platform
  # We add this as a RSpec config option so that we can use it in
  # other functions.
  require 'cem_acpt/platform'

  acpt_test_data = {
    test_file: test_file,
    node_name: node_name,
    node_data: node_data,
    backend: backend,
    platform: CemAcpt::Platform.get(node_data[:platform]),
    puppet_path: puppet_path,
  }
  acpt_test_data[:host] = host if host
  RSpec.configuration.acpt_test_data = acpt_test_data
end

#puppet_apply_options(opts = {}) ⇒ Object

This method formats Puppet Apply options



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/cem_acpt/spec_helper_acceptance.rb', line 84

def puppet_apply_options(opts = {})
  if [opts[:catch_changes], opts[:expect_changes], opts[:catch_failures], opts[:expect_failures]].compact.length > 1
    raise ArgumentError,
          'Please specify only one of "catch_changes", "expect_changes", "catch_failures", or "expect_failures"'
  end

  apply_opts = {}.merge(opts)

  if opts[:catch_changes]
    apply_opts[:detailed_exit_codes] = true
    apply_opts[:acceptable_exit_codes] = [0]
  elsif opts[:catch_failures]
    apply_opts[:detailed_exit_codes] = true
    apply_opts[:acceptable_exit_codes] = [0, 2]
  elsif opts[:expect_failures]
    apply_opts[:detailed_exit_codes] = true
    apply_opts[:acceptable_exit_codes] = [1, 4, 6]
  elsif opts[:expect_changes]
    apply_opts[:detailed_exit_codes] = true
    apply_opts[:acceptable_exit_codes] = [2]
  else
    apply_opts[:detailed_exit_codes] = false
    apply_opts[:acceptable_exit_codes] = [0]
  end
  apply_opts
end

#run_shell(cmd, opts = {}) ⇒ Object

This method runs a shell command on the test node.



148
149
150
151
152
153
154
# File 'lib/cem_acpt/spec_helper_acceptance.rb', line 148

def run_shell(cmd, opts = {})
  cmd = cmd.join(' ') if cmd.is_a?(Array)

  host = RSpec.configuration.acpt_test_data[:node_name]
  opts[:ssh_opts] = RSpec.configuration.acpt_test_data[:node_data][:ssh_opts]
  RSpec.configuration.acpt_test_data[:platform].run_shell(host, cmd, opts)
end