cloudspin-stack 0.1.19 → 0.1.20

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