kitchen-terraform 0.2.0 → 0.3.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.
@@ -27,12 +27,12 @@ module Terraform
27
27
  end
28
28
 
29
29
  def options
30
- "-input=false -state=#{state}#{color_switch}"
30
+ "-input=false -state=#{state} #{color_switch}"
31
31
  end
32
32
 
33
33
  private
34
34
 
35
- attr_accessor :color, :state
35
+ attr_accessor :state
36
36
 
37
37
  def initialize_attributes(color:, state:)
38
38
  self.color = color
@@ -0,0 +1,34 @@
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
+ module Terraform
18
+ # Behaviour for the [:apply_timeout] config option
19
+ module ApplyTimeoutConfig
20
+ def self.included(configurable_class)
21
+ configurable_class
22
+ .required_config :apply_timeout do |_, value, configurable|
23
+ configurable.coerce_apply_timeout value: value
24
+ end
25
+ configurable_class.default_config :apply_timeout, 600
26
+ end
27
+
28
+ def coerce_apply_timeout(value:)
29
+ config[:apply_timeout] = Integer value
30
+ rescue ArgumentError, TypeError
31
+ config_error attribute: 'apply_timeout', expected: 'an integer'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,76 @@
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_relative 'apply_command'
18
+ require_relative 'command_executor'
19
+ require_relative 'get_command'
20
+ require_relative 'output_command'
21
+ require_relative 'plan_command'
22
+ require_relative 'show_command'
23
+ require_relative 'validate_command'
24
+ require_relative 'version_command'
25
+
26
+ module Terraform
27
+ # Behaviour for implementing the workflow
28
+ module Client
29
+ include CommandExecutor
30
+
31
+ def apply_execution_plan
32
+ execute command: ApplyCommand.new(
33
+ color: provisioner[:color], state: provisioner[:state],
34
+ target: provisioner[:plan]
35
+ ), timeout: provisioner[:apply_timeout]
36
+ end
37
+
38
+ def current_state
39
+ execute(
40
+ command: ShowCommand
41
+ .new(color: provisioner[:color], target: provisioner[:state])
42
+ ) { |value| return value.gsub(/(\e\[\d+m|\n)/, '') }
43
+ end
44
+
45
+ def download_modules
46
+ execute command: GetCommand.new(target: provisioner[:directory])
47
+ end
48
+
49
+ def output_value(list: false, name:, &block)
50
+ execute(
51
+ command: OutputCommand.new(
52
+ list: list, state: provisioner[:state], target: name, version: version
53
+ )
54
+ ) { |value| list ? value.each(&block) : (return value) }
55
+ end
56
+
57
+ def plan_execution(destroy:)
58
+ execute command: PlanCommand.new(
59
+ color: provisioner[:color], destroy: destroy, out: provisioner[:plan],
60
+ state: provisioner[:state], target: provisioner[:directory],
61
+ variables: provisioner[:variables],
62
+ variable_files: provisioner[:variable_files]
63
+ )
64
+ end
65
+
66
+ def validate_configuration_files
67
+ execute command: ValidateCommand.new(target: provisioner[:directory])
68
+ end
69
+
70
+ def version
71
+ execute command: VersionCommand.new do |value|
72
+ return value.slice(/v\d+\.\d+\.\d+/)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,34 @@
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
+ module Terraform
18
+ # Behaviour for the [:color] config option
19
+ module ColorConfig
20
+ def self.included(configurable_class)
21
+ configurable_class.required_config :color do |_, value, configurable|
22
+ configurable.coerce_color value: value
23
+ end
24
+ configurable_class.default_config :color, true
25
+ end
26
+
27
+ def coerce_color(value:)
28
+ raise TypeError unless [TrueClass, FalseClass].include? value.class
29
+ config[:color] = value
30
+ rescue TypeError
31
+ config_error attribute: 'color', expected: 'a boolean'
32
+ end
33
+ end
34
+ end
@@ -15,10 +15,14 @@
15
15
  # limitations under the License.
16
16
 
17
17
  module Terraform
18
- # Shared color switche for Terraform
18
+ # Shared color switch for Terraform
19
19
  module ColorSwitch
20
20
  def color_switch
21
- color ? '' : ' -no-color'
21
+ color ? '' : '-no-color'
22
22
  end
23
+
24
+ private
25
+
26
+ attr_accessor :color
23
27
  end
24
28
  end
@@ -14,27 +14,11 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
- require 'kitchen'
18
17
  require 'mixlib/shellout'
19
18
 
20
19
  module Terraform
21
- # Interface to the Terraform command line client
20
+ # Terraform command to be executed
22
21
  class Command
23
- def self.execute(**keyword_arguments, &block)
24
- new(**keyword_arguments).execute(&block)
25
- end
26
-
27
- def execute
28
- shell_out.run_command
29
- shell_out.error!
30
- yield shell_out.stdout if block_given?
31
- rescue Errno::EACCES, Errno::ENOENT => error
32
- command_error error: error, type: Kitchen::InstanceFailure
33
- rescue Mixlib::ShellOut::CommandTimeout,
34
- Mixlib::ShellOut::ShellCommandFailed => error
35
- command_error error: error, type: Kitchen::TransientFailure
36
- end
37
-
38
22
  def name
39
23
  ''
40
24
  end
@@ -43,25 +27,36 @@ module Terraform
43
27
  '--help'
44
28
  end
45
29
 
46
- private
30
+ def output
31
+ processed_output raw_output: shell_out.stdout
32
+ end
47
33
 
48
- attr_accessor :shell_out
34
+ def run(logger:, timeout:)
35
+ shell_out.live_stream = logger
36
+ shell_out.timeout = timeout
37
+ shell_out.run_command
38
+ shell_out.error!
39
+ end
49
40
 
50
- def command_error(error:, type:)
51
- raise type, %(`#{shell_out.command}` failed: "#{error}")
41
+ def to_s
42
+ shell_out.command
52
43
  end
53
44
 
54
- def initialize(
55
- logger:, target: '', timeout: Mixlib::ShellOut::DEFAULT_READ_TIMEOUT,
56
- **keyword_arguments
57
- )
45
+ private
46
+
47
+ attr_accessor :shell_out
48
+
49
+ def initialize(target: '', **keyword_arguments)
58
50
  initialize_attributes(**keyword_arguments)
59
51
  self.shell_out = Mixlib::ShellOut
60
- .new "terraform #{name} #{options} #{target}",
61
- live_stream: logger, returns: 0, timeout: timeout
52
+ .new "terraform #{name} #{options} #{target}", returns: 0
62
53
  end
63
54
 
64
55
  def initialize_attributes(**_keyword_arguments)
65
56
  end
57
+
58
+ def processed_output(raw_output:)
59
+ raw_output
60
+ end
66
61
  end
67
62
  end
@@ -0,0 +1,41 @@
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'
18
+ require 'mixlib/shellout'
19
+
20
+ module Terraform
21
+ # Behaviour for executing commands
22
+ module CommandExecutor
23
+ def execute(command:, timeout: Mixlib::ShellOut::DEFAULT_READ_TIMEOUT)
24
+ command.run logger: logger, timeout: timeout
25
+ yield command.output if block_given?
26
+ rescue Errno::EACCES, Errno::ENOENT => error
27
+ command_error command: command, error: error,
28
+ type: Kitchen::InstanceFailure
29
+ rescue Mixlib::ShellOut::CommandTimeout,
30
+ Mixlib::ShellOut::ShellCommandFailed => error
31
+ command_error command: command, error: error,
32
+ type: Kitchen::TransientFailure
33
+ end
34
+
35
+ private
36
+
37
+ def command_error(command:, error:, type:)
38
+ raise type, %(`#{command}` failed: "#{error}")
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
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
+ module Terraform
18
+ # Logic to extend a command's behaviour based on the Terraform version
19
+ module CommandExtender
20
+ def extend_behaviour(version:)
21
+ extend version_behaviours.fetch(
22
+ version_behaviours.keys
23
+ .find { |extended_version| extended_version =~ version }
24
+ ) { return }
25
+ end
26
+ end
27
+ end
@@ -16,30 +16,45 @@
16
16
 
17
17
  require 'forwardable'
18
18
  require 'kitchen'
19
+ require_relative 'version'
19
20
 
20
21
  module Terraform
21
22
  # Common logic for classes that include Kitchen::Configurable
22
23
  module Configurable
23
24
  extend Forwardable
24
25
 
25
- def_delegators :instance, :provisioner, :transport
26
+ def_delegators :instance, :driver, :provisioner, :transport
26
27
 
27
- def config_deprecated(attribute:, expected:)
28
- logger.warn 'DEPRECATION NOTICE'
29
- logger.warn formatted attribute: attribute,
30
- message: "should be #{expected}"
28
+ def self.included(configurable_class)
29
+ configurable_class.plugin_version VERSION
30
+ end
31
+
32
+ def config_deprecated(attribute:, remediation:, type:, version:)
33
+ log_deprecation aspect: "#{formatted(attribute: attribute)} as #{type}",
34
+ remediation: remediation, version: version
31
35
  end
32
36
 
33
37
  def config_error(attribute:, expected:)
34
- raise Kitchen::UserError, formatted(
35
- attribute: attribute, message: "must be interpretable as #{expected}"
36
- )
38
+ raise Kitchen::UserError, "#{formatted attribute: attribute} must be " \
39
+ "interpretable as #{expected}"
40
+ end
41
+
42
+ def instance_pathname(filename:)
43
+ File.join config[:kitchen_root], '.kitchen', 'kitchen-terraform',
44
+ instance.name, filename
45
+ end
46
+
47
+ def log_deprecation(aspect:, remediation:, version:)
48
+ logger.warn 'DEPRECATION NOTICE'
49
+ logger.warn "Support for #{aspect} will be dropped in " \
50
+ "kitchen-terraform v#{version}"
51
+ logger.warn remediation
37
52
  end
38
53
 
39
54
  private
40
55
 
41
- def formatted(attribute:, message:)
42
- "#{self.class}#{instance.to_str}#config[:#{attribute}] #{message}"
56
+ def formatted(attribute:)
57
+ "#{self.class}#{instance.to_str}#config[:#{attribute}]"
43
58
  end
44
59
  end
45
60
  end
@@ -0,0 +1,27 @@
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
+ module Terraform
18
+ # Behaviour for the [:directory] config option
19
+ module DirectoryConfig
20
+ def self.included(configurable_class)
21
+ configurable_class.default_config :directory do |configurable|
22
+ configurable[:kitchen_root]
23
+ end
24
+ configurable_class.expand_path_for :directory
25
+ end
26
+ end
27
+ end
@@ -14,81 +14,44 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
- require 'delegate'
18
-
19
17
  module Terraform
20
- # Group to be verified
21
- class Group < DelegateClass Hash
22
- def populate(runner:)
23
- dig(:attributes).each_pair do |key, output_name|
24
- runner.set_attribute key: key,
25
- value: provisioner.output(name: output_name)
26
- end
27
- end
28
-
29
- def verify_each_host(options:)
30
- require 'terraform/inspec_runner'
31
- provisioner.each_list_output name: dig(:hostnames) do |hostname|
32
- store :host, hostname
33
- verifier.info "Verifying group: #{dig :name}; current host #{hostname}"
34
- InspecRunner.run_and_verify group: self, options: options.merge(self),
35
- verifier: verifier
18
+ # Group of Terraform server instances to be verified
19
+ class Group
20
+ def each_attribute(&block)
21
+ data[:attributes].each_pair(&block)
22
+ end
23
+
24
+ def evaluate(verifier:)
25
+ verifier.merge options: options
26
+ verifier.resolve_attributes group: self
27
+ verifier.resolve_hostnames group: self do |hostname|
28
+ verifier.info "Verifying host '#{hostname}' of group '#{data[:name]}'"
29
+ verifier.merge options: { host: hostname }
30
+ verifier.execute
36
31
  end
37
32
  end
38
33
 
39
- private
40
-
41
- attr_accessor :provisioner, :transport, :verifier
42
-
43
- def coerce_attributes
44
- store :attributes, Hash(dig(:attributes))
45
- rescue ArgumentError, TypeError
46
- verifier.config_error attribute: "groups][#{self}][:attributes",
47
- expected: 'a mapping of Inspec attribute names ' \
48
- 'to Terraform output variable names'
49
- end
50
-
51
- def coerce_controls
52
- store :controls, Array(dig(:controls))
53
- end
54
-
55
- def coerce_hostnames
56
- store :hostnames, String(dig(:hostnames))
34
+ def hostnames
35
+ data[:hostnames]
57
36
  end
58
37
 
59
- def coerce_name
60
- store :name, String(dig(:name))
38
+ def store_attribute(key:, value:)
39
+ data[:attributes][key] = value
61
40
  end
62
41
 
63
- def coerce_parameters
64
- coerce_attributes
65
- coerce_controls
66
- coerce_hostnames
67
- coerce_name
68
- coerce_port
69
- coerce_username
70
- end
42
+ private
71
43
 
72
- def coerce_port
73
- store :port, Integer(dig(:port) || transport[:port])
74
- rescue ArgumentError, TypeError
75
- verifier.config_error attribute: "groups][#{self}][:port",
76
- expected: 'an integer'
77
- end
44
+ attr_accessor :data
78
45
 
79
- def coerce_username
80
- store :user, String(dig(:username) || transport[:username])
46
+ def initialize(data:)
47
+ self.data = data
81
48
  end
82
49
 
83
- def initialize(value:, verifier:)
84
- super Hash value
85
- self.provisioner = verifier.provisioner
86
- self.transport = verifier.transport
87
- self.verifier = verifier
88
- coerce_parameters
89
- rescue ArgumentError, TypeError
90
- verifier.config_error attribute: "groups][#{self}",
91
- expected: 'a group mapping'
50
+ def options
51
+ {
52
+ attributes: data[:attributes], controls: data[:controls],
53
+ port: data[:port], user: data[:username]
54
+ }
92
55
  end
93
56
  end
94
57
  end