Class: Tfrb::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/tfrb/base.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(block, environments) ⇒ Base

Returns a new instance of Base.



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/tfrb/base.rb', line 16

def initialize(block, environments)
  @skip_import = false
  @dry_run = false
  @block = block
  @path = Tfrb::Config[:path]
  @temp_path = File.join(Tfrb::Config[:temp_path], block)
  Dir.mkdir(@temp_path) unless Dir.exist?(@temp_path)
  @local_state_path = temp_path('.terraform', 'local.tfstate')
  @environments = environments.each_with_object({}) do |environment, hash|
    hash[environment] = Tfrb::Block.load(environment)
  end
  @state = {}
end

Instance Attribute Details

#blockObject

Returns the value of attribute block.



8
9
10
# File 'lib/tfrb/base.rb', line 8

def block
  @block
end

#dry_runObject

Returns the value of attribute dry_run.



14
15
16
# File 'lib/tfrb/base.rb', line 14

def dry_run
  @dry_run
end

#environmentsObject

Returns the value of attribute environments.



11
12
13
# File 'lib/tfrb/base.rb', line 11

def environments
  @environments
end

#local_state_pathObject

Returns the value of attribute local_state_path.



10
11
12
# File 'lib/tfrb/base.rb', line 10

def local_state_path
  @local_state_path
end

#pathObject

Returns the value of attribute path.



9
10
11
# File 'lib/tfrb/base.rb', line 9

def path
  @path
end

#skip_importObject

Returns the value of attribute skip_import.



13
14
15
# File 'lib/tfrb/base.rb', line 13

def skip_import
  @skip_import
end

#stateObject

Returns the value of attribute state.



12
13
14
# File 'lib/tfrb/base.rb', line 12

def state
  @state
end

Class Method Details

.load(block, environments, skip_import = false, dry_run = false) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/tfrb/base.rb', line 182

def load(block, environments, skip_import = false, dry_run = false)
  Tfrb::Resource.load_helpers!

  printf "\033[1;32mLoading %s...\033[0m\n", block
  tfrb = self.new(block, environments)

  # Set skip_import
  tfrb.skip_import = skip_import

  # Set dry_run
  tfrb.dry_run = dry_run

  # Clean temporary files before starting
  tfrb.clean!

  # Load providers using extend (allows providers to fill in credentials, etc.)
  Tfrb::Provider.load(tfrb)

  # Write the .tf.json files to pick up any injection performed by providers
  tfrb.write!

  # Run terraform init if necessary
  tfrb.init! unless Dir.exist?(tfrb.temp_path('.terraform'))

  # Load state
  tfrb.load_state!

  # Preload resources
  Tfrb::Resource.preload(tfrb)

  # Write the .tf.json files so terraform import can be run by custom resources
  tfrb.write!

  # Load resources using extend (allows them to locate existing entities and run terraform import)
  Tfrb::Resource.load(tfrb)

  # Write the .tf.json files again to pick up any injection performed by resources
  tfrb.write!

  # Return a fully loaded tfrb instance
  tfrb
end

Instance Method Details

#apply!(parallelism = 1) ⇒ Object



146
147
148
149
150
151
152
153
# File 'lib/tfrb/base.rb', line 146

def apply!(parallelism = 1)
  printf "\033[1;32mApplying plan...\033[0m\n"
  tf_apply = Mixlib::ShellOut.new('terraform', 'apply', "-parallelism=#{parallelism}", '-auto-approve', 'plan.cache', shell_opts)
  tf_apply.run_command
  tf_apply.error!
  plan_cache = temp_path('plan.cache')
  File.delete(plan_cache) if File.exist?(plan_cache)
end

#clean!Object



155
156
157
158
159
160
161
162
# File 'lib/tfrb/base.rb', line 155

def clean!
  %w(*.tf.json).each do |glob|
    Dir.glob(temp_path(glob)).each do |file|
      File.delete(file) if File.exist?(file)
    end
  end
  File.delete(local_state_path) if File.exist?(local_state_path)
end

#import!(resource, name, id) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/tfrb/base.rb', line 65

def import!(resource, name, id)
  return if skip_import
  if resource !~ /^postgresql/
    environments_backup = Marshal.load(Marshal.dump(@environments))
    @environments.each do |environment_name, environment|
      if environment.has_key?('provider')
        environment['provider'].delete('postgresql') if environment['provider'].has_key?('postgresql')
        environment.delete('provider') if environment['provider'].empty?
      end
      if environment.has_key?('resource')
        environment['resource'].delete_if { |resource, _| resource =~ /^postgresql/ }
        environment.delete('resource') if environment['resource'].empty?
      end
    end
    write!
  end
  tf_import_args = ['terraform', 'import']
  if dry_run
    tf_import_args << "-lock=false"
    tf_import_args << "-state=#{local_state_path}"
  end
  providers = @environments.find { |_, e| e['resource'] && e['resource'][resource] && e['resource'][resource][name] && e['resource'][resource][name]['provider'] }
  if providers && provider = providers[1]['resource'][resource][name]['provider']
    tf_import_args << "-provider=#{provider}"
  end
  tf_import_args << "#{resource}.#{name}"
  tf_import_args << id
  tf_import = Mixlib::ShellOut.new(*tf_import_args, cwd: temp_path)
  tf_import.run_command
  tf_import.error!
  state[resource] ||= {}
  state[resource][name] = Tfrb::Resource.get_state(self, resource, name)
  printf "\033[1;32mImported %s.%s from %s\033[0m\n", resource, name, id
  if resource !~ /^postgresql/
    @environments = environments_backup
    write!
  end
end

#init!Object



36
37
38
39
40
# File 'lib/tfrb/base.rb', line 36

def init!
  tf_init = Mixlib::ShellOut.new('terraform', 'init', shell_opts)
  tf_init.run_command
  tf_init.error!
end

#load_state!Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/tfrb/base.rb', line 42

def load_state!
  return if skip_import
  return unless s3_state?
  tf_pullstate = Mixlib::ShellOut.new('terraform', 'state', 'pull', { cwd: temp_path })
  tf_pullstate.run_command
  tf_pullstate.error!
  File.write(local_state_path, tf_pullstate.stdout) if dry_run
  pulled_state = JSON.parse(tf_pullstate.stdout)
  if pulled_state['modules']
    pulled_state['modules'].each do |state_module|
      if state_module['resources'] && state_module['resources'].size >= 1
        state_module['resources'].each do |resource_key, resource_state|
          next if resource_key =~ /^data\./
          resource_type = resource_state['type']
          resource_name = resource_key.sub(/^#{resource_type}./, '')
          state[resource_type] = {} unless state.has_key?(resource_type)
          state[resource_type][resource_name] = resource_state['primary']['attributes'] unless state[resource_type].has_key?(resource_name)
        end
      end
    end
  end
end

#plan!Object



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/tfrb/base.rb', line 132

def plan!
  printf "\033[1;32mCalculating plan...\033[0m\n"
  tf_plan_args = ['terraform', 'plan']
  if dry_run
    tf_plan_args << "-lock=false"
    tf_plan_args << "-state=#{local_state_path}"
  else
    tf_plan_args << '-out=plan.cache'
  end
  tf_plan = Mixlib::ShellOut.new(*tf_plan_args, shell_opts)
  tf_plan.run_command
  tf_plan.error!
end

#s3_state?Boolean

Returns:

  • (Boolean)


164
165
166
# File 'lib/tfrb/base.rb', line 164

def s3_state?
  environments.find { |k,v| v['terraform'] && v['terraform']['backend'] && v['terraform']['backend']['s3'] }
end

#shell_optsObject



168
169
170
171
172
173
174
175
# File 'lib/tfrb/base.rb', line 168

def shell_opts
  {
    cwd: temp_path,
    live_stderr: $stderr,
    live_stdout: $stdout,
    timeout: 3600
  }
end

#statemv!(from_resource_id, to_resource_id) ⇒ Object



104
105
106
107
108
109
# File 'lib/tfrb/base.rb', line 104

def statemv!(from_resource_id, to_resource_id)
  printf "\033[1;32mMoving #{from_resource_id} to #{to_resource_id} in state...\033[0m\n"
  tf_statemv = Mixlib::ShellOut.new('terraform', 'state', 'mv', from_resource_id, to_resource_id, shell_opts)
  tf_statemv.run_command
  tf_statemv.error!
end

#staterm!(resource_id) ⇒ Object



111
112
113
114
115
116
# File 'lib/tfrb/base.rb', line 111

def staterm!(resource_id)
  printf "\033[1;32mRemoving #{resource_id} from state...\033[0m\n"
  tf_staterm = Mixlib::ShellOut.new('terraform', 'state', 'rm', resource_id, shell_opts)
  tf_staterm.run_command
  tf_staterm.error!
end

#taint!(resource_id) ⇒ Object



118
119
120
121
122
123
# File 'lib/tfrb/base.rb', line 118

def taint!(resource_id)
  printf "\033[1;32mTainting #{resource_id}...\033[0m\n"
  tf_taint = Mixlib::ShellOut.new('terraform', 'taint', resource_id, shell_opts)
  tf_taint.run_command
  tf_taint.error!
end

#temp_path(*args) ⇒ Object



177
178
179
# File 'lib/tfrb/base.rb', line 177

def temp_path(*args)
  File.join(@temp_path, *args)
end

#unlock!(lock_id) ⇒ Object



125
126
127
128
129
130
# File 'lib/tfrb/base.rb', line 125

def unlock!(lock_id)
  printf "\033[1;32mForce unlocking state...\033[0m\n"
  tf_unlock = Mixlib::ShellOut.new('terraform', 'force-unlock', lock_id, shell_opts.merge(input: 'yes'))
  tf_unlock.run_command
  tf_unlock.error!
end

#write!Object



30
31
32
33
34
# File 'lib/tfrb/base.rb', line 30

def write!
  @environments.each do |environment_name, environment|
    File.write(temp_path("#{environment_name}.tf.json"), JSON.pretty_generate(environment, indent: '  '))
  end
end