covalence 0.0.1 → 0.7.9.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +164 -0
  3. data/README.md +489 -19
  4. data/TODO.md +14 -0
  5. data/lib/covalence.rb +41 -0
  6. data/lib/covalence/core/bootstrap.rb +8 -0
  7. data/lib/covalence/core/cli_wrappers/packer.yml +9 -0
  8. data/lib/covalence/core/cli_wrappers/packer_cli.rb +27 -0
  9. data/lib/covalence/core/cli_wrappers/popen_wrapper.rb +123 -0
  10. data/lib/covalence/core/cli_wrappers/terraform.yml +39 -0
  11. data/lib/covalence/core/cli_wrappers/terraform_cli.rb +119 -0
  12. data/lib/covalence/core/data_stores/hiera.rb +50 -0
  13. data/lib/covalence/core/entities/context.rb +38 -0
  14. data/lib/covalence/core/entities/environment.rb +24 -0
  15. data/lib/covalence/core/entities/input.rb +112 -0
  16. data/lib/covalence/core/entities/stack.rb +74 -0
  17. data/lib/covalence/core/entities/state_store.rb +65 -0
  18. data/lib/covalence/core/repositories/context_repository.rb +30 -0
  19. data/lib/covalence/core/repositories/environment_repository.rb +92 -0
  20. data/lib/covalence/core/repositories/input_repository.rb +56 -0
  21. data/lib/covalence/core/repositories/stack_repository.rb +89 -0
  22. data/lib/covalence/core/repositories/state_store_repository.rb +31 -0
  23. data/lib/covalence/core/services/hiera_syntax_service.rb +19 -0
  24. data/lib/covalence/core/services/packer_stack_tasks.rb +104 -0
  25. data/lib/covalence/core/services/terraform_stack_tasks.rb +212 -0
  26. data/lib/covalence/core/state_stores/atlas.rb +157 -0
  27. data/lib/covalence/core/state_stores/consul.rb +153 -0
  28. data/lib/covalence/core/state_stores/s3.rb +147 -0
  29. data/lib/covalence/environment_tasks.rb +328 -0
  30. data/lib/covalence/helpers/shell_interpolation.rb +28 -0
  31. data/lib/covalence/helpers/spec_dependencies.rb +21 -0
  32. data/lib/covalence/rake/rspec/envs_spec.rb +75 -0
  33. data/lib/covalence/rake/rspec/yaml_spec.rb +14 -0
  34. data/lib/covalence/spec_tasks.rb +59 -0
  35. data/lib/covalence/version.rb +3 -0
  36. metadata +344 -26
  37. data/.gitignore +0 -9
  38. data/Gemfile +0 -4
  39. data/LICENSE.txt +0 -21
  40. data/Rakefile +0 -2
  41. data/bin/console +0 -14
  42. data/bin/setup +0 -8
  43. data/lib/prometheus_unifio.rb +0 -5
  44. data/lib/prometheus_unifio/version.rb +0 -3
  45. data/prometheus_unifio.gemspec +0 -32
@@ -0,0 +1,328 @@
1
+ require 'rake'
2
+ require 'slop'
3
+
4
+ require_relative '../covalence'
5
+ require_relative 'core/repositories/environment_repository'
6
+ require_relative 'core/services/terraform_stack_tasks'
7
+ require_relative 'core/services/packer_stack_tasks'
8
+
9
+ module Covalence
10
+ class EnvironmentTasks
11
+ include Rake::DSL
12
+
13
+ attr_reader :logger
14
+
15
+ def initialize
16
+ @logger = Covalence::LOGGER
17
+ end
18
+
19
+ # :reek:NestedIterators
20
+ # :reek:TooManyStatements
21
+ def run
22
+ task = get_task_attr(ARGV.first)
23
+ if !task.empty?
24
+ environments = EnvironmentRepository.find_filtered(task)
25
+ else
26
+ environments = EnvironmentRepository.find_all
27
+ end
28
+
29
+ if !task.has_key? 'environment'
30
+ all_namespace_terraform_tasks(environments)
31
+ end
32
+
33
+ environments.each do |environment|
34
+ # We do not want to render individual tasks for the reserved 'ci' and 'spec' namespaces, or any other specified with COVALENCE_RESERVED_NAMESPACE
35
+ break if task.has_key? 'environment' && RESERVED_NS.include?(task['environment'])
36
+
37
+ next if task.has_key? 'environment' && environment.name != task['environment']
38
+ logger.debug("Rendering #{environment.name} environment tasks")
39
+ environment_namespace_terraform_tasks(environment)
40
+ environment_namespace_packer_tasks(environment)
41
+
42
+ environment.stacks.each do |stack|
43
+ next if task.has_key? 'stack' && stack.name != task['stack']
44
+ logger.debug("Rendering #{stack.name} stack tasks")
45
+ EnvironmentRepository.populate_stack(stack)
46
+ case stack.type
47
+ when 'terraform'
48
+ tf_tasks = TerraformStackTasks.new(stack)
49
+ stack_namespace_terraform_tasks(tf_tasks)
50
+
51
+ stack.contexts.each do |context|
52
+ context_namespace_terraform_tasks(tf_tasks, context)
53
+ end
54
+ when 'packer'
55
+ packer_tasks = PackerStackTasks.new(stack)
56
+
57
+ stack.contexts.each do |context|
58
+ context_namespace_packer_tasks(packer_tasks, context)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+ # :reek:TooManyStatements
67
+ def context_namespace_terraform_tasks(tf_tasks, context)
68
+ target_args = context.to_command_options
69
+ context_name = context.name
70
+ stack_name = tf_tasks.stack_name
71
+ environment_name = tf_tasks.environment_name
72
+
73
+ desc "Apply changes to the #{generate_rake_taskname(stack_name, context_name)} stack of the #{environment_name} environment"
74
+ task generate_rake_taskname(environment_name, stack_name, context_name, "apply") do
75
+ tf_tasks.context_apply(target_args, get_runtime_args)
76
+ end
77
+
78
+ desc "Destroy the #{generate_rake_taskname(stack_name, context_name)} stack of the #{environment_name} environment"
79
+ task generate_rake_taskname(environment_name, stack_name, context_name, "destroy") do
80
+ tf_tasks.context_destroy(target_args, get_runtime_args)
81
+ end
82
+
83
+ desc "Create execution plan for the #{generate_rake_taskname(stack_name, context_name)} stack of the #{environment_name} environment"
84
+ task generate_rake_taskname(environment_name, stack_name, context_name, "plan") do |args|
85
+ custom_opts = Slop.parse(get_runtime_args, { suppress_errors: true, banner: false }) do |o|
86
+ o.bool '-nd', '--no-drift', 'enable \'-detailed-exitcode\''
87
+ end
88
+
89
+ runtime_args = []
90
+ if custom_opts.no_drift?
91
+ runtime_args << "-detailed-exitcode"
92
+ end
93
+
94
+ runtime_args += custom_opts.args
95
+ tf_tasks.context_plan(target_args, runtime_args)
96
+ end
97
+
98
+ desc "Create destruction plan for the #{generate_rake_taskname(stack_name, context_name)} stack of the #{environment_name} environment"
99
+ task generate_rake_taskname(environment_name, stack_name, context_name, "plan_destroy") do
100
+ tf_tasks.context_plan_destroy(target_args, get_runtime_args)
101
+ end
102
+ end
103
+
104
+ def context_namespace_packer_tasks(packer_tasks, context)
105
+ target_args = context.to_packer_command_options
106
+ context_name = context.name
107
+ stack_name = packer_tasks.stack_name
108
+ environment_name = packer_tasks.environment_name
109
+
110
+ desc "Build the #{generate_rake_taskname(stack_name, context_name)} packer stack of the #{environment_name} environment"
111
+ task generate_rake_taskname(environment_name, stack_name, context_name, "packer-build") do
112
+ custom_opts = Slop.parse(get_runtime_args, { suppress_errors: true, banner: false })
113
+ packer_tasks.context_build(target_args, custom_opts.args)
114
+ end
115
+
116
+ desc "Inspect the #{generate_rake_taskname(stack_name, context_name)} packer stack of the #{environment_name} environment"
117
+ task generate_rake_taskname(environment_name, stack_name, context_name, "packer-inspect") do
118
+ packer_tasks.context_inspect(target_args)
119
+ end
120
+
121
+ #desc "Push the #{stack_name} packer stack of the #{environment_name} environment"
122
+ #TODO: deferred until someone asks for this
123
+
124
+ desc "Validate the #{generate_rake_taskname(stack_name, context_name)} packer stack of the #{environment_name} environment"
125
+ task generate_rake_taskname(environment_name, stack_name, context_name, "packer-validate") do
126
+ custom_opts = Slop.parse(get_runtime_args, { suppress_errors: true, banner: false })
127
+ packer_tasks.context_validate(target_args, custom_opts.args)
128
+ end
129
+ end
130
+
131
+ # :reek:TooManyStatements
132
+ def stack_namespace_terraform_tasks(tf_tasks)
133
+ stack_name = tf_tasks.stack_name
134
+ environment_name = tf_tasks.environment_name
135
+
136
+ desc "Clean the #{stack_name} stack of the #{environment_name} environment"
137
+ task generate_rake_taskname(environment_name, stack_name, "clean") do
138
+ tf_tasks.stack_clean
139
+ end
140
+
141
+ desc "Format the #{stack_name} stack of the #{environment_name} environment"
142
+ task generate_rake_taskname(environment_name, stack_name, "format") do
143
+ tf_tasks.stack_format
144
+ end
145
+
146
+ desc "Refresh the #{stack_name} stack of the #{environment_name} environment"
147
+ task generate_rake_taskname(environment_name, stack_name, "refresh") do
148
+ tf_tasks.stack_refresh
149
+ end
150
+
151
+ desc "Synchronize state stores for the #{stack_name} stack of the #{environment_name} environment"
152
+ task generate_rake_taskname(environment_name, stack_name, "sync") do
153
+ tf_tasks.stack_sync
154
+ end
155
+
156
+ desc "Verify the #{stack_name} stack of the #{environment_name} environment"
157
+ # Maybe verify_local to highlight that it skips pulling in remote state
158
+ task generate_rake_taskname(environment_name, stack_name, "verify") do
159
+ tf_tasks.stack_verify
160
+ end
161
+ end
162
+
163
+ # :reek:TooManyStatements
164
+ # :reek:DuplicateMethodCall
165
+ # :reek:FeatureEnvy
166
+ # rubocop:disable Metrics/MethodLength
167
+ def environment_namespace_terraform_tasks(environ)
168
+ desc "Clean the #{environ.name} environment"
169
+ task "#{environ.name}:clean" do
170
+ environ.stacks.each { |stack| invoke_rake_task(environ.name, stack.name, "clean") if stack.type == 'terraform' }
171
+ end
172
+
173
+ desc "Format the #{environ.name} environment"
174
+ task "#{environ.name}:format" do
175
+ environ.stacks.each { |stack| invoke_rake_task(environ.name, stack.name, "format") if stack.type == 'terraform' }
176
+ end
177
+
178
+ desc "Refresh the #{environ.name} environment"
179
+ task "#{environ.name}:refresh" do
180
+ environ.stacks.each { |stack| invoke_rake_task(environ.name, stack.name, "refresh") if stack.type == 'terraform' }
181
+ end
182
+
183
+ desc "Verify the #{environ.name} environment"
184
+ task "#{environ.name}:verify" do
185
+ environ.stacks.each { |stack| invoke_rake_task(environ.name, stack.name, "verify") if stack.type == 'terraform' }
186
+ end
187
+
188
+ ## Tasks that support multiple contexts
189
+ desc "Apply changes to the #{environ.name} environment"
190
+ task "#{environ.name}:apply" do
191
+ environ.stacks.each do |stack|
192
+ stack.contexts.each do |context|
193
+ invoke_rake_task(environ.name, stack.name, context.name, "apply") if stack.type == 'terraform'
194
+ end
195
+ end
196
+ end
197
+
198
+ desc "Destroy the #{environ.name} environment"
199
+ task "#{environ.name}:destroy" do
200
+ environ.stacks.reverse.each do |stack|
201
+ stack.contexts.each do |context|
202
+ invoke_rake_task(environ.name, stack.name, context.name, "destroy") if stack.type == 'terraform'
203
+ end
204
+ end
205
+ end
206
+
207
+ desc "Create execution plan for the #{environ.name} environment"
208
+ task "#{environ.name}:plan" do
209
+ environ.stacks.each do |stack|
210
+ stack.contexts.each do |context|
211
+ invoke_rake_task(environ.name, stack.name, context.name, "plan") if stack.type == 'terraform'
212
+ end
213
+ end
214
+ end
215
+
216
+ desc "Create destruction plan for the #{environ.name} environment"
217
+ task "#{environ.name}:plan_destroy" do
218
+ environ.stacks.reverse.each do |stack|
219
+ stack.contexts.each do |context|
220
+ invoke_rake_task(environ.name, stack.name, context.name, "plan_destroy") if stack.type == 'terraform'
221
+ end
222
+ end
223
+ end
224
+
225
+ desc "Synchronize state stores for the #{environ.name} environment"
226
+ task "#{environ.name}:sync" do
227
+ environ.stacks.each { |stack| invoke_rake_task(environ.name, stack.name, "sync") if stack.type == 'terraform' }
228
+ end
229
+ end
230
+
231
+ # :reek:TooManyStatements
232
+ # :reek:DuplicateMethodCall
233
+ # :reek:FeatureEnvy
234
+ # rubocop:disable Metrics/MethodLength
235
+ def environment_namespace_packer_tasks(environ)
236
+ desc "Build the #{environ.name} environment"
237
+ task "#{environ.name}:packer-build" do
238
+ environ.stacks.each do |stack|
239
+ stack.contexts.each do |context|
240
+ invoke_rake_task(environ.name, stack.name, context.name, "packer-build") if stack.type == 'packer'
241
+ end
242
+ end
243
+ end
244
+
245
+ desc "Inspect the #{environ.name} environment"
246
+ task "#{environ.name}:packer-inspect" do
247
+ environ.stacks.each do |stack|
248
+ stack.contexts.each do |context|
249
+ invoke_rake_task(environ.name, stack.name, context.name, "packer-inspect") if stack.type == 'packer'
250
+ end
251
+ end
252
+ end
253
+
254
+ desc "Validate the #{environ.name} environment"
255
+ task "#{environ.name}:packer-validate" do
256
+ environ.stacks.each do |stack|
257
+ stack.contexts.each do |context|
258
+ invoke_rake_task(environ.name, stack.name, context.name, "packer-validate") if stack.type == 'packer'
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ # :reek:TooManyStatements
265
+ def all_namespace_terraform_tasks(environments)
266
+ desc "Clean all environments"
267
+ task "all:clean" do
268
+ environments.each { |environ| invoke_rake_task(environ.name, "clean") }
269
+ end
270
+
271
+ desc "Format all environments"
272
+ task "all:format" do
273
+ environments.each { |environ| invoke_rake_task(environ.name, "format") }
274
+ end
275
+
276
+ desc "Plan all environments"
277
+ task "all:plan" do
278
+ environments.each { |environ| invoke_rake_task(environ.name, "plan") }
279
+ end
280
+
281
+ desc "Refresh all environments"
282
+ task "all:refresh" do
283
+ environments.each { |environ| invoke_rake_task(environ.name, "refresh") }
284
+ end
285
+
286
+ desc "Verify all environments"
287
+ task "all:verify" do
288
+ environments.each { |environ| invoke_rake_task(environ.name, "verify") }
289
+ end
290
+ end
291
+
292
+ def generate_rake_taskname(*args)
293
+ args.delete_if(&:empty?).map(&:to_s).join(":")
294
+ end
295
+
296
+ def invoke_rake_task(*args)
297
+ task_name = generate_rake_taskname(*args)
298
+ logger.info "rake #{task_name}"
299
+ Rake::Task[task_name].invoke
300
+ end
301
+
302
+ def get_runtime_args
303
+ # strips out [<rake_task>, "--"]
304
+ ARGV.drop(2)
305
+ end
306
+
307
+ def get_task_attr(input)
308
+ logger.info("Task: #{input}")
309
+ task = input.to_s.split(':')
310
+ task_comps = Hash.new
311
+ return task_comps if task.length <= 1 || task[0] == 'all'
312
+
313
+ if task.length >= 2
314
+ task_comps['environment'] = task[0]
315
+ logger.info("Applying environment filter: #{task[0]}")
316
+ end
317
+ if task.length >= 3
318
+ task_comps['stack'] = task[1]
319
+ logger.info("Applying stack filter: #{task[1]}")
320
+ end
321
+
322
+ task_comps
323
+ end
324
+
325
+ end
326
+ end
327
+
328
+ Covalence::EnvironmentTasks.new.run
@@ -0,0 +1,28 @@
1
+ require 'open3'
2
+
3
+ module Covalence
4
+ module Helpers
5
+ class ShellInterpolation
6
+
7
+ def self.parse_shell(input)
8
+ Covalence::LOGGER.info "Evaluating requested interpolation: \"#{input}\""
9
+ matches = input.scan(/.?\$\([^)]*\)/)
10
+
11
+ Covalence::LOGGER.debug "matches: #{matches}"
12
+ matches.each do |cmd|
13
+ if cmd[0] != "\\"
14
+ cmd = cmd[1..-1] unless cmd[0] == "$"
15
+ interpolated_value = Open3.capture2e(ENV, "echo \"#{cmd}\"")[0].chomp
16
+ input = input.gsub(cmd, interpolated_value)
17
+ Covalence::LOGGER.debug "updated value: #{input}"
18
+ else
19
+ input = input.gsub(cmd, cmd[1..-1])
20
+ end
21
+ end
22
+ Covalence::LOGGER.info "Interpolated value: \"#{input}\""
23
+ return input
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ # Modularize dependencies around the rspec tasks
2
+ require 'rubygems'
3
+
4
+ module Covalence
5
+ module Helpers
6
+ class SpecDependencies
7
+ def self.dependencies
8
+ {
9
+ "ci_reporter_rspec" => "~> 1.0.0",
10
+ "rspec" => "~> 3.5"
11
+ }
12
+ end
13
+
14
+ def self.check_dependencies
15
+ self.dependencies.each do |name, requirement|
16
+ Gem::Specification.find_by_name(name, requirement)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,75 @@
1
+ require_relative '../../core/bootstrap'
2
+
3
+ module Covalence
4
+ environments = EnvironmentRepository.find_all.select do |environ|
5
+ TEST_ENVS.include?(environ.name.to_s)
6
+ end
7
+
8
+ environments.each do |environment|
9
+ environment.stacks.each do |stack|
10
+ EnvironmentRepository.populate_stack(stack)
11
+ case stack.type
12
+ when 'terraform'
13
+ tf_tasks = TerraformStackTasks.new(stack)
14
+ when 'packer'
15
+ packer_tasks = PackerStackTasks.new(stack)
16
+ end
17
+
18
+ path = File.expand_path(File.join(TERRAFORM, stack.module_path))
19
+
20
+ describe "Verify #{environment.name}:#{stack.name}" do
21
+
22
+ if stack.type == 'terraform'
23
+
24
+ it 'passes style check' do
25
+ expect {
26
+ expect(TerraformCli.terraform_check_style(path)).to be true
27
+ }.to_not raise_error
28
+ end
29
+
30
+ it 'passes validation' do
31
+ tmp_dir = Dir.mktmpdir
32
+ # Copy module to the workspace
33
+ FileUtils.copy_entry path, tmp_dir
34
+
35
+ # Copy any dependencies to the workspace
36
+ stack.dependencies.each do |dep|
37
+ dep_path = File.expand_path(File.join(Covalence::TERRAFORM, dep))
38
+ FileUtils.cp_r dep_path, tmp_dir
39
+ end
40
+
41
+ Dir.chdir(tmp_dir) do
42
+ expect {
43
+ expect(TerraformCli.terraform_get(path)).to be true
44
+ }.to_not raise_error
45
+
46
+ expect {
47
+ expect(TerraformCli.terraform_init).to be true
48
+ }.to_not raise_error
49
+
50
+ stack.materialize_cmd_inputs
51
+
52
+ expect {
53
+ expect(TerraformCli.terraform_validate("-input=false -var-file=covalence-inputs.tfvars")).to be true
54
+ }.to_not raise_error
55
+ end
56
+ end
57
+
58
+ it 'passes execution' do
59
+ expect {
60
+ expect(tf_tasks.stack_verify).to be true
61
+ }.to_not raise_error
62
+ end
63
+
64
+ elsif stack.type == 'packer'
65
+
66
+ it 'passes validation' do
67
+ expect {
68
+ expect(packer_tasks.context_validate(stack.contexts.first.to_packer_command_options)).to be true
69
+ }.to_not raise_error
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end