Class: Bolt::Plugin::Terraform
- Inherits:
-
Object
- Object
- Bolt::Plugin::Terraform
- Defined in:
- lib/bolt/plugin/terraform.rb
Constant Summary collapse
- KNOWN_KEYS =
Set['_plugin', 'dir', 'resource_type', 'uri', 'name', 'statefile', 'config', 'backend']
- REQ_KEYS =
Set['dir', 'resource_type']
Instance Method Summary collapse
-
#extract_resources(state) ⇒ Object
Format the list of resources into a list of [name, attribute map] pairs.
- #hooks ⇒ Object
-
#initialize(*_args) ⇒ Terraform
constructor
A new instance of Terraform.
- #load_local_statefile(opts) ⇒ Object
-
#load_remote_statefile(opts) ⇒ Object
Uses the Terraform CLI to pull remote state files.
- #load_statefile(opts) ⇒ Object
-
#lookup(name, resource, path) ⇒ Object
Look up a nested value from the resource attributes.
- #name ⇒ Object
-
#resolve_config(name, resource, config_template) ⇒ Object
Walk the “template” config mapping provided in the plugin config and replace all values with the corresponding value from the resource parameters.
- #resolve_reference(opts) ⇒ Object
-
#validate_options(opts) ⇒ Object
Make sure no unexpected keys are in the inventory config and that required keys are present.
- #warn_missing_property(name, property) ⇒ Object
Constructor Details
#initialize(*_args) ⇒ Terraform
Returns a new instance of Terraform.
12 13 14 |
# File 'lib/bolt/plugin/terraform.rb', line 12 def initialize(*_args) @logger = Logging.logger[self] end |
Instance Method Details
#extract_resources(state) ⇒ Object
Format the list of resources into a list of [name, attribute map] pairs. This method handles both version 4 and earlier statefiles, doing the appropriate munging based on the shape of the data.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/bolt/plugin/terraform.rb', line 118 def extract_resources(state) if state['version'] >= 4 state.fetch('resources', []).flat_map do |resource_set| prefix = "#{resource_set['type']}.#{resource_set['name']}" resource_set['instances'].map do |resource| instance_name = prefix instance_name += ".#{resource['index_key']}" if resource['index_key'] # When using `terraform state pull` with terraform >= 0.12 version 3 statefiles # Will be converted to version 4. When converted attributes is converted to attributes_flat attributes = resource['attributes'] || resource['attributes_flat'] [instance_name, attributes] end end else state.fetch('modules', {}).flat_map do |mod| mod.fetch('resources', {}).map do |name, resource| [name, resource.dig('primary', 'attributes')] end end end end |
#hooks ⇒ Object
20 21 22 |
# File 'lib/bolt/plugin/terraform.rb', line 20 def hooks [:resolve_reference] end |
#load_local_statefile(opts) ⇒ Object
107 108 109 110 111 112 113 |
# File 'lib/bolt/plugin/terraform.rb', line 107 def load_local_statefile(opts) dir = opts['dir'] filename = opts.fetch('statefile', 'terraform.tfstate') File.read(File.(File.join(dir, filename))) rescue StandardError => e raise Bolt::FileError.new("Could not load Terraform state file #{filename}: #{e}", filename) end |
#load_remote_statefile(opts) ⇒ Object
Uses the Terraform CLI to pull remote state files
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/bolt/plugin/terraform.rb', line 84 def load_remote_statefile(opts) dir = File.(opts['dir']) begin stdout_str, stderr_str, status = Open3.capture3('terraform state pull', chdir: dir) rescue Errno::ENOENT reason = if File.directory?(dir) "Could not find executable 'terraform'" else "Could not find directory '#{dir}'" end raise Bolt::Error.new(reason, 'FILE_ERROR') end unless status.success? err = stdout_str + stderr_str msg = "Could not pull Terraform remote state file for #{opts['dir']}:\n#{err}" raise Bolt::Error.new(msg, 'bolt/terraform-state-error') end stdout_str end |
#load_statefile(opts) ⇒ Object
73 74 75 76 77 78 79 80 81 |
# File 'lib/bolt/plugin/terraform.rb', line 73 def load_statefile(opts) statefile = if opts['backend'] == 'remote' load_remote_statefile(opts) else load_local_statefile(opts) end JSON.parse(statefile) end |
#lookup(name, resource, path) ⇒ Object
Look up a nested value from the resource attributes. The key is of the form ‘foo.bar.0.baz`. For terraform statefile version 3, this will exactly correspond to a key in the resource. In version 4, it will correspond to a nested hash entry at {bar: [{baz: <value>]}} For simplicity’s sake, we just check both.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/bolt/plugin/terraform.rb', line 145 def lookup(name, resource, path) segments = path.split('.').map do |segment| begin Integer(segment) rescue ArgumentError segment end end value = resource[path] || resource.dig(*segments) unless value warn_missing_property(name, path) end value end |
#name ⇒ Object
16 17 18 |
# File 'lib/bolt/plugin/terraform.rb', line 16 def name 'terraform' end |
#resolve_config(name, resource, config_template) ⇒ Object
Walk the “template” config mapping provided in the plugin config and replace all values with the corresponding value from the resource parameters.
164 165 166 167 168 169 170 171 172 |
# File 'lib/bolt/plugin/terraform.rb', line 164 def resolve_config(name, resource, config_template) Bolt::Util.walk_vals(config_template) do |value| if value.is_a?(String) lookup(name, resource, value) else value end end end |
#resolve_reference(opts) ⇒ Object
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 |
# File 'lib/bolt/plugin/terraform.rb', line 44 def resolve_reference(opts) (opts) state = load_statefile(opts) resources = extract_resources(state) regex = Regexp.new(opts['resource_type']) resources.select do |name, _resource| name.match?(regex) end.map do |name, resource| target = {} if opts.key?('uri') uri = lookup(name, resource, opts['uri']) target['uri'] = uri if uri end if opts.key?('name') real_name = lookup(name, resource, opts['name']) target['name'] = real_name if real_name end if opts.key?('config') target['config'] = resolve_config(name, resource, opts['config']) end target end.compact end |
#validate_options(opts) ⇒ Object
Make sure no unexpected keys are in the inventory config and that required keys are present
30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/bolt/plugin/terraform.rb', line 30 def (opts) opt_keys = opts.keys.to_set unless KNOWN_KEYS.superset?(opt_keys) keys = opt_keys - KNOWN_KEYS raise Bolt::ValidationError, "Unexpected key(s) in inventory config: #{keys.to_a.inspect}" end unless opt_keys.superset?(REQ_KEYS) keys = REQ_KEYS - opt_keys raise Bolt::ValidationError, "Expected key(s) in inventory config: #{keys.to_a.inspect}" end end |
#warn_missing_property(name, property) ⇒ Object
24 25 26 |
# File 'lib/bolt/plugin/terraform.rb', line 24 def warn_missing_property(name, property) @logger.warn("Could not find property #{property} of terraform resource #{name}") end |