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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +164 -0
- data/README.md +489 -19
- data/TODO.md +14 -0
- data/lib/covalence.rb +41 -0
- data/lib/covalence/core/bootstrap.rb +8 -0
- data/lib/covalence/core/cli_wrappers/packer.yml +9 -0
- data/lib/covalence/core/cli_wrappers/packer_cli.rb +27 -0
- data/lib/covalence/core/cli_wrappers/popen_wrapper.rb +123 -0
- data/lib/covalence/core/cli_wrappers/terraform.yml +39 -0
- data/lib/covalence/core/cli_wrappers/terraform_cli.rb +119 -0
- data/lib/covalence/core/data_stores/hiera.rb +50 -0
- data/lib/covalence/core/entities/context.rb +38 -0
- data/lib/covalence/core/entities/environment.rb +24 -0
- data/lib/covalence/core/entities/input.rb +112 -0
- data/lib/covalence/core/entities/stack.rb +74 -0
- data/lib/covalence/core/entities/state_store.rb +65 -0
- data/lib/covalence/core/repositories/context_repository.rb +30 -0
- data/lib/covalence/core/repositories/environment_repository.rb +92 -0
- data/lib/covalence/core/repositories/input_repository.rb +56 -0
- data/lib/covalence/core/repositories/stack_repository.rb +89 -0
- data/lib/covalence/core/repositories/state_store_repository.rb +31 -0
- data/lib/covalence/core/services/hiera_syntax_service.rb +19 -0
- data/lib/covalence/core/services/packer_stack_tasks.rb +104 -0
- data/lib/covalence/core/services/terraform_stack_tasks.rb +212 -0
- data/lib/covalence/core/state_stores/atlas.rb +157 -0
- data/lib/covalence/core/state_stores/consul.rb +153 -0
- data/lib/covalence/core/state_stores/s3.rb +147 -0
- data/lib/covalence/environment_tasks.rb +328 -0
- data/lib/covalence/helpers/shell_interpolation.rb +28 -0
- data/lib/covalence/helpers/spec_dependencies.rb +21 -0
- data/lib/covalence/rake/rspec/envs_spec.rb +75 -0
- data/lib/covalence/rake/rspec/yaml_spec.rb +14 -0
- data/lib/covalence/spec_tasks.rb +59 -0
- data/lib/covalence/version.rb +3 -0
- metadata +344 -26
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/lib/prometheus_unifio.rb +0 -5
- data/lib/prometheus_unifio/version.rb +0 -3
- 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
|