kitchen-terraform 3.3.1 → 4.0.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +45 -28
  5. data/lib/kitchen/driver/terraform.rb +87 -66
  6. data/lib/kitchen/provisioner/terraform.rb +1 -1
  7. data/lib/kitchen/terraform/client_version_verifier.rb +3 -3
  8. data/lib/kitchen/terraform/command/output.rb +43 -40
  9. data/lib/kitchen/{verifier/terraform/configure_inspec_runner_host.rb → terraform/config_attribute/systems.rb} +18 -11
  10. data/lib/kitchen/terraform/config_attribute/variable_files.rb +1 -1
  11. data/lib/kitchen/terraform/config_schemas/system.rb +555 -0
  12. data/lib/kitchen/terraform/{breaking/kitchen_instance.rb → config_schemas/systems.rb} +16 -5
  13. data/lib/kitchen/terraform/configurable.rb +2 -6
  14. data/lib/kitchen/terraform/inspec.rb +74 -0
  15. data/lib/kitchen/terraform/inspec_options_mapper.rb +49 -0
  16. data/lib/kitchen/terraform/inspec_with_hosts.rb +49 -0
  17. data/lib/kitchen/terraform/inspec_without_hosts.rb +44 -0
  18. data/lib/kitchen/terraform/shell_out.rb +13 -10
  19. data/lib/kitchen/terraform/system.rb +120 -0
  20. data/lib/kitchen/terraform/system_attrs_resolver.rb +57 -0
  21. data/lib/kitchen/terraform/system_hosts_resolver.rb +45 -0
  22. data/lib/kitchen/terraform/version.rb +60 -17
  23. data/lib/kitchen/verifier/terraform.rb +162 -156
  24. metadata +45 -41
  25. metadata.gz.sig +0 -0
  26. data/lib/kitchen/terraform/config_attribute/groups.rb +0 -148
  27. data/lib/kitchen/terraform/config_schemas/groups.rb +0 -52
  28. data/lib/kitchen/terraform/deprecating/kitchen_instance.rb +0 -61
  29. data/lib/kitchen/terraform/kitchen_instance.rb +0 -49
  30. data/lib/kitchen/verifier/terraform/configure_inspec_runner_attributes.rb +0 -98
  31. data/lib/kitchen/verifier/terraform/configure_inspec_runner_backend.rb +0 -32
  32. data/lib/kitchen/verifier/terraform/configure_inspec_runner_controls.rb +0 -41
  33. data/lib/kitchen/verifier/terraform/configure_inspec_runner_port.rb +0 -40
  34. data/lib/kitchen/verifier/terraform/configure_inspec_runner_ssh_key.rb +0 -41
  35. data/lib/kitchen/verifier/terraform/configure_inspec_runner_user.rb +0 -40
  36. data/lib/kitchen/verifier/terraform/enumerate_groups_and_hostnames.rb +0 -82
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2016 New Context Services, Inc.
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
+ require "kitchen/terraform"
18
+ require "kitchen/terraform/error"
19
+
20
+ module Kitchen
21
+ module Terraform
22
+ # SystemAttrsResolver is the class of objects which resolve for systems the attrs which are contained in outputs.
23
+ class SystemAttrsResolver
24
+ # #resolve resolves the attrs.
25
+ #
26
+ # @param attrs_outputs_keys [::Array<::String>] the names of the InSpec attributes.
27
+ # @param attrs_outputs_values [::Array<::String>] the names of the Terraform outputs.
28
+ # @param system [::Kitchen::Terraform::System] the system.
29
+ # @raise [::Kitchen::Terraform::Error] if the fetching the value of the output fails.
30
+ def resolve(attrs_outputs_keys:, attrs_outputs_values:, system:)
31
+ system.add_attrs attrs: @outputs.merge(
32
+ attrs_outputs_keys.lazy.map(&:to_s).zip(@outputs.fetch_values(*attrs_outputs_values)).to_h
33
+ )
34
+
35
+ self
36
+ rescue ::KeyError => key_error
37
+ raise ::Kitchen::Terraform::Error,
38
+ "Resolving the attrs of system #{system} failed\n#{key_error}"
39
+ end
40
+
41
+ private
42
+
43
+ # #initialize prepares the instance to be used.
44
+ #
45
+ # @param outputs [#to_hash] the outputs of the Terraform state under test.
46
+ def initialize(outputs:)
47
+ @outputs = Hash[outputs].inject({}) do |hash, (key, value)|
48
+ hash.store key, value.fetch("value")
49
+
50
+ hash
51
+ end
52
+ rescue ::KeyError => key_error
53
+ raise ::Kitchen::Terraform::Error, "Preparing to resolve attrs failed\n#{key_error}"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2016 New Context Services, Inc.
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
+ require "kitchen/terraform"
18
+ require "kitchen/terraform/error"
19
+
20
+ module Kitchen
21
+ module Terraform
22
+ # SystemHostsResolver is the class of objects which resolve for systems the hosts which are contained in outputs.
23
+ class SystemHostsResolver
24
+ # #resolve resolves the hosts.
25
+ #
26
+ # @param hosts_output [::String] the name of the Terraform output which has a value of hosts for the system.
27
+ # @param system [::Kitchen::Terraform::System] the system.
28
+ # @raise [::Kitchen::Terraform::Error] if the fetching the value of the output fails.
29
+ def resolve(hosts_output:, system:)
30
+ system.add_hosts hosts: @outputs.fetch(hosts_output).fetch("value")
31
+ rescue ::KeyError => key_error
32
+ raise ::Kitchen::Terraform::Error, "Resolving the hosts of system #{system} failed\n#{key_error}"
33
+ end
34
+
35
+ private
36
+
37
+ # #initialize prepares the instance to be used.
38
+ #
39
+ # @param outputs [#to_hash] the outputs of the Terraform state under test.
40
+ def initialize(outputs:)
41
+ @outputs = Hash[outputs]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -17,26 +17,69 @@
17
17
  require "rubygems"
18
18
  require "kitchen/terraform"
19
19
 
20
- # The version of the kitchen-terraform gem.
21
- class ::Kitchen::Terraform::Version
22
- def assign_specification_version(specification:)
23
- specification.version = @version.to_s
24
- self
25
- end
20
+ # Kitchen::Terraform::Version represents the version of the Kitchen-Terraform gem. The module can send the version to
21
+ # different containers as well as conditionally yield to blocks based on version requirements.
22
+ module ::Kitchen::Terraform::Version
23
+ class << self
24
+ # assign_plugin_version assigns the version to a class which includes Kitchen::Configurable.
25
+ #
26
+ # @param configurable_class [::Kitchen::Configurable] the configurable class to which the version will be assigned.
27
+ # @return [self]
28
+ def assign_plugin_version(configurable_class:)
29
+ configurable_class.plugin_version value.to_s
30
+ self
31
+ end
26
32
 
27
- def assign_plugin_version(configurable_class:)
28
- configurable_class.plugin_version @version.to_s
29
- self
30
- end
33
+ # assign_specification_version assigns the version to a Gem::Specification.
34
+ #
35
+ # @param specification [::Gem::Specification] the specification to which the version will be assigned.
36
+ # @return [self]
37
+ def assign_specification_version(specification:)
38
+ specification.version = value
39
+ self
40
+ end
31
41
 
32
- def if_satisfies(requirement:)
33
- yield if requirement.satisfied_by? @version
34
- self
35
- end
42
+ # if_satisfies yields control if the provided requirement is satisfied by the version.
43
+ #
44
+ # @param requirement [::Gem::Requirement, ::String] the requirement to be satisfied by the version.
45
+ # @raise [::Gem::Requirement::BadRequirementError] if the requirement is illformed.
46
+ # @return [self]
47
+ # @yield [] if the requirement is satisfied by the version.
48
+ def if_satisfies(requirement:)
49
+ yield if
50
+ ::Gem::Requirement
51
+ .new(requirement).satisfied_by? value
52
+
53
+ self
54
+ end
55
+
56
+ # temporarily_override overrides the current version with the version provided, yields control, and then resets the
57
+ # version.
58
+ #
59
+ # @note temporarily_override must only be used in tests to validate version flow control logic.
60
+ # @raise [::ArgumentError] if the version is malformed.
61
+ # @return [self]
62
+ # @yield [] the value of the version will be overridden while control is yielded.
63
+ def temporarily_override(version:)
64
+ current_value = value
65
+ self.value = version
66
+ yield
67
+ self.value = current_value
68
+ self
69
+ end
70
+
71
+ private
36
72
 
37
- private
73
+ # @api private
74
+ def value
75
+ self.value = ::Gem::Version.new "4.0.0" if not @value
76
+ @value
77
+ end
38
78
 
39
- def initialize(version: "3.3.1")
40
- @version = ::Gem::Version.new version
79
+ # @api private
80
+ def value=(version)
81
+ @value = ::Gem::Version.new version
82
+ self
83
+ end
41
84
  end
42
85
  end
@@ -16,175 +16,181 @@
16
16
 
17
17
  require "kitchen"
18
18
  require "kitchen/terraform/config_attribute/color"
19
- require "kitchen/terraform/config_attribute/groups"
19
+ require "kitchen/terraform/config_attribute/systems"
20
20
  require "kitchen/terraform/configurable"
21
21
  require "kitchen/terraform/error"
22
- require "kitchen/verifier/inspec"
22
+ require "kitchen/terraform/inspec_options_mapper"
23
+ require "kitchen/terraform/system_attrs_resolver"
24
+ require "kitchen/terraform/system_hosts_resolver"
23
25
 
24
- # This namespace is defined by Kitchen.
25
- #
26
- # @see http://www.rubydoc.info/gems/test-kitchen/Kitchen/Verifier
27
- module ::Kitchen::Verifier
28
- end
26
+ module Kitchen
27
+ # This namespace is defined by Kitchen.
28
+ #
29
+ # @see https://www.rubydoc.info/gems/test-kitchen/Kitchen/Verifier
30
+ module Verifier
31
+ # The verifier utilizes the {https://www.inspec.io/ InSpec infrastructure testing framework} to verify the behaviour and
32
+ # state of resources in the Terraform state.
33
+ #
34
+ # === Commands
35
+ #
36
+ # The following command-line commands are provided by the verifier.
37
+ #
38
+ # ==== kitchen verify
39
+ #
40
+ # A Test Kitchen instance is verified by iterating through the systems and executing the associated InSpec controls
41
+ # against the hosts of each system.
42
+ #
43
+ # === Configuration Attributes
44
+ #
45
+ # The configuration attributes of the verifier control the behaviour of the InSpec runner. Within the
46
+ # {http://kitchen.ci/docs/getting-started/kitchen-yml Test Kitchen configuration file}, these attributes must be
47
+ # declared in the +verifier+ mapping along with the plugin name.
48
+ #
49
+ # verifier:
50
+ # name: terraform
51
+ # a_configuration_attribute: some value
52
+ #
53
+ # ==== color
54
+ #
55
+ # {include:Kitchen::Terraform::ConfigAttribute::Color}
56
+ #
57
+ # ==== systems
58
+ #
59
+ # {include:Kitchen::Terraform::ConfigAttribute::Systems}
60
+ #
61
+ # === Ruby Interface
62
+ #
63
+ # This class implements the interface of Kitchen::Configurable which requires the following Reek suppressions:
64
+ # :reek:PrimaDonnaMethod { exclude: [ finalize_config!, load_needed_dependencies! ] }
65
+ class Terraform
66
+ include ::Kitchen::Configurable
67
+ include ::Kitchen::Logging
68
+ include ::Kitchen::Terraform::ConfigAttribute::Color
69
+ include ::Kitchen::Terraform::ConfigAttribute::Systems
70
+ include ::Kitchen::Terraform::Configurable
71
+ @api_version = 2
29
72
 
30
- # The verifier utilizes the {https://www.inspec.io/ InSpec infrastructure testing framework} to verify the behaviour and
31
- # state of resources in the Terraform state.
32
- #
33
- # === Commands
34
- #
35
- # The following command-line commands are provided by the verifier.
36
- #
37
- # ==== kitchen verify
38
- #
39
- # A Test Kitchen instance is verified by iterating through the groups and executing the associated InSpec controls in a
40
- # manner similar to the following command-line command.
41
- #
42
- # inspec exec \
43
- # [--attrs=<terraform_outputs>] \
44
- # --backend=<ssh|local> \
45
- # [--no-color] \
46
- # [--controls=<group.controls>] \
47
- # --host=<group.hostnames.current|localhost> \
48
- # [--password=<group.password>] \
49
- # [--port=<group.port>] \
50
- # --profiles-path=test/integration/<suite> \
51
- # [--user=<group.username>] \
52
- #
53
- # === InSpec Profiles
54
- #
55
- # The {https://www.inspec.io/docs/reference/profiles/ InSpec profile} for a Test Kitchen suite must be defined under
56
- # +./test/integration/<suite>/+.
57
- #
58
- # === Configuration Attributes
59
- #
60
- # The configuration attributes of the verifier control the behaviour of the InSpec runner. Within the
61
- # {http://kitchen.ci/docs/getting-started/kitchen-yml Test Kitchen configuration file}, these attributes must be
62
- # declared in the +verifier+ mapping along with the plugin name.
63
- #
64
- # verifier:
65
- # name: terraform
66
- # a_configuration_attribute: some value
67
- #
68
- # ==== color
69
- #
70
- # {include:Kitchen::Terraform::ConfigAttribute::Color}
71
- #
72
- # ==== groups
73
- #
74
- # {include:Kitchen::Terraform::ConfigAttribute::Groups}
75
- #
76
- # @version 2
77
- class ::Kitchen::Verifier::Terraform < ::Kitchen::Verifier::Inspec
78
- kitchen_verifier_api_version 2
73
+ # The verifier enumerates through each host of each system and verifies the associated InSpec controls.
74
+ #
75
+ # @example
76
+ # `kitchen verify suite-name`
77
+ # @param kitchen_state [::Hash] the mutable instance and verifier state.
78
+ # @raise [::Kitchen::ActionFailed] if the result of the action is a failure.
79
+ # @return [void]
80
+ def call(kitchen_state)
81
+ load_outputs kitchen_state: kitchen_state
82
+ config_systems.each do |system|
83
+ verify system: system
84
+ end
85
+ rescue ::Kitchen::Terraform::Error => error
86
+ raise ::Kitchen::ActionFailed, error.message
87
+ end
88
+
89
+ # doctor checks the system and configuration for common errors.
90
+ #
91
+ # @param _kitchen_state [::Hash] the mutable Kitchen instance state.
92
+ # @return [Boolean] +true+ if any errors are found; +false+ if no errors are found.
93
+ # @see https://github.com/test-kitchen/test-kitchen/blob/v1.21.2/lib/kitchen/verifier/base.rb#L85-L91
94
+ def doctor(_kitchen_state)
95
+ false
96
+ end
79
97
 
80
- include ::Kitchen::Terraform::ConfigAttribute::Color
98
+ # finalize_config! configures InSpec options which remain consistent between systems.
99
+ #
100
+ # @param kitchen_instance [::Kitchen::Instance] an associated Kitchen instance.
101
+ # @return [self]
102
+ def finalize_config!(kitchen_instance)
103
+ super kitchen_instance
104
+ configure_inspec_connection_options
105
+ configure_inspec_miscellaneous_options
106
+ end
81
107
 
82
- include ::Kitchen::Terraform::ConfigAttribute::Groups
108
+ private
83
109
 
84
- include ::Kitchen::Terraform::Configurable
110
+ def configure_inspec_connection_options
111
+ @inspec_options.merge! transport_connection_options
85
112
 
86
- # The verifier enumerates through each hostname of each group and verifies the associated InSpec controls.
87
- #
88
- # @example
89
- # `kitchen verify suite-name`
90
- # @param state [::Hash] the mutable instance and verifier state.
91
- # @raise [::Kitchen::ActionFailed] if the result of the action is a failure.
92
- # @return [void]
93
- def call(state)
94
- state
95
- .fetch :kitchen_terraform_output do
96
- raise(
97
- ::Kitchen::Terraform::Error,
98
- "The Test Kitchen state does not include :kitchen_terraform_output; this implies that the " \
99
- "kitchen-terraform provisioner has not successfully converged"
113
+ @inspec_options
114
+ .store(
115
+ "connection_timeout",
116
+ @inspec_options.delete("timeout")
117
+ )
118
+ end
119
+
120
+ def configure_inspec_miscellaneous_options
121
+ @inspec_options.merge!(
122
+ "color" => config_color,
123
+ "distinct_exit" => false,
124
+ "sudo" => false,
125
+ "sudo_command" => "sudo -E",
126
+ "sudo_options" => "",
100
127
  )
101
128
  end
102
- .tap do |output|
103
- ::Kitchen::Verifier::Terraform::EnumerateGroupsAndHostnames
104
- .call(
105
- groups: config_groups,
106
- output: ::Kitchen::Util.stringified_hash(output)
107
- ) do |group:, hostname:|
108
- state
109
- .store(
110
- :kitchen_terraform_group,
111
- group
112
- )
113
- state
114
- .store(
115
- :kitchen_terraform_hostname,
116
- hostname
117
- )
118
- info "Verifying host '#{hostname}' of group '#{group.fetch :name}'"
119
- super state
120
- end
129
+
130
+ def configure_inspec_system_options(system:)
131
+ ::Kitchen::Terraform::InSpecOptionsMapper.new(system: system).map options: @inspec_options
121
132
  end
122
- rescue ::Kitchen::Terraform::Error => error
123
- raise(
124
- ::Kitchen::ActionFailed,
125
- error.message
126
- )
127
- end
128
133
 
129
- private
134
+ def load_outputs(kitchen_state:)
135
+ @outputs = ::Kitchen::Util.stringified_hash Hash kitchen_state.fetch :kitchen_terraform_outputs
136
+ rescue ::KeyError => key_error
137
+ raise ::Kitchen::Terraform::Error,
138
+ "Loading Terraform outputs from the Kitchen state failed; this implies that the " \
139
+ "Kitchen-Terraform provisioner has not successfully converged\n#{key_error}"
140
+ end
130
141
 
131
- # Modifies the Inspec Runner options generated by the kitchen-inspec verifier to support the verification of each
132
- # group's hosts.
133
- #
134
- # @api private
135
- # @return [::Hash] Inspec Runner options.
136
- # @see https://github.com/chef/inspec/blob/master/lib/inspec/runner.rb ::Inspec::Runner
137
- def runner_options(transport, state = {}, platform = nil, suite = nil)
138
- super(transport, state, platform, suite)
139
- .tap do |options|
140
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerBackend
141
- .call(
142
- hostname: state.fetch(:kitchen_terraform_hostname),
143
- options: options
144
- )
145
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerHost
146
- .call(
147
- hostname: state.fetch(:kitchen_terraform_hostname),
148
- options: options
149
- )
150
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerPort
151
- .call(
152
- group: state.fetch(:kitchen_terraform_group),
153
- options: options
154
- )
155
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerSSHKey
156
- .call(
157
- group: state.fetch(:kitchen_terraform_group),
158
- options: options
159
- )
160
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerUser
161
- .call(
162
- group: state.fetch(:kitchen_terraform_group),
163
- options: options
164
- )
165
- options
166
- .store(
167
- :attributes,
168
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerAttributes
169
- .call(
170
- group: state.fetch(:kitchen_terraform_group),
171
- output: ::Kitchen::Util.stringified_hash(state.fetch(:kitchen_terraform_output))
172
- )
173
- )
174
- ::Kitchen::Verifier::Terraform::ConfigureInspecRunnerControls
175
- .call(
176
- group: state.fetch(:kitchen_terraform_group),
177
- options: options
178
- )
142
+ def initialize(configuration = {})
143
+ init_config configuration
144
+ @inspec_options = {}
145
+ @outputs = {}
146
+ @transport_attributes = [
147
+ :compression, :compression_level, :connection_retries, :connection_retry_sleep, :connection_timeout,
148
+ :keepalive, :keepalive_interval, :max_wait_until_ready,
149
+ ]
150
+ end
151
+
152
+ def inspec_profile_path
153
+ @inspec_profile_path ||= ::File.join config.fetch(:test_base_path), instance.suite.name
179
154
  end
155
+
156
+ # load_needed_dependencies! loads the InSpec libraries required to verify a Terraform state.
157
+ #
158
+ # @raise [::Kitchen::ClientError] if loading the InSpec libraries fails.
159
+ # @see https://github.com/test-kitchen/test-kitchen/blob/v1.21.2/lib/kitchen/configurable.rb#L252-L274
160
+ def load_needed_dependencies!
161
+ require "kitchen/terraform/inspec"
162
+ require "kitchen/terraform/system"
163
+ ::Kitchen::Terraform::InSpec.logger = logger
164
+ rescue ::LoadError => load_error
165
+ raise ::Kitchen::ClientError, load_error.message
166
+ end
167
+
168
+ def system_attrs_resolver
169
+ @system_attrs_resolver ||= ::Kitchen::Terraform::SystemAttrsResolver.new outputs: @outputs
170
+ end
171
+
172
+ def system_hosts_resolver
173
+ @system_hosts_resolver ||= ::Kitchen::Terraform::SystemHostsResolver.new outputs: @outputs
174
+ end
175
+
176
+ def transport_connection_options
177
+ ::Kitchen::Util.stringified_hash(
178
+ instance.transport.diagnose.select do |key|
179
+ @transport_attributes.include? key
180
+ end.tap do |options|
181
+ options.store :timeout, options.fetch(:connection_timeout)
182
+ end
183
+ )
184
+ end
185
+
186
+ def verify(system:)
187
+ configure_inspec_system_options system: system
188
+ ::Kitchen::Terraform::System
189
+ .new(mapping: system)
190
+ .resolve_attrs(system_attrs_resolver: system_attrs_resolver)
191
+ .resolve_hosts(system_hosts_resolver: system_hosts_resolver)
192
+ .verify(inspec_options: @inspec_options, inspec_profile_path: inspec_profile_path)
193
+ end
194
+ end
180
195
  end
181
196
  end
182
-
183
- require "kitchen/verifier/terraform/configure_inspec_runner_attributes"
184
- require "kitchen/verifier/terraform/configure_inspec_runner_backend"
185
- require "kitchen/verifier/terraform/configure_inspec_runner_controls"
186
- require "kitchen/verifier/terraform/configure_inspec_runner_host"
187
- require "kitchen/verifier/terraform/configure_inspec_runner_port"
188
- require "kitchen/verifier/terraform/configure_inspec_runner_ssh_key"
189
- require "kitchen/verifier/terraform/configure_inspec_runner_user"
190
- require "kitchen/verifier/terraform/enumerate_groups_and_hostnames"