simplygenius-atmos 0.11.9 → 0.13.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
  SHA256:
3
- metadata.gz: 685baebb62604a3094114ff1616e1e044c0560ed9285c27686a4b8c3e635f964
4
- data.tar.gz: 2eadc8adc7ca8574de3d9f44d1af4d7668c17de8d9b6bee7f38c5f3234f47f63
3
+ metadata.gz: e57a5d360cd9c6016f83847333f3bf1519e70a8b73b0203c969d79fdf5a5e3f0
4
+ data.tar.gz: 4307c006f083fb94fdf5155df08a18a43d2ad6493070d279502b9a2a87a73c34
5
5
  SHA512:
6
- metadata.gz: 96249401ac017e427d0a5131f8ee441d51da30b91ae5da13a42bbddc258b1f7402c30411d5d304f2680c79e88d6e62eed8bfa26f0f70bd1b85eaf5ced69fd985
7
- data.tar.gz: 2025652bff07e78886a228f75e87183eb8b70ec0ec294223008f6abc4203ad9bf3cd617a39c06a521320812454c61e28d2a8c541eb9d5beb7c35b86bad4a3e3f
6
+ metadata.gz: d084865f58d1dfe8081debef0ce69c7fd2ab1068d813d8d704a4dc40962337c883be4763a2b161d5b8df4d0f6edd34a28209f95abb780b964d3abeb6dd7a94ab
7
+ data.tar.gz: 9f5f91afd5545dc14a073c4dfea67b86b699ed4add68077896cb151c990d50d32089246fe03b08b372eb98bb2694e98166933ac072fd22578e654a48771bc5b2
@@ -1,3 +1,63 @@
1
+ 0.13.0 (01/20/2021)
2
+ -------------------
3
+
4
+ #### Notes on major changes
5
+
6
+ * Now testing the atmos runtime against terraform 0.14 (and 0.11, 0.13)
7
+ * The ability to link files into the terraform execution directory was enhanced, but still doesn't work for the .terraform.lock.hcl as it is overwritten by terraform and doesn't follow the link
8
+
9
+ #### Full changelog
10
+
11
+ * exclude some new taskdef attributes for creation of new taskdef during deploy [a01b997](https://github.com/simplygenius/atmos/commit/a01b997)
12
+ * add debug logging for aws sdk logger [55dd303](https://github.com/simplygenius/atmos/commit/55dd303)
13
+ * directory sorting made more consistent across platforms [13622ff](https://github.com/simplygenius/atmos/commit/13622ff)
14
+ * switch ci/cd from travis to github actions [13622ff](https://github.com/simplygenius/atmos/commit/13622ff)
15
+ * fix test for tf 0.14 [122f861](https://github.com/simplygenius/atmos/commit/122f861)
16
+ * replace plugin sharing with built in terraform variant [eb75266](https://github.com/simplygenius/atmos/commit/eb75266)
17
+ * allow more options for linking in non-tf files into working dir [9878336](https://github.com/simplygenius/atmos/commit/9878336)
18
+ * sort secret list [6b2dbfe](https://github.com/simplygenius/atmos/commit/6b2dbfe)
19
+ * Only run terraform init when needed [d77186b](https://github.com/simplygenius/atmos/commit/d77186b)
20
+
21
+
22
+ 0.12.1 (10/14/2020)
23
+ -------------------
24
+
25
+ * fix bug with deeply nested overrides [60cb84a](https://github.com/simplygenius/atmos/commit/60cb84a)
26
+
27
+ 0.12.0 (10/12/2020)
28
+ -------------------
29
+
30
+ #### Notes on major changes
31
+
32
+ * Now testing the atmos runtime against terraform 0.13 (and 0.11, 0.12), but no changes were necessary, so it should work if you upgrade aside from recipe changes
33
+ * The fix to to make overrides (keys like ^foo to replace foo rather than add to it) work no matter where they appear in the config load chain could break compatibility if you were depending on the old behavior, i.e. you have multiple overrides for the same key with only the last one winning, or you have an override earlier in the change that was previously not taking effect
34
+
35
+ #### Full changelog
36
+
37
+ * Make overrides work no matter where they appear in the config load chain [47d73ba](https://github.com/simplygenius/atmos/commit/47d73ba)
38
+ * allow interpolation in (remote_)config_source references, e.g. for supplying an access key from ~/.atmos.yml in the url [43f0c4d](https://github.com/simplygenius/atmos/commit/43f0c4d)
39
+ * fix docker build [f4b3e2e](https://github.com/simplygenius/atmos/commit/f4b3e2e)
40
+ * fix stdin test [5bd3f95](https://github.com/simplygenius/atmos/commit/5bd3f95)
41
+ * exit with code on terraform error [2d2af2a](https://github.com/simplygenius/atmos/commit/2d2af2a)
42
+ * test against tf 0.1[123] [1bbfb3b](https://github.com/simplygenius/atmos/commit/1bbfb3b)
43
+ * add 'none' provider for tests (or if one wants to only run against terraform without aws) [991cefe](https://github.com/simplygenius/atmos/commit/991cefe)
44
+ * fix activesupport cve [3d8b30c](https://github.com/simplygenius/atmos/commit/3d8b30c)
45
+ * Add the ability to fetch atmos config from remote sources [b4b97fb](https://github.com/simplygenius/atmos/commit/b4b97fb)
46
+ * Add convenience (`atmos account setup_credentials`)for populating aws local credential stores with the aws accounts managed by atmos [05466bd](https://github.com/simplygenius/atmos/commit/05466bd)
47
+ * Add error msg for when task definition does't exist [0f8291e](https://github.com/simplygenius/atmos/commit/0f8291e)
48
+ * Fix `jsonify` to convert the correct value to JSON. (#4) [1bda229](https://github.com/simplygenius/atmos/commit/1bda229)
49
+ * Use ruby 2.4 for legacy testing since 2.3 is eol [78d2862](https://github.com/simplygenius/atmos/commit/78d2862)
50
+
51
+ 0.11.11 (02/20/2020)
52
+ --------------------
53
+
54
+ * Add the ability to setup a version requirement for forcing teams to use a consistent version of atmos [5c44ef3](https://github.com/simplygenius/atmos/commit/5c44ef3)
55
+
56
+ 0.11.10 (02/19/2020)
57
+ --------------------
58
+
59
+ * allow overriding atmos config from cli [f83902b](https://github.com/simplygenius/atmos/commit/f83902b)
60
+
1
61
  0.11.9 (02/13/2020)
2
62
  -------------------
3
63
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/simplygenius/atmos.svg?branch=master)](https://travis-ci.org/simplygenius/atmos)
1
+ [![Build Status](https://github.com/simplygenius/atmos/workflows/CD/badge.svg)](https://github.com/simplygenius/atmos/actions)
2
2
  [![Coverage Status](https://coveralls.io/repos/github/simplygenius/atmos/badge.svg?branch=master)](https://coveralls.io/github/simplygenius/atmos?branch=master)
3
3
  [![Gitter](https://badges.gitter.im/simplygenius/atmos.svg)](https://gitter.im/simplygenius/atmos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
4
4
 
@@ -2,6 +2,7 @@ require_relative '../atmos'
2
2
  require_relative '../atmos/ui'
3
3
  require 'clamp'
4
4
  require 'sigdump/setup'
5
+ require 'open-uri'
5
6
 
6
7
  Dir.glob(File.join(File.join(__dir__, 'commands'), '*.rb')) do |f|
7
8
  require_relative "commands/#{File.basename(f).sub(/\.rb$/, "")}"
@@ -51,6 +52,10 @@ module SimplyGenius
51
52
  "PATH", "adds additional paths to ruby load path",
52
53
  multivalued: true
53
54
 
55
+ option ["-o", "--override"],
56
+ "KEYVALUE", "overrides atmos configuration\nin the form 'some.config=value' where value can\nbe expressed in yaml form for complex types\ne.g. foo=1 foo=abc, foo=[x, y], foo={x: y}",
57
+ multivalued: true
58
+
54
59
  option ["-v", "--version"],
55
60
  :flag, "Shows the atmos version"
56
61
 
@@ -115,6 +120,41 @@ module SimplyGenius
115
120
  end
116
121
  end
117
122
 
123
+ def fetch_latest_version
124
+ begin
125
+ latest_ver = JSON.parse(URI.open("https://rubygems.org/api/v1/versions/simplygenius-atmos/latest.json").read)['version']
126
+ rescue => e
127
+ latest_ver = "[Version Fetch Failed]"
128
+ logger.log_exception(e, "Couldn't check latest atmos gem version", level: :debug)
129
+ end
130
+ latest_ver
131
+ end
132
+
133
+ def version_check(atmos_version)
134
+
135
+ required_ver = Atmos.config["atmos.version_requirement"]
136
+ if required_ver.present?
137
+ case required_ver
138
+
139
+ when "latest"
140
+ latest_ver = fetch_latest_version
141
+
142
+ if latest_ver != atmos_version
143
+ raise "The atmos version (#{atmos_version}) does not match the given requirement (latest: #{latest_ver})"
144
+ end
145
+
146
+ when /[~<>=]*\s*[\d\.]*/
147
+ if ! Gem::Dependency.new('', required_ver).match?('', atmos_version)
148
+ raise "The atmos version (#{atmos_version}) does not match the given requirement (#{required_ver})"
149
+ end
150
+
151
+ else
152
+ raise "Invalid atmos.version_requirement, should be 'latest' or in a gem dependency form"
153
+ end
154
+ end
155
+
156
+ end
157
+
118
158
  # hook into clamp lifecycle to force logging setup even when we are calling
119
159
  # a subcommand
120
160
  def parse(arguments)
@@ -128,6 +168,13 @@ module SimplyGenius
128
168
 
129
169
  Logging.setup_logging(level, color?, log)
130
170
 
171
+ override_list.each do |o|
172
+ k, v = o.split("=")
173
+ v = YAML.load(v)
174
+ logger.debug("Overriding config '#{k}' = #{v.inspect}")
175
+ Atmos.config.[]=(k, v, additive: false)
176
+ end
177
+
131
178
  UI.color_enabled = color?
132
179
 
133
180
  Atmos.config.add_user_load_path(*load_path_list)
@@ -138,6 +185,8 @@ module SimplyGenius
138
185
  logger.info "Atmos Version #{VERSION}"
139
186
  exit(0)
140
187
  end
188
+
189
+ version_check(VERSION)
141
190
  end
142
191
  end
143
192
 
@@ -62,6 +62,42 @@ module SimplyGenius
62
62
  end
63
63
  end
64
64
 
65
+ subcommand "setup_credentials", "Convenience that adds accounts to the local aws\ncredentials store" do
66
+
67
+ option ["-u", "--user"],
68
+ "USERNAME", "The username in the cloud provider\n", required: true
69
+
70
+ option ["-k", "--key"],
71
+ "KEY", "The access key in the cloud provider\n"
72
+
73
+ option ["-s", "--secret"],
74
+ "SECRET", "The access secret in the cloud provider\n"
75
+
76
+ option ["-d", "--default"],
77
+ :flag, "Sets as default credentials\n"
78
+
79
+ option ["-f", "--force"],
80
+ :flag, "Forces overwrites of existing\n"
81
+
82
+ option ["-n", "--nowrite"],
83
+ :flag, "Trial run without writing results to files\n"
84
+
85
+ def execute
86
+
87
+ if ! key.present?
88
+ key = ask("Input your access key: ") { |q| q.echo = "*" }
89
+ raise ArgumentError.new("An access key is required") if key.blank?
90
+ end
91
+ if ! secret.present?
92
+ secret = ask("Input your access secret: ") { |q| q.echo = "*" }
93
+ raise ArgumentError.new("An access secret is required") if secret.blank?
94
+ end
95
+
96
+ Atmos.config.provider.account_manager.setup_credentials(username: user, access_key: key, access_secret: secret,
97
+ become_default: default?, force: force?, nowrite: nowrite?)
98
+ end
99
+ end
100
+
65
101
  end
66
102
 
67
103
  end
@@ -68,6 +68,7 @@ module SimplyGenius
68
68
  rescue TerraformExecutor::ProcessFailed => e
69
69
  logger.error(e.message)
70
70
  logger.error(rebootstrap_msg)
71
+ exit(1)
71
72
  end
72
73
  end
73
74
  end
@@ -15,8 +15,6 @@ module SimplyGenius
15
15
  def execute
16
16
  @terraform_arguments.insert(0, "init")
17
17
  super
18
-
19
- init_shared_plugins
20
18
  end
21
19
 
22
20
  end
@@ -60,7 +60,7 @@ module SimplyGenius
60
60
  Atmos.config.provider.auth_manager.authenticate(ENV) do |auth_env|
61
61
  ClimateControl.modify(auth_env) do
62
62
  logger.info "Secret keys are:"
63
- Atmos.config.provider.secret_manager.to_h.keys.each {|k| logger.info k}
63
+ Atmos.config.provider.secret_manager.to_h.keys.sort.each {|k| logger.info k}
64
64
  end
65
65
  end
66
66
 
@@ -21,27 +21,23 @@ module SimplyGenius
21
21
  end
22
22
 
23
23
  def init_automatically(auth_env, get_modules)
24
- tf_init_dir = File.join(Atmos.config.tf_working_dir, '.terraform')
24
+ tf_init_dir = File.join(Atmos.config.tf_working_dir, 'recipes', '.terraform')
25
25
  backend_initialized = File.exist?(File.join(tf_init_dir, 'terraform.tfstate'))
26
26
  auto_init_enabled = Atmos.config["atmos.terraform.auto_init"].to_s == "true"
27
27
 
28
28
  if auto_init && auto_init_enabled && ! backend_initialized
29
29
  exe = TerraformExecutor.new(process_env: auth_env)
30
30
  exe.run("init", get_modules: get_modules.present?)
31
- init_shared_plugins
32
31
  end
33
32
  end
34
33
 
35
- def init_shared_plugins
34
+ def enable_shared_plugins(env)
36
35
  if ! Atmos.config["atmos.terraform.disable_shared_plugins"]
37
36
  home_dir = OS.windows? ? File.join("~", "Application Data") : "~"
38
- shared_plugins_dir = File.expand_path(File.join(home_dir,".terraform.d", "plugins"))
39
- logger.debug("Updating shared terraform plugins dir: #{shared_plugins_dir}")
40
- mkdir_p(shared_plugins_dir)
41
- terraform_plugins_dir = File.join(Atmos.config.tf_working_dir,'recipes', '.terraform', 'plugins')
42
- if File.exist?(terraform_plugins_dir)
43
- cp_r("#{terraform_plugins_dir}/.", shared_plugins_dir)
44
- end
37
+ plugin_cache_dir = File.expand_path(File.join(home_dir,".terraform.d", "plugin-cache"))
38
+ logger.debug("Plugin cache dir: #{plugin_cache_dir}")
39
+ mkdir_p(plugin_cache_dir)
40
+ env["TF_PLUGIN_CACHE_DIR"] = plugin_cache_dir
45
41
  end
46
42
  end
47
43
 
@@ -58,12 +54,15 @@ module SimplyGenius
58
54
  begin
59
55
  get_modules = @terraform_arguments.delete("--get-modules")
60
56
 
57
+ enable_shared_plugins(auth_env)
58
+
61
59
  init_automatically(auth_env, get_modules)
62
60
 
63
61
  exe = TerraformExecutor.new(process_env: auth_env)
64
62
  exe.run(*@terraform_arguments, get_modules: get_modules.present?)
65
63
  rescue TerraformExecutor::ProcessFailed => e
66
64
  logger.error(e.message)
65
+ exit(1)
67
66
  end
68
67
  end
69
68
  end
@@ -61,7 +61,7 @@ module SimplyGenius
61
61
  result[k] = ev
62
62
  end
63
63
  else
64
- result["data"] = JSON.generate(result)
64
+ result["data"] = JSON.generate(obj)
65
65
  end
66
66
 
67
67
  return result
@@ -73,9 +73,9 @@ module SimplyGenius
73
73
  chunk = $stdin.read_nonblock(1)
74
74
  data = chunk + $stdin.read
75
75
  logger.debug("Received stdin: " + data)
76
- rescue Errno::EAGAIN
76
+ rescue EOFError, SystemCallError => e # All Errno exceptions
77
77
  data = nil
78
- logger.debug("No stdin")
78
+ logger.debug("No stdin due to: #{e}")
79
79
  end
80
80
  return data
81
81
  end
@@ -5,6 +5,7 @@ require_relative '../atmos/plugin_manager'
5
5
  require 'yaml'
6
6
  require 'fileutils'
7
7
  require 'find'
8
+ require 'open-uri'
8
9
 
9
10
  module SimplyGenius
10
11
  module Atmos
@@ -26,7 +27,7 @@ module SimplyGenius
26
27
  @config_file = config ? File.expand_path(config, root_dir) : File.join(root_dir, "config", "atmos.yml")
27
28
  @user_config_file = "~/.atmos.yml"
28
29
  @tmp_root = File.join(root_dir, "tmp")
29
- @included_configs = []
30
+ @included_configs = {}
30
31
  end
31
32
 
32
33
  def is_atmos_repo?
@@ -39,9 +40,9 @@ module SimplyGenius
39
40
  return result
40
41
  end
41
42
 
42
- def []=(key, value)
43
+ def []=(key, value, additive: true)
43
44
  load
44
- result = @config.notation_put(key, value)
45
+ result = @config.notation_put(key, value, additive: additive)
45
46
  return result
46
47
  end
47
48
 
@@ -114,6 +115,7 @@ module SimplyGenius
114
115
  if merge_to_existing
115
116
  existing = load_file(user_config_file, SettingsHash.new)
116
117
  data = config_merge(existing, data, ["saving #{user_config_file}"])
118
+ data = finalize_merge(data)
117
119
  end
118
120
  File.write(user_config_file, YAML.dump(data.to_hash))
119
121
  File.chmod(0600, user_config_file)
@@ -132,21 +134,27 @@ module SimplyGenius
132
134
  when Hash
133
135
  result = lhs.deep_dup
134
136
 
135
- lhs.each do |k, v|
136
- if k =~ /^\^(.*)/
137
- key = k.is_a?(Symbol) ? $1.to_sym : $1
138
- result[key] = result.delete(k)
139
- end
140
- end
141
-
142
137
  rhs.each do |k, v|
143
- if k =~ /^\^(.*)/
144
- key = k.is_a?(Symbol) ? $1.to_sym : $1
145
- result[key] = v
146
- else
147
- result[k] = config_merge(result[k], v, debug_state + [k])
138
+ new_state = debug_state + [k]
139
+
140
+ # The load sequence could have multiple overrides and its not
141
+ # obvious to the user which are lhs vs rhs, so rather than having
142
+ # one seem to win arbitrarily, we collect them all additively
143
+ # under their key, then at the end of the load process we push the
144
+ # override back as a replacement for its base key in the
145
+ # finalize_merge method. This means that if one has multiple
146
+ # overrides for the same key (not usually what one wants), those
147
+ # overrides get merged together additively before replacing the
148
+ # base. Thus the need for the log messages below.
149
+ #
150
+ if k =~ /^\^/
151
+ logger.debug { "Override seen at #{new_state.join(" -> ")}" }
152
+ logger.warn { "Multiple overrides on a single key seen at #{new_state.join(" -> ")}" } if result.has_key?(k)
148
153
  end
154
+
155
+ result[k] = config_merge(result[k], v, new_state)
149
156
  end
157
+
150
158
  when Enumerable
151
159
  result = lhs + rhs
152
160
  else
@@ -168,6 +176,37 @@ module SimplyGenius
168
176
 
169
177
  private
170
178
 
179
+ def finalize_merge(config)
180
+ result = config.deep_dup
181
+
182
+ config.each do |k, v|
183
+
184
+ key = k
185
+
186
+ if k =~ /^\^(.*)/
187
+ key = k.is_a?(Symbol) ? $1.to_sym : $1
188
+ result[key] = result.delete(k)
189
+ end
190
+
191
+ if v.is_a?(Hash)
192
+ result[key] = finalize_merge(v)
193
+ end
194
+
195
+ end
196
+
197
+ return result
198
+ end
199
+
200
+ def load_remote_config_sources(config, *remote_sources)
201
+ remote_sources.each do |remote_source|
202
+ logger.debug("Loading remote atmos config file: #{remote_source}")
203
+ contents = URI.open(remote_source).read
204
+ config = load_config(remote_source, contents, config)
205
+ end
206
+
207
+ config
208
+ end
209
+
171
210
  def load_config_sources(relative_root, config, *patterns)
172
211
  patterns.each do |pattern|
173
212
  logger.debug("Loading atmos config files using pattern: #{pattern}")
@@ -180,7 +219,7 @@ module SimplyGenius
180
219
  pattern = File.expand_path(pattern)
181
220
  logger.debug("Expanded pattern: #{pattern}")
182
221
 
183
- Dir[pattern].each do |f|
222
+ Dir[pattern].sort.each do |f|
184
223
  config = load_file(f, config)
185
224
  end
186
225
  end
@@ -217,26 +256,26 @@ module SimplyGenius
217
256
  @full_config = load_file(config_file, SettingsHash.new)
218
257
  end
219
258
 
220
- @full_config = load_config_sources(File.dirname(config_file), @full_config, *Array(@full_config.notation_get("atmos.config_sources")))
259
+ @user_config_file = @full_config.notation_get("atmos.user_config") || @user_config_file
260
+ @user_config_file = File.expand_path(@user_config_file)
261
+ user_config_file_data = load_file(@user_config_file)
262
+ temp_settings = create_settings(config_merge(@full_config, user_config_file_data, [@user_config_file]), finalize: true)
263
+
264
+ @full_config = load_config_sources(File.dirname(config_file), @full_config, *Array(temp_settings.notation_get("atmos.config_sources")))
265
+ @full_config = load_remote_config_sources(@full_config, *Array(temp_settings.notation_get("atmos.remote_config_sources")))
221
266
 
222
267
  @full_config['provider'] = provider_name = @full_config['provider'] || 'aws'
223
268
 
224
269
  @full_config = load_submap(File.dirname(config_file), 'providers', provider_name, @full_config)
225
270
  @full_config = load_submap(File.dirname(config_file), 'environments', atmos_env, @full_config)
226
271
 
227
- @user_config_file = @full_config.notation_get("atmos.user_config") || @user_config_file
228
- @user_config_file = File.expand_path(@user_config_file)
229
- @full_config = load_file(user_config_file, @full_config)
272
+ @full_config = config_merge(@full_config, user_config_file_data, [@user_config_file])
230
273
 
231
- global = SettingsHash.new(@full_config.reject {|k, v| ['providers', 'environments'].include?(k) })
232
- conf = config_merge(global, {
233
- atmos_env: atmos_env,
234
- atmos_working_group: working_group,
235
- atmos_version: VERSION
236
- }, ["builtins"])
274
+ conf = create_settings(@full_config, finalize: true)
275
+
276
+ # hash emptied out to allow GC of all loaded file contents
277
+ @included_configs = {}
237
278
 
238
- conf.error_resolver = ->(statement) { find_config_error(statement) }
239
- conf.enable_expansion = true
240
279
  conf
241
280
 
242
281
  end
@@ -245,15 +284,8 @@ module SimplyGenius
245
284
  def load_file(file, config=SettingsHash.new, &block)
246
285
  if File.exist?(file)
247
286
  logger.debug("Loading atmos config file #{file}")
248
- data = YAML.load_file(file)
249
- if ! data.is_a?(Hash)
250
- logger.debug("Skipping invalid atmos config file (not hash-like): #{file}")
251
- else
252
- data = SettingsHash.new(data)
253
- data = block.call(data) if block
254
- config = config_merge(config, data, [file])
255
- @included_configs << file
256
- end
287
+ contents = File.read(file)
288
+ config = load_config(file, contents, config, &block)
257
289
  else
258
290
  logger.debug "Could not find an atmos config file at: #{file}"
259
291
  end
@@ -261,16 +293,49 @@ module SimplyGenius
261
293
  config
262
294
  end
263
295
 
296
+ def load_config(location, yml_string, config=SettingsHash.new, &block)
297
+ data = YAML.load(yml_string)
298
+
299
+ if ! data.is_a?(Hash)
300
+ logger.debug("Skipping invalid atmos config (not hash-like): #{location}")
301
+ else
302
+ data = SettingsHash.new(data)
303
+ data = block.call(data) if block
304
+ # if lhs has a override ^, then it loses it when rhs gets merged in, which breaks things for subsequent merges
305
+ config = config_merge(config, data, [location])
306
+ @included_configs[location] = yml_string
307
+ end
308
+
309
+ config
310
+ end
311
+
312
+ def create_settings(config, finalize: true)
313
+ builtins = {
314
+ atmos_env: atmos_env,
315
+ atmos_working_group: working_group,
316
+ atmos_version: VERSION
317
+ }
318
+
319
+ global = SettingsHash.new(config.reject {|k, v| ['providers', 'environments'].include?(k) })
320
+ conf = config_merge(global, builtins, ["builtins"])
321
+ conf = finalize_merge(conf) if finalize
322
+
323
+ conf.error_resolver = ->(statement) { find_config_error(statement) }
324
+ conf.enable_expansion = true
325
+
326
+ conf
327
+ end
328
+
264
329
  def find_config_error(statement)
265
330
  filename = nil
266
331
  line = 0
267
332
 
268
- @included_configs.each do |c|
333
+ @included_configs.each do |location, contents|
269
334
  current_line = 0
270
- File.foreach(c) do |f|
335
+ contents.each_line do |line|
271
336
  current_line += 1
272
- if f.include?(statement)
273
- filename = c
337
+ if line.include?(statement)
338
+ filename = location
274
339
  line = current_line
275
340
  break
276
341
  end
@@ -7,14 +7,15 @@ module SimplyGenius
7
7
  include GemLogger::LoggerSupport
8
8
 
9
9
  def self.get(name)
10
- @provider ||= begin
10
+ @providers ||= {}
11
+ provider = @providers[name] ||= begin
11
12
  logger.debug("Loading provider: #{name}")
12
13
  require "simplygenius/atmos/providers/#{name}/provider"
13
- provider = "SimplyGenius::Atmos::Providers::#{name.camelize}::Provider".constantize
14
- logger.debug("Loaded provider #{provider}")
15
- provider.new(name)
14
+ provider_class = "SimplyGenius::Atmos::Providers::#{name.camelize}::Provider".constantize
15
+ logger.debug("Loaded provider #{provider_class}")
16
+ provider_class.new(name)
16
17
  end
17
- return @provider
18
+ return provider
18
19
  end
19
20
 
20
21
  end
@@ -1,5 +1,6 @@
1
1
  require_relative '../../../atmos'
2
2
  require 'aws-sdk-organizations'
3
+ require 'inifile'
3
4
 
4
5
  module SimplyGenius
5
6
  module Atmos
@@ -75,6 +76,76 @@ module SimplyGenius
75
76
  return result
76
77
  end
77
78
 
79
+ def setup_credentials(username:, access_key:, access_secret:, become_default: false, force: false, nowrite: false)
80
+ creds = File.expand_path("~/.aws/credentials")
81
+ config = File.expand_path("~/.aws/config")
82
+
83
+ creds_data = IniFile.load(creds) || IniFile.new
84
+ config_data = IniFile.load(config) || IniFile.new
85
+
86
+ org = Atmos.config["org"]
87
+ accounts = Atmos.config.account_hash
88
+
89
+ # Our entrypoint account (the ops env/account) is the account we
90
+ # have credentials for, and typically one authenticate using the
91
+ # credentials for that account, then assume role (<env>-admin) to
92
+ # actually operate in each env, even for the ops account
93
+ ops_config = Atmos::Config.new("ops")
94
+ entrypoint_section = become_default ? "default" : org
95
+ entrypoint_config_section = become_default ? "default" : "profile #{org}"
96
+ write_entrypoint = true
97
+ if config_data.has_section?(entrypoint_config_section) || creds_data.has_section?(entrypoint_section)
98
+ if force
99
+ logger.info "Overwriting pre-existing sections: '#{entrypoint_config_section}'/'#{entrypoint_section}'"
100
+ else
101
+ logger.info "Skipping pre-existing sections (use force to overwrite): '#{entrypoint_config_section}'/'#{entrypoint_section}'"
102
+ write_entrypoint = false
103
+ end
104
+ end
105
+ if write_entrypoint
106
+ config_data[entrypoint_config_section]["region"] = ops_config["region"]
107
+ creds_data[entrypoint_section]["aws_access_key_id"] = access_key
108
+ creds_data[entrypoint_section]["aws_secret_access_key"] = access_secret
109
+ creds_data[entrypoint_section]["mfa_serial"] = "arn:aws:iam::#{accounts["ops"]}:mfa/#{username}"
110
+ end
111
+
112
+ accounts.each do |env, account_id|
113
+ env_config = Atmos::Config.new(env)
114
+
115
+ section = "#{org}-#{env}"
116
+ config_section = "profile #{section}"
117
+
118
+ if config_data.has_section?(config_section) || creds_data.has_section?(section)
119
+ if force
120
+ logger.info "Overwriting pre-existing sections: '#{config_section}'/'#{section}'"
121
+ else
122
+ logger.info "Skipping pre-existing sections (use force to overwrite): '#{config_section}'/'#{section}'"
123
+ next
124
+ end
125
+ end
126
+
127
+ config_data[config_section]["source_profile"] = entrypoint_section
128
+ role_name = env_config["auth.assume_role_name"]
129
+ config_data[config_section]["role_arn"] = "arn:aws:iam::#{account_id}:role/#{role_name}"
130
+ end
131
+
132
+ if nowrite
133
+ logger.info "Trial run only, would write the following:\n"
134
+ puts "*** #{config}:\n\n"
135
+ puts config_data.to_s
136
+
137
+ puts "\n\n*** #{creds}:\n\n"
138
+ puts creds_data.to_s
139
+ else
140
+ logger.info "Writing credentials to disk"
141
+ mkdir_p(File.dirname(config))
142
+ mv config, "#{config}.bak" if File.exist?(config)
143
+ mv creds, "#{creds}.bak" if File.exist?(creds)
144
+ config_data.write(filename: config)
145
+ creds_data.write(filename: creds)
146
+ end
147
+
148
+ end
78
149
  end
79
150
 
80
151
  end
@@ -64,7 +64,8 @@ module SimplyGenius
64
64
 
65
65
  new_defn = latest_defn.to_h
66
66
  [:revision, :status, :task_definition_arn,
67
- :requires_attributes, :compatibilities].each do |attr|
67
+ :requires_attributes, :compatibilities,
68
+ :registered_at, :registered_by].each do |attr|
68
69
  new_defn.delete(attr)
69
70
  end
70
71
  new_defn[:container_definitions].each {|c| c[:image] = remote_image}
@@ -198,6 +199,8 @@ module SimplyGenius
198
199
  logger.info "Running task as '#{task_opts[:launch_type]}'"
199
200
  end
200
201
 
202
+ raise "Could not find a task definition in AWS for '#{name}'" if defn_arn.blank?
203
+
201
204
  resp = ecs.describe_task_definition(task_definition: defn_arn)
202
205
  defn = resp.task_definition
203
206
  raise "Invalid Launch type '#{launch_type}'" unless (defn.requires_compatibilities + defn.compatibilities).include?(launch_type)
@@ -1,5 +1,7 @@
1
1
  require_relative '../../../atmos'
2
2
 
3
+ require 'aws-sdk-core'
4
+
3
5
  Dir.glob(File.join(__dir__, '*.rb')) do |f|
4
6
  require_relative "#{File.basename(f).sub(/\.rb$/, "")}"
5
7
  end
@@ -11,6 +13,7 @@ module SimplyGenius
11
13
 
12
14
  class Provider
13
15
  include GemLogger::LoggerSupport
16
+ ::Aws.config.update(logger: logger, log_level: :debug)
14
17
 
15
18
  def initialize(name)
16
19
  @name = name
@@ -0,0 +1,24 @@
1
+ require_relative '../../../atmos'
2
+
3
+ module SimplyGenius
4
+ module Atmos
5
+ module Providers
6
+ module None
7
+
8
+ class AuthManager
9
+ include GemLogger::LoggerSupport
10
+
11
+ def initialize(provider)
12
+ @provider = provider
13
+ end
14
+
15
+ def authenticate(system_env, **opts, &block)
16
+ logger.debug("Calling none authentication target")
17
+ block.call(system_env)
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../../../atmos'
2
+
3
+ Dir.glob(File.join(__dir__, '*.rb')) do |f|
4
+ require_relative "#{File.basename(f).sub(/\.rb$/, "")}"
5
+ end
6
+
7
+ module SimplyGenius
8
+ module Atmos
9
+ module Providers
10
+ module None
11
+
12
+ class Provider
13
+ include GemLogger::LoggerSupport
14
+
15
+ def initialize(name)
16
+ @name = name
17
+ end
18
+
19
+ def auth_manager
20
+ @auth_manager ||= begin
21
+ AuthManager.new(self)
22
+ end
23
+ end
24
+
25
+ def user_manager
26
+ raise NotImplementedError.new("No user manager in none provider")
27
+ end
28
+
29
+ def account_manager
30
+ raise NotImplementedError.new("No account manager in none provider")
31
+ end
32
+
33
+ def secret_manager
34
+ @secret_manager ||= begin
35
+ SecretManager.new(self)
36
+ end
37
+ end
38
+
39
+ def container_manager
40
+ raise NotImplementedError.new("No container manager in none provider")
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,40 @@
1
+ require_relative '../../../atmos'
2
+
3
+ module SimplyGenius
4
+ module Atmos
5
+ module Providers
6
+ module None
7
+
8
+ class SecretManager
9
+ include GemLogger::LoggerSupport
10
+
11
+ def initialize(provider)
12
+ @provider = provider
13
+ @secrets = {}
14
+ end
15
+
16
+ def set(key, value, force: false)
17
+ if @secrets.has_key?(key) && ! force
18
+ raise "A value already exists for the given key, force overwrite or delete first"
19
+ end
20
+ @secrets[key] = value
21
+ end
22
+
23
+ def get(key)
24
+ @secrets[key]
25
+ end
26
+
27
+ def delete(key)
28
+ @secrets.delete(key)
29
+ end
30
+
31
+ def to_h
32
+ @secrets.to_h
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -113,7 +113,7 @@ module SimplyGenius
113
113
  begin
114
114
  logger.debug("Cloning zip archive to tmpdir")
115
115
 
116
- open(sourcepath, 'rb') do |io|
116
+ URI.open(sourcepath, 'rb') do |io|
117
117
  Zip::File.open_buffer(io) do |zip_file|
118
118
  zip_file.each do |f|
119
119
  fpath = File.join(tmpdir, f.name)
@@ -308,7 +308,7 @@ module SimplyGenius
308
308
 
309
309
  def clean_links
310
310
  Find.find(Atmos.config.tf_working_dir) do |f|
311
- Find.prune if f =~ /\/.terraform\/modules\//
311
+ Find.prune if f =~ /\/.terraform\//
312
312
  File.delete(f) if File.symlink?(f)
313
313
  end
314
314
  end
@@ -318,13 +318,30 @@ module SimplyGenius
318
318
  working_dir_links ||= ['modules', 'templates']
319
319
  working_dir_links.each do |subdir|
320
320
  source = File.join(Atmos.config.root_dir, subdir)
321
- ln_sf(source, Atmos.config.tf_working_dir) if File.exist?(source)
321
+ parts = File.split(subdir)
322
+ target_dir = Atmos.config.tf_working_dir
323
+ if parts[0] != "."
324
+ target_dir = File.join(Atmos.config.tf_working_dir, parts[0])
325
+ mkdir_p(target_dir)
326
+ end
327
+ ln_sf(source, target_dir) if File.exist?(source)
322
328
  end
323
329
  end
324
330
 
325
331
  def link_recipes
326
332
  @recipes.each do |recipe|
327
- ln_sf(File.join(Atmos.config.root_dir, 'recipes', "#{recipe}.tf"), tf_recipes_dir)
333
+ recipe_satisfied = false
334
+ ["#{recipe}.tf", recipe].each do |recipe_variant|
335
+ fqrecipe = File.join(Atmos.config.root_dir, 'recipes', recipe_variant)
336
+ if File.exist?(fqrecipe)
337
+ ln_sf(fqrecipe, tf_recipes_dir)
338
+ recipe_satisfied = true
339
+ break
340
+ else
341
+ logger.debug("Recipe target does not exist: #{fqrecipe}")
342
+ end
343
+ end
344
+ logger.error("Recipe '#{recipe}' is not present") unless recipe_satisfied
328
345
  end
329
346
  end
330
347
 
@@ -1,5 +1,5 @@
1
1
  module SimplyGenius
2
2
  module Atmos
3
- VERSION = "0.11.9"
3
+ VERSION = "0.13.0"
4
4
  end
5
5
  end
@@ -67,4 +67,9 @@ atmos:
67
67
  # to the directory containing atmos.yml
68
68
  #
69
69
  config_sources:
70
- - atmos/*.y{,a}ml
70
+ - atmos/*.y{,a}ml
71
+ # A list of remote atmos configuration yml files.
72
+ # Takes any URI supported by ruby's open-uri
73
+ #
74
+ remote_config_sources:
75
+ # - https://some.url/to/config.yml
@@ -35,6 +35,12 @@ atmos:
35
35
  # gets expanded at runtime
36
36
  load_path:
37
37
 
38
+ # Forces one to use the version of atmos that is given. This version
39
+ # specifier can be 'latest' or in the form of a gem version dependency
40
+ # specifier, e.g. '~> 0.11.10'. If unset, then no version check will be
41
+ # performed
42
+ version_requirement:
43
+
38
44
  # Configure the mechanism that allows terraform to callback into atmos
39
45
  ipc:
40
46
  # Disables all IPC callbacks into atmos from terraform
@@ -113,12 +119,12 @@ atmos:
113
119
  terraform:
114
120
  # Disable module fetch from convenience plan/apply commands
115
121
  disable_auto_modules: false
116
- # By default (value=false), `atmos init` will update the terraform user
117
- # plugin directory (~/.terraform.d/plugins) with the plugins for the current
118
- # env/group so that they can be reused across all env/group combinations.
119
- # Otherwise, disabling this functionality (value=true) means that each
120
- # env/group combination will be independent and download all plugins for
121
- # itself only
122
+ # By default (value=false), `atmos init` (and auto init) will set
123
+ # TF_PLUGIN_CACHE_DIR to ~/.terraform.d/plugin-cache so that when terraform
124
+ # installs providers/plugins they can be reused across all env/group
125
+ # combinations. Otherwise, disabling this functionality (value=true) means
126
+ # that each env/group combination will be independent and download all
127
+ # plugins for itself only
122
128
  disable_shared_plugins: false
123
129
  # Customize what gets linked into the working directory that terraform gets
124
130
  # executed in
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplygenius-atmos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.9
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Conway
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-13 00:00:00.000000000 Z
11
+ date: 2021-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,142 +28,156 @@ dependencies:
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 12.3.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 12.3.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.7'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.7'
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: concurrent-ruby
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: simplecov
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - "~>"
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
- version: '0.10'
75
+ version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - "~>"
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
- version: '0.10'
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: coveralls
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - "~>"
87
+ - - ">="
74
88
  - !ruby/object:Gem::Version
75
- version: '0.8'
89
+ version: 0.8.23
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - "~>"
94
+ - - ">="
81
95
  - !ruby/object:Gem::Version
82
- version: '0.8'
96
+ version: 0.8.23
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: test_construct
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
- - - "~>"
101
+ - - ">="
88
102
  - !ruby/object:Gem::Version
89
- version: 2.0.1
103
+ version: '0'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
- - - "~>"
108
+ - - ">="
95
109
  - !ruby/object:Gem::Version
96
- version: 2.0.1
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: vcr
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - "~>"
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
- version: 4.0.0
117
+ version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - "~>"
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
- version: 4.0.0
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: webmock
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - "~>"
129
+ - - ">="
116
130
  - !ruby/object:Gem::Version
117
- version: 3.3.0
131
+ version: '0'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - "~>"
136
+ - - ">="
123
137
  - !ruby/object:Gem::Version
124
- version: 3.3.0
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: pry
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - "~>"
143
+ - - ">="
130
144
  - !ruby/object:Gem::Version
131
- version: 0.11.3
145
+ version: '0'
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - "~>"
150
+ - - ">="
137
151
  - !ruby/object:Gem::Version
138
- version: 0.11.3
152
+ version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: pry-byebug
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
- - - "~>"
157
+ - - ">="
144
158
  - !ruby/object:Gem::Version
145
- version: 3.6.0
159
+ version: '0'
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
- - - "~>"
164
+ - - ">="
151
165
  - !ruby/object:Gem::Version
152
- version: 3.6.0
166
+ version: '0'
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: activesupport
155
169
  requirement: !ruby/object:Gem::Requirement
156
170
  requirements:
157
- - - "~>"
171
+ - - ">="
158
172
  - !ruby/object:Gem::Version
159
- version: 5.2.1
173
+ version: 5.2.4.3
160
174
  type: :runtime
161
175
  prerelease: false
162
176
  version_requirements: !ruby/object:Gem::Requirement
163
177
  requirements:
164
- - - "~>"
178
+ - - ">="
165
179
  - !ruby/object:Gem::Version
166
- version: 5.2.1
180
+ version: 5.2.4.3
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: gem_logger
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -472,6 +486,20 @@ dependencies:
472
486
  - - "~>"
473
487
  - !ruby/object:Gem::Version
474
488
  version: 1.1.2
489
+ - !ruby/object:Gem::Dependency
490
+ name: inifile
491
+ requirement: !ruby/object:Gem::Requirement
492
+ requirements:
493
+ - - "~>"
494
+ - !ruby/object:Gem::Version
495
+ version: 3.0.0
496
+ type: :runtime
497
+ prerelease: false
498
+ version_requirements: !ruby/object:Gem::Requirement
499
+ requirements:
500
+ - - "~>"
501
+ - !ruby/object:Gem::Version
502
+ version: 3.0.0
475
503
  - !ruby/object:Gem::Dependency
476
504
  name: deepsort
477
505
  requirement: !ruby/object:Gem::Requirement
@@ -558,6 +586,9 @@ files:
558
586
  - lib/simplygenius/atmos/providers/aws/s3_secret_manager.rb
559
587
  - lib/simplygenius/atmos/providers/aws/ssm_secret_manager.rb
560
588
  - lib/simplygenius/atmos/providers/aws/user_manager.rb
589
+ - lib/simplygenius/atmos/providers/none/auth_manager.rb
590
+ - lib/simplygenius/atmos/providers/none/provider.rb
591
+ - lib/simplygenius/atmos/providers/none/secret_manager.rb
561
592
  - lib/simplygenius/atmos/settings_hash.rb
562
593
  - lib/simplygenius/atmos/source_path.rb
563
594
  - lib/simplygenius/atmos/template.rb
@@ -574,7 +605,7 @@ homepage: https://github.com/simplygenius/atmos
574
605
  licenses:
575
606
  - Apache-2.0
576
607
  metadata: {}
577
- post_install_message:
608
+ post_install_message:
578
609
  rdoc_options: []
579
610
  require_paths:
580
611
  - lib
@@ -589,8 +620,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
589
620
  - !ruby/object:Gem::Version
590
621
  version: '0'
591
622
  requirements: []
592
- rubygems_version: 3.0.3
593
- signing_key:
623
+ rubygems_version: 3.1.4
624
+ signing_key:
594
625
  specification_version: 4
595
626
  summary: Atmos provides a terraform scaffold for creating cloud system architectures
596
627
  test_files: []