terragov 0.3.3 → 0.4.0

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