kitchen-terraform 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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