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,89 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ require_relative '../../../covalence'
4
+ require_relative '../entities/stack'
5
+ require_relative 'state_store_repository'
6
+ require_relative 'input_repository'
7
+ require_relative 'context_repository'
8
+
9
+ module Covalence
10
+ class StackRepository
11
+ def self.find(data_store, environment_name, stack_name)
12
+ stack_scope = {
13
+ 'environment' => environment_name,
14
+ 'stack' => stack_name,
15
+ }
16
+ tool = lookup_tool(data_store, stack_scope)
17
+ return if tool.nil?
18
+
19
+ stack_data_store = data_store.initialize_scope(stack_scope)
20
+ stack_module = lookup_shared_namespace(stack_data_store, stack_name)
21
+ shared_namespace = stack_module.gsub('/', '::') unless stack_module.nil?
22
+
23
+ Stack.new(
24
+ type: tool,
25
+ name: stack_name,
26
+ environment_name: environment_name,
27
+ module_path: stack_module,
28
+ )
29
+ end
30
+
31
+ def self.populate(data_store, stack)
32
+ stack_scope = {
33
+ 'environment' => stack.environment_name,
34
+ 'stack' => stack.name,
35
+ }
36
+ stack_data_store = data_store.initialize_scope(stack_scope)
37
+ shared_namespace = stack.module_path.gsub('/', '::')
38
+
39
+ stack.dependencies = lookup_dependencies(stack_data_store, stack.name)
40
+ stack.packer_template = lookup_packer_template(stack_data_store, stack.name)
41
+ stack.workspace = lookup_workspace(stack_data_store, stack.name)
42
+ stack.state_stores = StateStoreRepository.query_by_stack_name(stack_data_store, stack.name, stack.workspace, stack.type)
43
+ stack.contexts = ContextRepository.query_by_namespace(stack_data_store, shared_namespace, stack.type)
44
+ stack.inputs = InputRepository.query_by_namespace(stack_data_store, shared_namespace, stack.type)
45
+ stack.args = find_args_by_namespace(stack_data_store, shared_namespace)
46
+ end
47
+
48
+ class << self
49
+ private
50
+ def lookup_packer_template(data_store, stack_name)
51
+ data_store.lookup("#{stack_name}::packer-template", nil)
52
+ end
53
+
54
+ def lookup_dependencies(data_store, stack_name)
55
+ data_store.lookup("#{stack_name}::deps", [])
56
+ end
57
+
58
+ def lookup_shared_namespace(data_store, stack_name)
59
+ data_store.lookup("#{stack_name}::module", stack_name)
60
+ end
61
+
62
+ def lookup_workspace(data_store, stack_name)
63
+ wrkspc = data_store.lookup("#{stack_name}::workspace", "")
64
+ wrkspc = Covalence::Helpers::ShellInterpolation.parse_shell(wrkspc) if wrkspc.to_s.include?("$(")
65
+ return wrkspc
66
+ end
67
+
68
+ # maybe arg_string instead of args
69
+ def find_args_by_namespace(data_store, namespace)
70
+ data_store.lookup("#{namespace}::args", "")
71
+ end
72
+
73
+ def lookup_tool(data_store, arguments)
74
+ if !data_store.lookup("#{arguments['stack']}::state", nil, arguments).nil?
75
+ return 'terraform'
76
+ elsif !data_store.lookup("#{arguments['stack']}::packer-template", nil, arguments).nil?
77
+ return 'packer'
78
+ else
79
+ logger.debug "#{arguments['environment']}:#{arguments['stack']} is neither a valid Terraform or Packer stack."
80
+ return nil
81
+ end
82
+ end
83
+
84
+ def logger
85
+ Covalence::LOGGER
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,31 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require_relative '../entities/state_store'
3
+
4
+ module Covalence
5
+ # todo: monitor behavior forking to determine when the split the class
6
+ class StateStoreRepository
7
+ class << self
8
+ def query_by_stack_name(data_store, stack_name, stack_workspace, tool)
9
+ if tool == 'terraform'
10
+ query_tool_by_stack_name(data_store, stack_name, stack_workspace)
11
+ else
12
+ return nil
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def query_tool_by_stack_name(data_store, stack_name, stack_workspace)
19
+ stores = data_store.lookup("#{stack_name}::state", [])
20
+ raise "State store array cannot be empty" if stores.blank?
21
+ stores.map do |store|
22
+ StateStore.new(
23
+ backend: store.keys.first,
24
+ params: store.values.first,
25
+ workspace_enabled: !stack_workspace.empty?
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'yaml'
2
+
3
+ module Covalence
4
+ class HieraSyntaxService
5
+ def self.check_yaml(filelist)
6
+ errors = []
7
+
8
+ [*filelist].each do |hiera_file|
9
+ begin
10
+ YAML.load_file(hiera_file)
11
+ rescue StandardError => error
12
+ errors << "ERROR: Failed to parse #{hiera_file}: #{error}"
13
+ end
14
+ end
15
+
16
+ errors.map { |error| error.to_s }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,104 @@
1
+ require 'tempfile'
2
+ require_relative '../../../covalence'
3
+ require_relative '../cli_wrappers/packer_cli'
4
+
5
+ module Covalence
6
+ class PackerStackTasks
7
+
8
+ def initialize(stack)
9
+ @path = File.expand_path(File.join(Covalence::PACKER, stack.module_path))
10
+ @stack = stack
11
+ @template = "#{@path}/#{stack.packer_template}"
12
+ end
13
+
14
+ def stack_name
15
+ stack.name
16
+ end
17
+
18
+ def environment_name
19
+ stack.environment_name
20
+ end
21
+
22
+ def context_build(*additional_args)
23
+ Dir.mktmpdir do |tmpdir|
24
+ populate_workspace(tmpdir)
25
+ Dir.chdir(tmpdir) do
26
+ logger.info "In #{tmpdir}:"
27
+
28
+ stack.materialize_cmd_inputs
29
+ args = collect_args(stack.args,
30
+ additional_args,
31
+ "-var-file=covalence-inputs.json")
32
+
33
+ call_packer_cmd("packer_build", args)
34
+ end
35
+ end
36
+ end
37
+
38
+ def context_inspect(*additional_args)
39
+ Dir.mktmpdir do |tmpdir|
40
+ populate_workspace(tmpdir)
41
+ Dir.chdir(tmpdir) do
42
+ logger.info "In #{tmpdir}:"
43
+
44
+ call_packer_cmd("packer_inspect", [])
45
+ end
46
+ end
47
+ end
48
+
49
+ def context_validate(*additional_args)
50
+ Dir.mktmpdir do |tmpdir|
51
+ populate_workspace(tmpdir)
52
+ Dir.chdir(tmpdir) do
53
+ logger.info "In #{tmpdir}:"
54
+
55
+ stack.materialize_cmd_inputs
56
+ args = collect_args(stack.args,
57
+ additional_args,
58
+ "-var-file=covalence-inputs.json")
59
+
60
+ call_packer_cmd("packer_validate", args)
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+ attr_reader :template_path, :stack
67
+
68
+ def populate_workspace(workspace)
69
+ # Copy module to the workspace
70
+ FileUtils.copy_entry @path, workspace
71
+
72
+ # Copy any dependencies to the workspace
73
+ @stack.dependencies.each do |dep|
74
+ logger.info "Copying '#{dep}' dependency to #{workspace}"
75
+ dep_path = File.expand_path(File.join(Covalence::PACKER, dep))
76
+ FileUtils.cp_r dep_path, workspace
77
+ end
78
+ end
79
+
80
+ def call_packer_cmd(packer_cmd, args)
81
+ if template_is_yaml?(@template)
82
+ config = YAML.load_file(@template).to_json
83
+ logger.info "\nGenerated build template:\n\n#{config}"
84
+ File.open('covalence-packer-template.json','w') {|f| f.write(config)}
85
+
86
+ PackerCli.public_send(packer_cmd.to_sym, 'covalence-packer-template.json', args: args)
87
+ else
88
+ PackerCli.public_send(packer_cmd.to_sym, @template, args: args)
89
+ end
90
+ end
91
+
92
+ def template_is_yaml?(template)
93
+ %w(.yaml .yml).include?(File.extname(template))
94
+ end
95
+
96
+ def collect_args(*args)
97
+ args.flatten.compact.reject(&:empty?).map(&:strip)
98
+ end
99
+
100
+ def logger
101
+ Covalence::LOGGER
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,212 @@
1
+ require_relative '../../../covalence'
2
+ require_relative '../cli_wrappers/terraform_cli'
3
+
4
+ module Covalence
5
+ class TerraformStackTasks
6
+
7
+ def initialize(stack)
8
+ @path = File.expand_path(File.join(Covalence::TERRAFORM, stack.module_path))
9
+ @stack = stack
10
+ end
11
+
12
+ def stack_name
13
+ stack.name
14
+ end
15
+
16
+ def environment_name
17
+ stack.environment_name
18
+ end
19
+
20
+ def stack_clean
21
+ TerraformCli.terraform_clean(@path)
22
+ end
23
+
24
+ def stack_format
25
+ TerraformCli.terraform_fmt(@path)
26
+ end
27
+
28
+ # :reek:TooManyStatements
29
+ def stack_verify
30
+ Dir.mktmpdir do |tmpdir|
31
+ populate_workspace(tmpdir)
32
+ Dir.chdir(tmpdir) do
33
+ logger.info "In #{tmpdir}:"
34
+
35
+ TerraformCli.terraform_get(@path)
36
+ TerraformCli.terraform_init
37
+
38
+ stack.materialize_cmd_inputs
39
+ args = collect_args("-input=false",
40
+ stack.args,
41
+ "-var-file=covalence-inputs.tfvars")
42
+
43
+ TerraformCli.terraform_validate(args: args)
44
+
45
+ TerraformCli.terraform_plan(args: args)
46
+ end
47
+ end
48
+ end
49
+
50
+ # :reek:TooManyStatements
51
+ def stack_refresh
52
+ Dir.mktmpdir do |tmpdir|
53
+ populate_workspace(tmpdir)
54
+ Dir.chdir(tmpdir) do
55
+ logger.info "In #{tmpdir}:"
56
+
57
+ TerraformCli.terraform_workspace(@stack.workspace) unless stack.workspace.to_s.empty?
58
+
59
+ stack.materialize_state_inputs
60
+ TerraformCli.terraform_get(@path)
61
+ TerraformCli.terraform_init
62
+
63
+ TerraformCli.terraform_refresh
64
+ end
65
+ end
66
+ end
67
+
68
+ # :reek:TooManyStatements
69
+ def stack_sync
70
+ Dir.mktmpdir do |tmpdir|
71
+ populate_workspace(tmpdir)
72
+ Dir.chdir(tmpdir) do
73
+ logger.info "In #{tmpdir}:"
74
+
75
+ TerraformCli.terraform_workspace(@stack.workspace) unless stack.workspace.to_s.empty?
76
+
77
+ stack.materialize_state_inputs
78
+ TerraformCli.terraform_get(@path)
79
+ TerraformCli.terraform_init
80
+
81
+ stack.state_stores.drop(1).each do |store|
82
+ stack.materialize_state_inputs(store: store)
83
+ TerraformCli.terraform_init("-force-copy")
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ # :reek:TooManyStatements
90
+ def context_plan(*additional_args)
91
+ Dir.mktmpdir do |tmpdir|
92
+ populate_workspace(tmpdir)
93
+ Dir.chdir(tmpdir) do
94
+ logger.info "In #{tmpdir}:"
95
+
96
+ TerraformCli.terraform_workspace(@stack.workspace) unless stack.workspace.to_s.empty?
97
+
98
+ stack.materialize_state_inputs
99
+ TerraformCli.terraform_get(@path)
100
+ TerraformCli.terraform_init
101
+
102
+ stack.materialize_cmd_inputs
103
+ args = collect_args("-input=false",
104
+ stack.args,
105
+ additional_args,
106
+ "-var-file=covalence-inputs.tfvars")
107
+
108
+ TerraformCli.terraform_plan(args: args)
109
+ end
110
+ end
111
+ end
112
+
113
+ # :reek:TooManyStatements
114
+ def context_plan_destroy(*additional_args)
115
+ Dir.mktmpdir do |tmpdir|
116
+ populate_workspace(tmpdir)
117
+ Dir.chdir(tmpdir) do
118
+ logger.info "In #{tmpdir}:"
119
+
120
+ TerraformCli.terraform_workspace(@stack.workspace) unless stack.workspace.to_s.empty?
121
+
122
+ stack.materialize_state_inputs
123
+ TerraformCli.terraform_get(@path)
124
+ TerraformCli.terraform_init
125
+
126
+ stack.materialize_cmd_inputs
127
+ args = collect_args("-destroy",
128
+ "-input=false",
129
+ stack.args,
130
+ additional_args,
131
+ "-var-file=covalence-inputs.tfvars")
132
+
133
+ TerraformCli.terraform_plan(args: args)
134
+ end
135
+ end
136
+ end
137
+
138
+ # :reek:TooManyStatements
139
+ def context_apply(*additional_args)
140
+ Dir.mktmpdir do |tmpdir|
141
+ populate_workspace(tmpdir)
142
+ Dir.chdir(tmpdir) do
143
+ logger.info "In #{tmpdir}:"
144
+
145
+ TerraformCli.terraform_workspace(@stack.workspace) unless stack.workspace.to_s.empty?
146
+
147
+ stack.materialize_state_inputs
148
+ TerraformCli.terraform_get(@path)
149
+ TerraformCli.terraform_init
150
+
151
+ stack.materialize_cmd_inputs
152
+ args = collect_args("-input=false",
153
+ "-auto-approve=true",
154
+ stack.args,
155
+ additional_args,
156
+ "-var-file=covalence-inputs.tfvars")
157
+
158
+ TerraformCli.terraform_apply(args: args)
159
+ end
160
+ end
161
+ end
162
+
163
+ # :reek:TooManyStatements
164
+ def context_destroy(*additional_args)
165
+ Dir.mktmpdir do |tmpdir|
166
+ populate_workspace(tmpdir)
167
+ Dir.chdir(tmpdir) do
168
+ logger.info "In #{tmpdir}:"
169
+
170
+ TerraformCli.terraform_workspace(@stack.workspace) unless stack.workspace.to_s.empty?
171
+
172
+ stack.materialize_state_inputs
173
+ TerraformCli.terraform_get(@path)
174
+ TerraformCli.terraform_init
175
+
176
+ stack.materialize_cmd_inputs
177
+ args = collect_args("-input=false",
178
+ "-auto-approve=true",
179
+ stack.args,
180
+ additional_args,
181
+ "-var-file=covalence-inputs.tfvars")
182
+
183
+ TerraformCli.terraform_destroy(args: args)
184
+ end
185
+ end
186
+ end
187
+
188
+ private
189
+ attr_reader :path, :stack, :store_args
190
+
191
+ def populate_workspace(workspace)
192
+ # Copy module to the workspace
193
+ FileUtils.copy_entry @path, workspace
194
+
195
+ # Copy any dependencies to the workspace
196
+ @stack.dependencies.each do |dep|
197
+ logger.info "Copying '#{dep}' dependency to #{workspace}"
198
+ dep_path = File.expand_path(File.join(Covalence::TERRAFORM, dep))
199
+ FileUtils.cp_r dep_path, workspace
200
+ end
201
+ end
202
+
203
+ def logger
204
+ Covalence::LOGGER
205
+ end
206
+
207
+ # :reek:FeatureEnvy
208
+ def collect_args(*args)
209
+ args.flatten.compact.reject(&:empty?).map(&:strip)
210
+ end
211
+ end
212
+ end