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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -3
- data/README.md +31 -28
- data/lib/kitchen/driver/terraform.rb +24 -8
- data/lib/kitchen/provisioner/terraform.rb +20 -144
- data/lib/kitchen/verifier/terraform.rb +32 -19
- data/lib/terraform/apply_command.rb +2 -2
- data/lib/terraform/apply_timeout_config.rb +34 -0
- data/lib/terraform/client.rb +76 -0
- data/lib/terraform/color_config.rb +34 -0
- data/lib/terraform/color_switch.rb +6 -2
- data/lib/terraform/command.rb +22 -27
- data/lib/terraform/command_executor.rb +41 -0
- data/lib/terraform/command_extender.rb +27 -0
- data/lib/terraform/configurable.rb +25 -10
- data/lib/terraform/directory_config.rb +27 -0
- data/lib/terraform/group.rb +26 -63
- data/lib/terraform/groups_config.rb +78 -0
- data/lib/terraform/inspec_runner.rb +5 -15
- data/lib/terraform/output_command.rb +13 -6
- data/lib/terraform/plan_command.rb +3 -4
- data/lib/terraform/plan_config.rb +27 -0
- data/lib/terraform/show_command.rb +39 -0
- data/lib/terraform/state_config.rb +27 -0
- data/lib/terraform/validate_command.rb +4 -0
- data/lib/terraform/variable_files_config.rb +33 -0
- data/lib/terraform/variables_config.rb +47 -0
- data/lib/terraform/version.rb +1 -1
- data/lib/terraform/version_command.rb +4 -0
- data/lib/terraform/zero_seven_output.rb +33 -0
- data/lib/terraform/zero_six_output.rb +30 -0
- metadata +90 -5
- metadata.gz.sig +0 -0
@@ -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 :
|
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
|
18
|
+
# Shared color switch for Terraform
|
19
19
|
module ColorSwitch
|
20
20
|
def color_switch
|
21
|
-
color ? '' : '
|
21
|
+
color ? '' : '-no-color'
|
22
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_accessor :color
|
23
27
|
end
|
24
28
|
end
|
data/lib/terraform/command.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
|
30
|
+
def output
|
31
|
+
processed_output raw_output: shell_out.stdout
|
32
|
+
end
|
47
33
|
|
48
|
-
|
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
|
51
|
-
|
41
|
+
def to_s
|
42
|
+
shell_out.command
|
52
43
|
end
|
53
44
|
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
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
|
42
|
-
"#{self.class}#{instance.to_str}#config[:#{attribute}]
|
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
|
data/lib/terraform/group.rb
CHANGED
@@ -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
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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
|
60
|
-
|
38
|
+
def store_attribute(key:, value:)
|
39
|
+
data[:attributes][key] = value
|
61
40
|
end
|
62
41
|
|
63
|
-
|
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
|
-
|
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
|
80
|
-
|
46
|
+
def initialize(data:)
|
47
|
+
self.data = data
|
81
48
|
end
|
82
49
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|