terragov 0.3.3 → 0.4.0

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: 81263feff3c924e10abd2ee9970eb7f0b8c38a8b
4
- data.tar.gz: caa6a769b206bd259ed0cb6ad9402226ee140f53
3
+ metadata.gz: 04caa8ed8dd4cc7302986169259c013f0e3f478b
4
+ data.tar.gz: e1542e895f76d7de847d4c1b72ffb07001f7bb6b
5
5
  SHA512:
6
- metadata.gz: cc20df3bc78578f2f72661ee803affded61c966394262753c7d2026c78cc0e9119287b6fb66f7d26de1f80b6a10c5ace7081fba0c8e3a082e8c7121313362367
7
- data.tar.gz: 7d4cbadd5f067d840dc14de3a92305735346fefb13aad4ae7980cc3549ed613be0f85298097c12b6b5ef81149070bbd260cdaaed503914c0451eb4bd169c937f
6
+ metadata.gz: 1629715b97ee6ed0a9b3d4dcc4305804387dc1b9c7b367f5cf847a8843a0f9269de6fd38b3c6195ff6252d2b1523da7f00e66ca5b828133ad523e3b3bbc077a3
7
+ data.tar.gz: 2bd326f14b744b6fc2cb07c2f5b531efebf198756d4459fc307a562a03bf7d2a697b4332e1cb85f57e1ac035dc9f943e6c83ed68a63ccf2bcc91f995a6f68162
@@ -1,3 +1,11 @@
1
+ ## 0.4.0 (2018-02-07)
2
+
3
+ - Added end to end tests
4
+ - Added further tests to Terraform class
5
+ - Code tidy
6
+ - Changed "dryrun" flag to "test"
7
+ - Broke out logic to separate Config class
8
+
1
9
  ## 0.3.3 (2018-01-30)
2
10
 
3
11
  - Allow full hierachy use of data across all environments
data/README.md CHANGED
@@ -85,7 +85,7 @@ These may be set in the same way as described above, with the same precedence, b
85
85
  Argument | Description
86
86
  --- | ---
87
87
  `verbose` | Be more noisy
88
- `dryrun` | CLI option is `--dry-run`, but config file and env var is `dryrun` and `TERRAGOV_DRYRUN` respectively
88
+ `test` | Output commands, but do not run anything. Useful for tests.
89
89
  `skip_git_check` | Do not compare branches between the repo and data directories
90
90
 
91
91
  ## Deployment
@@ -1,4 +1,5 @@
1
1
  require 'terragov/buildpaths'
2
+ require 'terragov/config.rb'
2
3
  require 'terragov/cleaner'
3
4
  require 'terragov/cli'
4
5
  require 'terragov/git'
@@ -48,11 +48,7 @@ module Terragov
48
48
  end
49
49
 
50
50
  def data_validation(path)
51
- if File.exist?(path)
52
- true
53
- else
54
- false
55
- end
51
+ File.exist?(path)
56
52
  end
57
53
 
58
54
  def data_paths(options = {})
@@ -86,7 +82,7 @@ module Terragov
86
82
  paths.each do |path|
87
83
  puts path
88
84
  end
89
- return false
85
+ false
90
86
  end
91
87
  end
92
88
 
@@ -101,7 +97,6 @@ module Terragov
101
97
  $full_vars = []
102
98
  data_paths(options).each do |path|
103
99
  if data_validation(path)
104
- # TODO: write sops class
105
100
  if path == paths[:secret_project_data] || path == paths[:secret_common_project_data]
106
101
  $full_vars << "-var-file <(sops -d #{path})"
107
102
  else
@@ -6,6 +6,7 @@ require_relative 'terraform'
6
6
  require_relative 'cleaner'
7
7
  require_relative 'version'
8
8
  require_relative 'git'
9
+ require_relative 'config'
9
10
 
10
11
  module Terragov
11
12
  class Cli
@@ -16,157 +17,45 @@ module Terragov
16
17
  program :version, Terragov::VERSION
17
18
  program :description, 'Wrapper for GOV.UK Terraform deployments.'
18
19
 
20
+ # Project should be loaded independently, unless used in deployment mode
21
+ global_option('-p', '--project STRING', String, 'Name of the project') do |project|
22
+ $project = project || ENV['TERRAGOV_PROJECT']
23
+ end
24
+
19
25
  global_option('-c', '--config-file FILE', 'Specify a config file. Has less precedence than environment variables, which in turn have less precedence than CLI options') do |config_file|
20
- $config_file = config_file
26
+ $config_file = config_file || ENV['TERRAGOV_CONFIG_FILE']
21
27
  end
22
28
 
23
29
  global_option('-d', '--data-dir DIRECTORY', String, 'Location of the data directory') do |data_dir|
24
- $data_dir = data_dir
30
+ @data_dir_cli ||= data_dir
25
31
  end
26
32
 
27
33
  global_option('-e', '--environment STRING', String, 'Select environment') do |environment|
28
- $environment = environment
29
- end
30
-
31
- global_option('-p', '--project STRING', String, 'Name of the project') do |project|
32
- $project = project
34
+ @environment_cli = environment || false
33
35
  end
34
36
 
35
37
  global_option('-r', '--repo-dir DIRECTORY', String, 'Location of the main terraform repository') do |repo_dir|
36
- $repo_dir = repo_dir
38
+ @repo_dir_cli = repo_dir || false
37
39
  end
38
40
 
39
41
  global_option('-s', '--stack STRING', String, 'Name of the stack') do |stack|
40
- $stack = stack
42
+ @stack_cli = stack || false
41
43
  end
42
44
 
43
45
  global_option('--extra STRING', String, 'Any additional arguments to pass in the following format: --extra \\\\-target resource.foo.') do |extra|
44
- $extra = extra
46
+ @extra = extra || nil
45
47
  end
46
48
 
47
49
  global_option('--verbose', 'Verbose mode') do |verbose|
48
- $verbose = verbose
50
+ @verbose = verbose || false
49
51
  end
50
52
 
51
- global_option('--dry-run', 'Dry run mode', 'Output the commands to run, but do not run them') do |dryrun|
52
- $dryrun = dryrun
53
+ global_option('--test', 'Dry run mode', 'Output the commands to run, but do not run them') do |test|
54
+ @test = test || false
53
55
  end
54
56
 
55
57
  global_option('--skip-git-check', 'Skip git check', 'Do not check the status of git repositories') do |skip_git_check|
56
- $skip_git_check = skip_git_check
57
- end
58
- end
59
-
60
- def data_dir
61
- $data_dir ? $data_dir : false
62
- end
63
-
64
- def environment
65
- $environment ? $environment : false
66
- end
67
-
68
- def project
69
- $project ? $project : false
70
- end
71
-
72
- def repo_dir
73
- $repo_dir ? $repo_dir : false
74
- end
75
-
76
- def stack
77
- $stack ? $stack : false
78
- end
79
-
80
- def extra
81
- return $extra if $extra
82
- end
83
-
84
- def verbose
85
- true ? $verbose : false
86
- end
87
-
88
- def dryrun
89
- true ? $dryrun : false
90
- end
91
-
92
- def skip_git_check
93
- true ? $skip_git_check : false
94
- end
95
-
96
- def load_config_file
97
- if $config_file || ENV['TERRAGOV_CONFIG_FILE']
98
- file = $config_file || ENV['TERRAGOV_CONFIG_FILE']
99
- YAML.load_file(File.expand_path(file))
100
- end
101
- end
102
-
103
- def config_file_default
104
- if load_config_file['default'].nil?
105
- return nil
106
- else
107
- return load_config_file['default']
108
- end
109
- end
110
-
111
- def config_file_specific_project(project_name)
112
- load_config_file[project_name]
113
- end
114
-
115
- def config_file(option)
116
- # This has to be loaded in seperately to avoid any cyclic dependencies
117
- project_name = $project || ENV['TERRAGOV_PROJECT']
118
-
119
- if project_name.nil?
120
- if config_file_default.nil?
121
- return nil
122
- else
123
- return config_file_default[option]
124
- end
125
- else
126
- project_config = config_file_specific_project(project_name)
127
- if project_config.nil? or project_config[option].nil?
128
- return config_file_default[option]
129
- else
130
- return project_config[option]
131
- end
132
- end
133
- end
134
-
135
- def config(option, file = false, required = true)
136
- env_var = "TERRAGOV_#{option.upcase}"
137
- error_message = "Must set #{option}. Use --help for details."
138
-
139
- # Load from CLI option
140
- if public_send(option)
141
- if file
142
- return File.expand_path(public_send(option))
143
- else
144
- return public_send(option)
145
- end
146
-
147
- # Load from environment variable
148
- elsif ENV[env_var]
149
- if file
150
- return File.expand_path(ENV[env_var])
151
- else
152
- return ENV[env_var]
153
- end
154
-
155
- # Load from config file
156
- elsif !load_config_file.nil?
157
- if config_file(option).nil?
158
- abort(error_message) if required
159
- return false
160
- else
161
- if file
162
- return File.expand_path(config_file(option))
163
- else
164
- return config_file(option)
165
- end
166
- end
167
- else
168
- abort(error_message) if required
169
- return false
58
+ @skip_git_check = skip_git_check || false
170
59
  end
171
60
  end
172
61
 
@@ -175,66 +64,84 @@ module Terragov
175
64
 
176
65
  # Always load the project name first
177
66
  unless deployment
178
- cmd_hash = { 'project' => config('project') }
67
+ cmd_hash = { 'project' => $project }
179
68
  end
180
69
 
70
+ config = Terragov::Config.new
71
+
72
+ data_dir = config.lookup({
73
+ name: 'data_dir',
74
+ cli: @data_dir_cli,
75
+ file: true,
76
+ })
77
+ environment = config.lookup({
78
+ name: 'environment',
79
+ cli: @environment_cli,
80
+ })
81
+ repo_dir = config.lookup({
82
+ name: 'repo_dir',
83
+ cli: @repo_dir_cli,
84
+ file: true,
85
+ })
86
+ stack = config.lookup({
87
+ name: 'stack',
88
+ cli: @stack_cli,
89
+ })
90
+
91
+ #require 'pry'; binding.pry
92
+
181
93
  cmd_hash.merge({
182
- 'environment' => config('environment'),
183
- 'data_dir' => config('data_dir', true),
184
- 'stack' => config('stack'),
185
- 'repo_dir' => config('repo_dir', true),
186
- 'extra' => extra
94
+ 'environment' => environment,
95
+ 'data_dir' => data_dir,
96
+ 'stack' => stack,
97
+ 'repo_dir' => repo_dir,
98
+ 'extra' => @extra,
187
99
  })
188
100
  end
189
101
 
190
102
  def git_compare_repo_and_data(skip = false)
191
- git_helper = Terragov::Git.new
192
- # FIXME: this is confusing as we want to check the repository git status from
193
- # the root, but the "data" directory is one level down in the repository
194
- repo_dir_root = cmd_options['repo_dir']
195
- data_dir_root = File.expand_path(File.join(cmd_options['data_dir'], '../'))
196
-
197
- repo_dir_branch = git_helper.branch_name(repo_dir_root)
198
- data_dir_branch = git_helper.branch_name(data_dir_root)
199
-
200
- branches = {
201
- 'repo_dir' => repo_dir_branch,
202
- 'data_dir' => data_dir_branch
203
- }
204
-
205
103
  unless skip
104
+ git_helper = Terragov::Git.new
105
+ # FIXME: this is confusing as we want to check the repository git status from
106
+ # the root, but the "data" directory is one level down in the repository
107
+ repo_dir_root = cmd_options['repo_dir']
108
+ data_dir_root = File.expand_path(File.join(cmd_options['data_dir'], '../'))
109
+
110
+ branches = {
111
+ 'repo_dir' => git_helper.branch_name(repo_dir_root),
112
+ 'data_dir' => git_helper.branch_name(data_dir_root),
113
+ }
114
+
206
115
  branches.each do |name, branch|
207
116
  unless branch =~ /^master$/
208
117
  exit unless HighLine.agree("#{name} not on 'master' branch, continue on branch '#{branch}'?")
209
118
  end
210
119
  end
211
-
212
- unless git_helper.compare_branch(repo_dir_root, data_dir_root)
213
- puts "Warning: repo_dir(#{repo_dir_branch}) and data_dir(#{data_dir_branch}) on different branches"
214
- exit unless HighLine.agree('Do you wish to continue?')
215
- end
216
120
  end
217
121
  end
218
122
 
219
- def run_terraform_cmd(cmd, opt = nil, deployment = false)
123
+ def run_terraform_cmd(cmd, deployment = false)
124
+ # Create a hash based on method above
220
125
  paths = Terragov::BuildPaths.new.base(cmd_options)
221
- varfiles = Terragov::BuildPaths.new.build_command(cmd_options)
222
- backend = paths[:backend_file]
223
- project_dir = paths[:project_dir]
224
126
 
225
- be_verbose = config('verbose', false, false)
226
-
227
- do_dryrun = config('dryrun', false, false)
127
+ # Construct the hash to pass to the Terraform class
128
+ terraform_args = {
129
+ command: cmd,
130
+ vars: Terragov::BuildPaths.new.build_command(cmd_options),
131
+ backend: paths[:backend_file],
132
+ directory: paths[:project_dir],
133
+ test: Terragov::Config.new.lookup({name: 'test', required: false, cli: @test}),
134
+ verbose: Terragov::Config.new.lookup({name: 'verbose', required: false, cli: @verbose}),
135
+ }
228
136
 
229
137
  unless deployment
230
- skip_check = config('skip_git_check', false, false)
138
+ skip_check = Terragov::Config.new.lookup({name: 'skip_git_check', required: false, cli: @skip_git_check})
231
139
  git_compare_repo_and_data(skip_check)
232
140
  end
233
141
 
234
- puts cmd_options.to_yaml if be_verbose
142
+ puts cmd_options.to_yaml if terraform_args[:verbose]
235
143
 
236
- cmd = "#{cmd} #{opt}" if opt
237
- Terragov::Terraform.new.execute(cmd, varfiles, backend, project_dir, do_dryrun, be_verbose)
144
+ Terragov::Terraform.new.execute(terraform_args)
238
145
  end
239
146
 
240
147
  def run_deployment(file, group, command, force)
@@ -249,23 +156,24 @@ module Terragov
249
156
  abort("Deployment configuration must be an array of projects to run")
250
157
  end
251
158
 
252
- if command == 'plan' || command == 'apply'
159
+ case command
160
+ when 'plan', 'apply'
253
161
  if force && command == 'apply'
254
162
  command = "#{command} -auto-approve"
255
163
  end
256
164
 
257
165
  deployment_config.each do |proj|
258
166
  $project = proj
259
- run_terraform_cmd(command, nil, true)
167
+ run_terraform_cmd(command, true)
260
168
  end
261
- elsif command == 'destroy'
169
+ when 'destroy'
262
170
  if force
263
171
  command = "#{command} -force"
264
172
  end
265
173
 
266
174
  deployment_config.reverse.each do |proj|
267
175
  $project = proj
268
- run_terraform_cmd(command, nil, true)
176
+ run_terraform_cmd(command, true)
269
177
  end
270
178
  else
271
179
  abort("Command must be apply, plan or destroy")
@@ -327,7 +235,7 @@ module Terragov
327
235
  c.description = 'Clean your directory of any files terraform may have left lying around'
328
236
  c.option '--force', 'Force removal of files'
329
237
  c.action do |_args, options|
330
- if config('verbose', false, false)
238
+ if Terragov::Config.new.lookup({name: 'verbose', required: false, cli: @verbose})
331
239
  puts "Selecting directory #{repo_dir}"
332
240
  end
333
241
 
@@ -336,7 +244,7 @@ module Terragov
336
244
  /terraform\.tfstate\.backup/,
337
245
  ]
338
246
 
339
- path = config('repo_dir', true)
247
+ path = Terragov::Config.new.lookup({name: 'repo_dir', file: true})
340
248
 
341
249
  Terragov::Cleaner.new.delete(path, files_to_delete, options.force)
342
250
  end
@@ -0,0 +1,86 @@
1
+ module Terragov
2
+ class Config
3
+ def project_name
4
+ $project || ENV['TERRAGOV_PROJECT']
5
+ end
6
+
7
+ def load_config_file
8
+ file = $config_file || ENV['TERRAGOV_CONFIG_FILE']
9
+ YAML.load_file(File.expand_path(file)) if file
10
+ end
11
+
12
+ def config_file_default
13
+ return nil if load_config_file['default'].nil?
14
+ load_config_file['default']
15
+ end
16
+
17
+ def config_file_specific_project(project_name)
18
+ load_config_file[project_name]
19
+ end
20
+
21
+ def config_file(option)
22
+ # If project name not specified in config, return default settings
23
+ if project_name.nil?
24
+ return nil if config_file_default.nil?
25
+ return config_file_default[option]
26
+ end
27
+
28
+ # If project specified, but no config found for that option return default
29
+ project_config = config_file_specific_project(project_name)
30
+ if project_config.nil? or project_config[option].nil?
31
+ return config_file_default[option]
32
+ end
33
+
34
+ # Otherwise return the value for that option
35
+ project_config[option]
36
+ end
37
+
38
+ def lookup(settings = {})
39
+ # Structure of hash should be:
40
+ #
41
+ # name: Name of the item to lookup
42
+ # cli: Result of CLI options
43
+ # required: whether or not it's required (default: true)
44
+ # file: whether to return a file path
45
+
46
+ default = {
47
+ cli: false,
48
+ required: true,
49
+ file: false,
50
+ }
51
+
52
+ settings = default.merge(settings)
53
+
54
+ env_var = "TERRAGOV_#{settings[:name].upcase}"
55
+ error_message = "Must set #{settings[:name]}. Use --help for details."
56
+
57
+ # Load from CLI option
58
+ if settings[:cli]
59
+ return File.expand_path(settings[:cli]) if settings[:file]
60
+ return settings[:cli]
61
+ end
62
+
63
+ # Load from environment variable
64
+ if ENV[env_var]
65
+ return File.expand_path(ENV[env_var]) if settings[:file]
66
+ return ENV[env_var]
67
+ end
68
+
69
+ # Return error/false if config_file isn't available
70
+ if load_config_file.nil?
71
+ abort(error_message) if settings[:required]
72
+ return false
73
+ end
74
+
75
+ # Return error/false if the specific option isn't available
76
+ if config_file(settings[:name]).nil?
77
+ abort(error_message) if settings[:required]
78
+ return false
79
+ end
80
+
81
+ # Otherwise return the value from the config file
82
+ return File.expand_path(config_file(settings[:name])) if settings[:file]
83
+ config_file(settings[:name])
84
+ end
85
+ end
86
+ end
@@ -11,41 +11,61 @@ module Terragov
11
11
  end
12
12
  end
13
13
 
14
- def execute(command, vars, backend, directory, dryrun = false, verbose = false)
14
+ def execute(_args = {})
15
+ # Schema:
16
+ # {
17
+ # command: which command to run
18
+ # test: set to true to output commands to run, used for testing
19
+ # verbose: set to true for more output
20
+ # directory: main repository directory
21
+ # backend: backend file
22
+ # vars: data directory
23
+ # }
24
+ default = {
25
+ test: false,
26
+ verbose: false,
27
+ }
28
+
29
+ args = default.merge(_args)
30
+
15
31
  packages = %w[terraform sops]
16
32
 
17
- packages.each do |pkg|
18
- package_check(pkg)
33
+ unless args[:test]
34
+ packages.each do |pkg|
35
+ package_check(pkg)
36
+ end
19
37
  end
20
38
 
21
- if command == 'init'
39
+ if args[:command] == 'init'
22
40
  puts "Running 'init' is not required as it is applied for each command"
23
41
  exit 1
24
42
  end
25
43
 
26
44
  current_dir = Dir.pwd
27
45
 
28
- Dir.chdir directory
29
- init(backend, dryrun, verbose)
46
+ Dir.chdir args[:directory] unless args[:test]
47
+ init(args[:backend], args[:test], args[:verbose])
30
48
 
31
- if command == 'plan'
49
+ if args[:command] == 'plan'
32
50
  command = 'plan -detailed-exitcode'
51
+ else
52
+ command = args[:command]
33
53
  end
34
54
 
35
- full_command = "bash -c 'terraform #{command} #{vars}'"
55
+ full_command = "bash -c 'terraform #{command} #{args[:vars]}'"
36
56
 
37
- run(full_command, dryrun, verbose)
57
+ run(full_command, args[:test], args[:verbose])
38
58
 
39
- Dir.chdir current_dir
59
+ Dir.chdir current_dir unless args[:test]
40
60
  end
41
61
 
42
- def init(backend_file, dryrun = false, verbose = false)
62
+ def init(backend_file, test = false, verbose = false)
43
63
  init_cmd = "terraform init -backend-config #{backend_file}"
44
- run(init_cmd, dryrun, verbose)
64
+ run(init_cmd, test, verbose)
45
65
  end
46
66
 
47
- def run(command, dryrun = false, verbose = false)
48
- if dryrun
67
+ def run(command, test = false, verbose = false)
68
+ if test
49
69
  puts command
50
70
  else
51
71
  puts command if verbose
@@ -1,3 +1,3 @@
1
1
  module Terragov
2
- VERSION = '0.3.3'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terragov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laura Martin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-30 00:00:00.000000000 Z
11
+ date: 2018-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -177,6 +177,7 @@ files:
177
177
  - lib/terragov/buildpaths.rb
178
178
  - lib/terragov/cleaner.rb
179
179
  - lib/terragov/cli.rb
180
+ - lib/terragov/config.rb
180
181
  - lib/terragov/git.rb
181
182
  - lib/terragov/terraform.rb
182
183
  - lib/terragov/version.rb