cloudspin-stack 0.1.19 → 0.1.20

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1363b9d4f696c0820332e7ea1c24611c7f094f44
4
- data.tar.gz: 7fc99227a54475c3bc850825cef71c81a93af347
3
+ metadata.gz: 7367da5398e58437223ee59141a3acfde89306c4
4
+ data.tar.gz: c7d4175e095df03cbd6e51bb8ba81a7cb9e9bef4
5
5
  SHA512:
6
- metadata.gz: 8fcfbd2f253bbf7447b89555b9479bf45bbe0347eefaa1a6382b2052768f7cc915a3f2706270e1590e009b9cf198f0a0f9d85672dc5327b7edf0face8d8601c7
7
- data.tar.gz: ead9896dc17751e16d04422f8a4e0cbadc9a814fd00a49cce4861c741fef38ac7466f5838087dbad429908db5db9ae9928723432d64431cb8c870de012e90029
6
+ metadata.gz: 8677124654c9c41f12a14c60d825d565d2574ae98ae07bc6fba1fdb86d9b60c0aefadc392dde3e54ceae77260519e05f7bb8b17fe633c3c0e23eb85f88a84732
7
+ data.tar.gz: c6f53cae6e58db92b63b72a32d869a5a40557affbc5d6c78d17387953c8235a442fbc3321432e2494f753959791c6fa12976c425922c01e02b8f64dea17e702b
@@ -20,13 +20,15 @@ module Cloudspin
20
20
 
21
21
  class_option :environment,
22
22
  :aliases => '-e',
23
- :banner => 'YAML-CONFIG-FILE',
24
- :desc => 'An environment instance to manage.'
23
+ :banner => 'environment_id',
24
+ :desc => 'An environment instance to manage. File ./environments/stack-instance-ENVIRONMENT_ID.yaml must exist.'
25
25
 
26
26
  desc 'up', 'Create or update the stack instance'
27
27
  option :dry, :type => :boolean, :default => false
28
28
  option :plan, :type => :boolean, :default => false
29
+ option :'show-init', :type => :boolean, :default => true
29
30
  def up
31
+ puts instance.init_dry if options[:'show-init']
30
32
  if options[:plan] && options[:dry]
31
33
  puts instance.plan_dry
32
34
  elsif options[:plan] && ! options[:dry]
@@ -41,7 +43,9 @@ module Cloudspin
41
43
  desc 'down', 'Destroy the stack instance'
42
44
  option :dry, :type => :boolean, :default => false
43
45
  option :plan, :type => :boolean, :default => false
46
+ option :'show-init', :type => :boolean, :default => true
44
47
  def down
48
+ puts instance.init_dry if options[:'show-init']
45
49
  if options[:plan] && options[:dry]
46
50
  puts instance.plan_dry(plan_destroy: true)
47
51
  elsif options[:plan] && ! options[:dry]
@@ -69,8 +73,7 @@ module Cloudspin
69
73
  Cloudspin::Stack::Instance.from_files(
70
74
  instance_configuration_files,
71
75
  stack_definition: stack_definition,
72
- base_working_folder: './work',
73
- base_statefile_folder: './state'
76
+ base_working_folder: './work'
74
77
  )
75
78
  end
76
79
 
@@ -9,51 +9,50 @@ module Cloudspin
9
9
 
10
10
  attr_reader :id,
11
11
  :working_folder,
12
- :statefile_folder,
13
12
  :configuration
14
13
 
15
14
  def initialize(
16
15
  id:,
17
16
  stack_definition:,
18
17
  working_folder:,
19
- statefile_folder:,
20
18
  configuration:
21
19
  )
22
20
  validate_id(id)
23
21
  @id = id
24
22
  @stack_definition = stack_definition
25
- @working_folder = working_folder
26
- @statefile_folder = statefile_folder
27
- @configuration = configuration
28
- @backend_config = {}
23
+ @working_folder = working_folder
24
+ @configuration = configuration
29
25
  end
30
26
 
31
27
  def self.from_folder(
32
28
  *instance_configuration_files,
33
29
  definition_folder:,
34
- base_working_folder:,
35
- base_statefile_folder:
30
+ base_folder: '.',
31
+ base_working_folder:
36
32
  )
37
33
  self.from_files(
38
34
  instance_configuration_files,
39
35
  stack_definition: Definition.from_folder(definition_folder),
40
- base_working_folder: base_working_folder,
41
- base_statefile_folder: base_statefile_folder
36
+ base_folder: base_folder,
37
+ base_working_folder: base_working_folder
42
38
  )
43
39
  end
44
40
 
45
41
  def self.from_files(
46
42
  *instance_configuration_files,
47
43
  stack_definition:,
48
- base_working_folder:,
49
- base_statefile_folder:
44
+ base_folder: '.',
45
+ base_working_folder:
50
46
  )
51
- instance_configuration = InstanceConfiguration.from_files(stack_definition, instance_configuration_files)
47
+ instance_configuration = InstanceConfiguration.from_files(
48
+ instance_configuration_files,
49
+ stack_definition: stack_definition,
50
+ base_folder: base_folder
51
+ )
52
52
  self.new(
53
53
  id: instance_configuration.instance_identifier,
54
54
  stack_definition: stack_definition,
55
55
  working_folder: ensure_folder("#{base_working_folder}/#{instance_configuration.instance_identifier}"),
56
- statefile_folder: ensure_folder("#{base_statefile_folder}/#{instance_configuration.instance_identifier}"),
57
56
  configuration: instance_configuration
58
57
  )
59
58
  end
@@ -81,24 +80,20 @@ module Cloudspin
81
80
  RubyTerraform.clean(directory: working_folder)
82
81
  mkdir_p File.dirname(working_folder)
83
82
  cp_r @stack_definition.source_path, working_folder
83
+ ensure_state_folder
84
84
  Dir.chdir(working_folder) do
85
- RubyTerraform.init(backend_config: @backend_config)
86
- RubyTerraform.plan(
87
- destroy: plan_destroy,
88
- state: terraform_statefile,
89
- vars: terraform_variables
90
- )
85
+ terraform_init
86
+ RubyTerraform.plan(terraform_command_parameters(destroy: plan_destroy))
91
87
  end
92
88
  end
93
89
 
94
90
  def plan_dry(plan_destroy: false)
95
91
  plan_command = RubyTerraform::Commands::Plan.new
96
92
  command_line_builder = plan_command.instantiate_builder
97
- configured_command = plan_command.configure_command(command_line_builder, {
98
- :destroy => plan_destroy,
99
- :state => terraform_statefile,
100
- :vars => terraform_variables
101
- })
93
+ configured_command = plan_command.configure_command(
94
+ command_line_builder,
95
+ terraform_command_parameters(:destroy => plan_destroy)
96
+ )
102
97
  built_command = configured_command.build
103
98
  "cd #{working_folder} && #{built_command.to_s}"
104
99
  end
@@ -107,23 +102,17 @@ module Cloudspin
107
102
  RubyTerraform.clean(directory: working_folder)
108
103
  mkdir_p File.dirname(working_folder)
109
104
  cp_r @stack_definition.source_path, working_folder
105
+ ensure_state_folder
110
106
  Dir.chdir(working_folder) do
111
- RubyTerraform.init(backend_config: @backend_config)
112
- RubyTerraform.apply(
113
- auto_approve: true,
114
- state: terraform_statefile,
115
- vars: terraform_variables
116
- )
107
+ terraform_init
108
+ RubyTerraform.apply(terraform_command_parameters(auto_approve: true))
117
109
  end
118
110
  end
119
111
 
120
112
  def up_dry
121
113
  up_command = RubyTerraform::Commands::Apply.new
122
114
  command_line_builder = up_command.instantiate_builder
123
- configured_command = up_command.configure_command(command_line_builder, {
124
- :state => terraform_statefile,
125
- :vars => terraform_variables
126
- })
115
+ configured_command = up_command.configure_command(command_line_builder, terraform_command_parameters)
127
116
  built_command = configured_command.build
128
117
  "cd #{working_folder} && #{built_command.to_s}"
129
118
  end
@@ -132,35 +121,82 @@ module Cloudspin
132
121
  RubyTerraform.clean(directory: working_folder)
133
122
  mkdir_p File.dirname(working_folder)
134
123
  cp_r @stack_definition.source_path, working_folder
124
+ ensure_state_folder
135
125
  Dir.chdir(working_folder) do
136
- RubyTerraform.init(backend_config: @backend_config)
137
- RubyTerraform.destroy(
138
- force: true,
139
- state: terraform_statefile,
140
- vars: terraform_variables
141
- )
126
+ terraform_init
127
+ RubyTerraform.destroy(terraform_command_parameters(force: true))
142
128
  end
143
129
  end
144
130
 
145
131
  def down_dry
146
132
  down_command = RubyTerraform::Commands::Destroy.new
147
133
  command_line_builder = down_command.instantiate_builder
148
- configured_command = down_command.configure_command(command_line_builder, {
149
- :state => terraform_statefile,
150
- :vars => terraform_variables
151
- })
134
+ configured_command = down_command.configure_command(command_line_builder, terraform_command_parameters)
152
135
  built_command = configured_command.build
153
136
  "cd #{working_folder} && #{built_command.to_s}"
154
137
  end
155
138
 
139
+ def terraform_init
140
+ RubyTerraform.init(terraform_init_params)
141
+ end
142
+
143
+ def init_dry
144
+ init_command = RubyTerraform::Commands::Init.new
145
+ command_line_builder = init_command.instantiate_builder
146
+ configured_command = init_command.configure_command(
147
+ command_line_builder,
148
+ terraform_init_params
149
+ )
150
+ built_command = configured_command.build
151
+ "cd #{working_folder} && #{built_command.to_s}"
152
+ end
153
+
154
+ def terraform_init_params
155
+ if configuration.has_remote_state_configuration?
156
+ {
157
+ backend: 's3',
158
+ backend_config: backend_parameters
159
+ }
160
+ else
161
+ {}
162
+ end
163
+ end
164
+
165
+ def ensure_state_folder
166
+ if configuration.has_local_state_configuration?
167
+ Instance.ensure_folder(configuration.terraform_backend['statefile_folder'])
168
+ end
169
+ end
170
+
171
+ def terraform_command_parameters(added_parameters = {})
172
+ {
173
+ vars: terraform_variables
174
+ }.merge(local_state_parameters).merge(added_parameters)
175
+ end
176
+
156
177
  def terraform_variables
157
178
  parameter_values.merge(resource_values) { |key, oldval, newval|
158
179
  raise "Duplicate values for terraform variable '#{key}' ('#{oldval}' and '#{newval}')"
159
180
  }.merge({ 'instance_identifier' => id })
160
181
  end
161
182
 
162
- def terraform_statefile
163
- statefile_folder + "/stack-#{id}.tfstate"
183
+ def local_state_parameters
184
+ if configuration.has_local_state_configuration?
185
+ { state: configuration.local_statefile }
186
+ else
187
+ {}
188
+ end
189
+ end
190
+
191
+ def backend_parameters
192
+ if configuration.has_remote_state_configuration?
193
+ {
194
+ 'bucket' => configuration.terraform_backend['bucket'],
195
+ 'key' => configuration.terraform_backend['key']
196
+ }
197
+ else
198
+ {}
199
+ end
164
200
  end
165
201
 
166
202
  end
@@ -7,28 +7,52 @@ module Cloudspin
7
7
 
8
8
  attr_reader :stack_definition
9
9
  attr_reader :stack_name
10
+ attr_reader :base_folder
10
11
 
11
12
  attr_reader :instance_values
12
13
  attr_reader :parameter_values
13
14
  attr_reader :resource_values
14
15
 
15
- def initialize(stack_definition)
16
+ attr_reader :terraform_backend
17
+
18
+ def initialize(
19
+ configuration_values: {},
20
+ stack_definition:,
21
+ base_folder: '.'
22
+ )
16
23
  @stack_definition = stack_definition
17
24
  @stack_name = stack_definition.name
18
- @instance_values = {}
19
- @parameter_values = {}
20
- @resource_values = {}
25
+ @base_folder = base_folder
26
+
27
+ @instance_values = configuration_values['instance'] || {}
28
+ @parameter_values = configuration_values['parameters'] || {}
29
+ @resource_values = configuration_values['resources'] || {}
30
+
31
+ @terraform_backend = configuration_values['terraform_backend'] || {}
32
+ if @terraform_backend.empty?
33
+ @terraform_backend['statefile_folder'] = default_state_folder
34
+ else
35
+ @terraform_backend['key'] = default_state_key
36
+ end
21
37
  end
22
38
 
23
- def self.from_files(stack_definition, *configuration_files)
24
- config = self.new(stack_definition)
39
+ def self.from_files(
40
+ *configuration_files,
41
+ stack_definition:,
42
+ base_folder: '.'
43
+ )
44
+ configuration_values = {}
25
45
  configuration_files.flatten.each { |config_file|
26
- config.add_values(load_file(config_file))
46
+ configuration_values = configuration_values.deep_merge(yaml_file_to_hash(config_file))
27
47
  }
28
- config
48
+ self.new(
49
+ stack_definition: stack_definition,
50
+ base_folder: base_folder,
51
+ configuration_values: configuration_values
52
+ )
29
53
  end
30
54
 
31
- def self.load_file(yaml_file)
55
+ def self.yaml_file_to_hash(yaml_file)
32
56
  if File.exists?(yaml_file)
33
57
  YAML.load_file(yaml_file) || {}
34
58
  else
@@ -36,11 +60,24 @@ module Cloudspin
36
60
  end
37
61
  end
38
62
 
39
- def add_values(values)
40
- @instance_values.merge!(values['instance']) if values['instance']
41
- @parameter_values.merge!(values['parameters']) if values['parameters']
42
- @resource_values.merge!(values['resources']) if values['resources']
43
- self
63
+ def has_local_state_configuration?
64
+ ! @terraform_backend['statefile_folder'].nil?
65
+ end
66
+
67
+ def local_statefile
68
+ "#{@terraform_backend['statefile_folder']}/#{instance_identifier}.tfstate"
69
+ end
70
+
71
+ def has_remote_state_configuration?
72
+ ! @terraform_backend['key'].nil?
73
+ end
74
+
75
+ def default_state_folder
76
+ Pathname.new("#{base_folder}/state/#{instance_identifier}").realdirpath.to_s
77
+ end
78
+
79
+ def default_state_key
80
+ "/#{instance_identifier}.tfstate"
44
81
  end
45
82
 
46
83
  def instance_identifier
@@ -55,9 +92,11 @@ module Cloudspin
55
92
 
56
93
  def to_s
57
94
  {
95
+ 'instance_identifier' => instance_identifier,
58
96
  'instance' => instance_values,
59
97
  'parameters' => parameter_values,
60
- 'resources' => resource_values
98
+ 'resources' => resource_values,
99
+ 'terraform_backend' => terraform_backend
61
100
  }.to_s
62
101
  end
63
102
 
@@ -66,3 +105,10 @@ module Cloudspin
66
105
  end
67
106
  end
68
107
 
108
+ # hat tip: https://stackoverflow.com/questions/9381553/ruby-merge-nested-hash
109
+ class ::Hash
110
+ def deep_merge(second)
111
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
112
+ self.merge(second.to_h, &merger)
113
+ end
114
+ end
@@ -1,5 +1,5 @@
1
1
  module Cloudspin
2
2
  module Stack
3
- VERSION = '0.1.19'
3
+ VERSION = '0.1.20'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudspin-stack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.19
4
+ version: 0.1.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - 'kief '
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-11 00:00:00.000000000 Z
11
+ date: 2018-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-terraform