covalence 0.0.1 → 0.7.9.rc1

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.
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