simplygenius-atmos 0.11.7 → 0.12.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: 9d9f77f7e442308682d210706de83bfa685bfb055c5ece17e6f036da45a99583
4
- data.tar.gz: 764c4b6a7f8536badde7259ed19ebac6b8d7597f67d6d67befc7434ce2849bdf
3
+ metadata.gz: a43cd732a3f97e0fc2884f1b284b8f389db9b1845879a1e8cd5b23a657b9dab6
4
+ data.tar.gz: 0b84e5d5026b849bac355875bc4759b8cfc21abc4ad51627d2e35626f928f2e4
5
5
  SHA512:
6
- metadata.gz: 29df33fc0fcf298cae81cb1c80803ff8d6008e94680dad18a08d5b925d8b1b2b45b238a66f9ec7ed2cfb17adb111592c1ec0db3d3b152d6a0f7b81e87ddefe57
7
- data.tar.gz: 20bba372d51af430e0e3f830e189466c68302a5b6f498eaf2915e2487e28554270ea88ff208e910799fd9c2ff723af407c9117ebfde7bc8bb6f7863ff6ffecb6
6
+ metadata.gz: 84bfe472a2bef731c2cd3dc715ca2b0d214a343cef6491f09bc4762445d7faf340b704366a5b0e099c3e9a42cb6f4e23fecf918671cdeb6a51b4a1a7e15a993e
7
+ data.tar.gz: 2a3f7167cd23068e9e8ea810810ed3e896b36d6c803b8f844b38bfa11e8127dd37505fcf91995321d9a53af4d851236eebebfc8d6421a6c03109aa8e90267596
@@ -1,3 +1,49 @@
1
+ 0.12.0 (10/12/2020)
2
+ -------------------
3
+
4
+ #### Notes on major changes
5
+
6
+ * 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
7
+ * 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
8
+
9
+ #### Full changelog
10
+
11
+ * Make overrides work no matter where they appear in the config load chain [47d73ba](https://github.com/simplygenius/atmos/commit/47d73ba)
12
+ * 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)
13
+ * fix docker build [f4b3e2e](https://github.com/simplygenius/atmos/commit/f4b3e2e)
14
+ * fix stdin test [5bd3f95](https://github.com/simplygenius/atmos/commit/5bd3f95)
15
+ * exit with code on terraform error [2d2af2a](https://github.com/simplygenius/atmos/commit/2d2af2a)
16
+ * test against tf 0.1[123] [1bbfb3b](https://github.com/simplygenius/atmos/commit/1bbfb3b)
17
+ * add 'none' provider for tests (or if one wants to only run against terraform without aws) [991cefe](https://github.com/simplygenius/atmos/commit/991cefe)
18
+ * fix activesupport cve [3d8b30c](https://github.com/simplygenius/atmos/commit/3d8b30c)
19
+ * Add the ability to fetch atmos config from remote sources [b4b97fb](https://github.com/simplygenius/atmos/commit/b4b97fb)
20
+ * 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)
21
+ * Add error msg for when task definition does't exist [0f8291e](https://github.com/simplygenius/atmos/commit/0f8291e)
22
+ * Fix `jsonify` to convert the correct value to JSON. (#4) [1bda229](https://github.com/simplygenius/atmos/commit/1bda229)
23
+ * Use ruby 2.4 for legacy testing since 2.3 is eol [78d2862](https://github.com/simplygenius/atmos/commit/78d2862)
24
+
25
+ 0.11.11 (02/20/2020)
26
+ --------------------
27
+
28
+ * 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)
29
+
30
+ 0.11.10 (02/19/2020)
31
+ --------------------
32
+
33
+ * allow overriding atmos config from cli [f83902b](https://github.com/simplygenius/atmos/commit/f83902b)
34
+
35
+ 0.11.9 (02/13/2020)
36
+ -------------------
37
+
38
+ * Automatically run init when needed for current environment [f902232](https://github.com/simplygenius/atmos/commit/f902232)
39
+
40
+
41
+ 0.11.8 (02/04/2020)
42
+ -------------------
43
+
44
+ * newer terraform in docker image [771ab07](https://github.com/simplygenius/atmos/commit/771ab07)
45
+ * fix plan summary to handle newer terraform [f0a5b95](https://github.com/simplygenius/atmos/commit/f0a5b95)
46
+
1
47
  0.11.7 (01/30/2020)
2
48
  -------------------
3
49
 
@@ -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
@@ -14,6 +14,9 @@ module SimplyGenius
14
14
  args = ["apply"]
15
15
  args << "--get-modules" unless Atmos.config["atmos.terraform.disable_auto_modules"].to_s == "true"
16
16
  @terraform_arguments.insert(0, *args)
17
+
18
+ self.auto_init = true
19
+
17
20
  super
18
21
  end
19
22
 
@@ -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
@@ -7,7 +7,6 @@ module SimplyGenius
7
7
  module Commands
8
8
 
9
9
  class Init < Terraform
10
- include FileUtils
11
10
 
12
11
  def self.description
13
12
  "Runs terraform init"
@@ -17,17 +16,7 @@ module SimplyGenius
17
16
  @terraform_arguments.insert(0, "init")
18
17
  super
19
18
 
20
- if ! Atmos.config["atmos.terraform.disable_shared_plugins"]
21
- home_dir = OS.windows? ? File.join("~", "Application Data") : "~"
22
- shared_plugins_dir = File.expand_path(File.join(home_dir,".terraform.d", "plugins"))
23
- logger.debug("Updating shared terraform plugins dir: #{shared_plugins_dir}")
24
- mkdir_p(shared_plugins_dir)
25
- terraform_plugins_dir = File.join(Atmos.config.tf_working_dir,'recipes', '.terraform', 'plugins')
26
- if File.exist?(terraform_plugins_dir)
27
- cp_r("#{terraform_plugins_dir}/.", shared_plugins_dir)
28
- end
29
- end
30
-
19
+ init_shared_plugins
31
20
  end
32
21
 
33
22
  end
@@ -14,6 +14,9 @@ module SimplyGenius
14
14
  args = ["plan"]
15
15
  args << "--get-modules" unless Atmos.config["atmos.terraform.disable_auto_modules"].to_s == "true"
16
16
  @terraform_arguments.insert(0, *args)
17
+
18
+ self.auto_init = true
19
+
17
20
  super
18
21
  end
19
22
 
@@ -6,6 +6,9 @@ module SimplyGenius
6
6
  module Commands
7
7
 
8
8
  class Terraform < BaseCommand
9
+ include FileUtils
10
+
11
+ attr_accessor :auto_init
9
12
 
10
13
  def self.description
11
14
  "Runs terraform"
@@ -17,6 +20,31 @@ module SimplyGenius
17
20
  @terraform_arguments = arguments
18
21
  end
19
22
 
23
+ def init_automatically(auth_env, get_modules)
24
+ tf_init_dir = File.join(Atmos.config.tf_working_dir, '.terraform')
25
+ backend_initialized = File.exist?(File.join(tf_init_dir, 'terraform.tfstate'))
26
+ auto_init_enabled = Atmos.config["atmos.terraform.auto_init"].to_s == "true"
27
+
28
+ if auto_init && auto_init_enabled && ! backend_initialized
29
+ exe = TerraformExecutor.new(process_env: auth_env)
30
+ exe.run("init", get_modules: get_modules.present?)
31
+ init_shared_plugins
32
+ end
33
+ end
34
+
35
+ def init_shared_plugins
36
+ if ! Atmos.config["atmos.terraform.disable_shared_plugins"]
37
+ 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
45
+ end
46
+ end
47
+
20
48
  def execute
21
49
 
22
50
  unless Atmos.config.is_atmos_repo?
@@ -28,11 +56,15 @@ module SimplyGenius
28
56
 
29
57
  Atmos.config.provider.auth_manager.authenticate(ENV) do |auth_env|
30
58
  begin
31
- exe = TerraformExecutor.new(process_env: auth_env)
32
59
  get_modules = @terraform_arguments.delete("--get-modules")
60
+
61
+ init_automatically(auth_env, get_modules)
62
+
63
+ exe = TerraformExecutor.new(process_env: auth_env)
33
64
  exe.run(*@terraform_arguments, get_modules: get_modules.present?)
34
65
  rescue TerraformExecutor::ProcessFailed => e
35
66
  logger.error(e.message)
67
+ exit(1)
36
68
  end
37
69
  end
38
70
  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
@@ -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
 
@@ -113,13 +114,13 @@ module SimplyGenius
113
114
 
114
115
  if merge_to_existing
115
116
  existing = load_file(user_config_file, SettingsHash.new)
116
- data = config_merge(existing, data, ["saving #{user_config_file}"])
117
+ data = config_merge(existing, data, ["saving #{user_config_file}"], finalize: true)
117
118
  end
118
119
  File.write(user_config_file, YAML.dump(data.to_hash))
119
120
  File.chmod(0600, user_config_file)
120
121
  end
121
122
 
122
- def config_merge(lhs, rhs, debug_state=[])
123
+ def config_merge(lhs, rhs, debug_state=[], finalize: true)
123
124
  result = nil
124
125
 
125
126
  return rhs if lhs.nil?
@@ -132,21 +133,35 @@ module SimplyGenius
132
133
  when Hash
133
134
  result = lhs.deep_dup
134
135
 
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)
136
+ rhs.each do |k, v|
137
+ new_state = debug_state + [k]
138
+
139
+ if k =~ /^\^/
140
+ logger.debug { "Override seen at #{new_state.join(" -> ")}" }
141
+ logger.warn { "Multiple overrides on a single key seen at #{new_state.join(" -> ")}" } if result.has_key?(k)
139
142
  end
143
+
144
+ result[k] = config_merge(result[k], v, new_state, finalize: finalize)
140
145
  end
141
146
 
142
- 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])
147
+ # The load sequence could have multiple overrides and its not
148
+ # obvious to the user which are lhs vs rhs, so rather than having
149
+ # one seem to win arbitrarily, we collect them all additively under
150
+ # their key, then at the end of the load process we push the
151
+ # override back as a replacement for its base key.
152
+ # This means that if one has multiple overrides for the same key
153
+ # (not usually what one wants), those overrides get merged together
154
+ # additively before replacing the base. Thus the need for the debug
155
+ # log message above.
156
+ if finalize
157
+ result.each do |k, v|
158
+ if k =~ /^\^(.*)/
159
+ key = k.is_a?(Symbol) ? $1.to_sym : $1
160
+ result[key] = result.delete(k)
161
+ end
148
162
  end
149
163
  end
164
+
150
165
  when Enumerable
151
166
  result = lhs + rhs
152
167
  else
@@ -168,6 +183,16 @@ module SimplyGenius
168
183
 
169
184
  private
170
185
 
186
+ def load_remote_config_sources(config, *remote_sources)
187
+ remote_sources.each do |remote_source|
188
+ logger.debug("Loading remote atmos config file: #{remote_source}")
189
+ contents = URI.open(remote_source).read
190
+ config = load_config(remote_source, contents, config)
191
+ end
192
+
193
+ config
194
+ end
195
+
171
196
  def load_config_sources(relative_root, config, *patterns)
172
197
  patterns.each do |pattern|
173
198
  logger.debug("Loading atmos config files using pattern: #{pattern}")
@@ -197,7 +222,7 @@ module SimplyGenius
197
222
 
198
223
  begin
199
224
  submap = config.deep_fetch(group, name)
200
- config = config_merge(config, submap, ["#{submap_file} submap(#{group}.#{name})"])
225
+ config = config_merge(config, submap, ["#{submap_file} submap(#{group}.#{name})"], finalize: false)
201
226
  rescue
202
227
  logger.debug("No #{group} config found for '#{name}'")
203
228
  end
@@ -217,26 +242,26 @@ module SimplyGenius
217
242
  @full_config = load_file(config_file, SettingsHash.new)
218
243
  end
219
244
 
220
- @full_config = load_config_sources(File.dirname(config_file), @full_config, *Array(@full_config.notation_get("atmos.config_sources")))
245
+ @user_config_file = @full_config.notation_get("atmos.user_config") || @user_config_file
246
+ @user_config_file = File.expand_path(@user_config_file)
247
+ user_config_file_data = load_file(@user_config_file)
248
+ temp_settings = create_settings(config_merge(@full_config, user_config_file_data, [@user_config_file], finalize: false), finalize: false)
249
+
250
+ @full_config = load_config_sources(File.dirname(config_file), @full_config, *Array(temp_settings.notation_get("atmos.config_sources")))
251
+ @full_config = load_remote_config_sources(@full_config, *Array(temp_settings.notation_get("atmos.remote_config_sources")))
221
252
 
222
253
  @full_config['provider'] = provider_name = @full_config['provider'] || 'aws'
223
254
 
224
255
  @full_config = load_submap(File.dirname(config_file), 'providers', provider_name, @full_config)
225
256
  @full_config = load_submap(File.dirname(config_file), 'environments', atmos_env, @full_config)
226
257
 
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)
258
+ @full_config = config_merge(@full_config, user_config_file_data, [@user_config_file], finalize: false)
230
259
 
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"])
260
+ conf = create_settings(@full_config, finalize: true)
261
+
262
+ # hash emptied out to allow GC of all loaded file contents
263
+ @included_configs = {}
237
264
 
238
- conf.error_resolver = ->(statement) { find_config_error(statement) }
239
- conf.enable_expansion = true
240
265
  conf
241
266
 
242
267
  end
@@ -245,15 +270,8 @@ module SimplyGenius
245
270
  def load_file(file, config=SettingsHash.new, &block)
246
271
  if File.exist?(file)
247
272
  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
273
+ contents = File.read(file)
274
+ config = load_config(file, contents, config, &block)
257
275
  else
258
276
  logger.debug "Could not find an atmos config file at: #{file}"
259
277
  end
@@ -261,16 +279,48 @@ module SimplyGenius
261
279
  config
262
280
  end
263
281
 
282
+ def load_config(location, yml_string, config=SettingsHash.new, &block)
283
+ data = YAML.load(yml_string)
284
+
285
+ if ! data.is_a?(Hash)
286
+ logger.debug("Skipping invalid atmos config (not hash-like): #{location}")
287
+ else
288
+ data = SettingsHash.new(data)
289
+ data = block.call(data) if block
290
+ # if lhs has a override ^, then it loses it when rhs gets merged in, which breaks things for subsequent merges
291
+ config = config_merge(config, data, [location], finalize: false)
292
+ @included_configs[location] = yml_string
293
+ end
294
+
295
+ config
296
+ end
297
+
298
+ def create_settings(config, finalize: true)
299
+ builtins = {
300
+ atmos_env: atmos_env,
301
+ atmos_working_group: working_group,
302
+ atmos_version: VERSION
303
+ }
304
+
305
+ global = SettingsHash.new(config.reject {|k, v| ['providers', 'environments'].include?(k) })
306
+ conf = config_merge(global, builtins, ["builtins"], finalize: finalize)
307
+
308
+ conf.error_resolver = ->(statement) { find_config_error(statement) }
309
+ conf.enable_expansion = true
310
+
311
+ conf
312
+ end
313
+
264
314
  def find_config_error(statement)
265
315
  filename = nil
266
316
  line = 0
267
317
 
268
- @included_configs.each do |c|
318
+ @included_configs.each do |location, contents|
269
319
  current_line = 0
270
- File.foreach(c) do |f|
320
+ contents.each_line do |line|
271
321
  current_line += 1
272
- if f.include?(statement)
273
- filename = c
322
+ if line.include?(statement)
323
+ filename = location
274
324
  line = current_line
275
325
  break
276
326
  end
@@ -34,7 +34,10 @@ module SimplyGenius
34
34
 
35
35
  def summarize(data)
36
36
  # Looking for +/-/~ at start within 2 spaces, could also look for lines that end with {
37
- lines = data.lines.select {|l| l =~ /^[\e\[\dm]*\s{0,2}[~+\-<]/ }.collect(&:chomp)
37
+ lines = data.lines.select { |l|
38
+ l = l.gsub(/\e\[\d+m/, '')
39
+ l =~ /^\s{0,2}[~+\-<]/
40
+ }.collect(&:chomp)
38
41
  lines = lines.reject {|l| l =~ /-----/ }
39
42
  "Plan Summary:\n#{lines.join("\n")}"
40
43
  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
@@ -198,6 +198,8 @@ module SimplyGenius
198
198
  logger.info "Running task as '#{task_opts[:launch_type]}'"
199
199
  end
200
200
 
201
+ raise "Could not find a task definition in AWS for '#{name}'" if defn_arn.blank?
202
+
201
203
  resp = ecs.describe_task_definition(task_definition: defn_arn)
202
204
  defn = resp.task_definition
203
205
  raise "Invalid Launch type '#{launch_type}'" unless (defn.requires_compatibilities + defn.compatibilities).include?(launch_type)
@@ -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)
@@ -1,5 +1,5 @@
1
1
  module SimplyGenius
2
2
  module Atmos
3
- VERSION = "0.11.7"
3
+ VERSION = "0.12.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
@@ -125,3 +131,5 @@ atmos:
125
131
  working_dir_links: ['modules', 'templates', 'bin', '.terraform-version']
126
132
  # Set true if running terraform version < 0.11.x
127
133
  compat11: false
134
+ # Automatically run "init" if needed when using "atmos plan|apply"
135
+ auto_init: true
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.7
4
+ version: 0.12.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-01-30 00:00:00.000000000 Z
11
+ date: 2020-10-13 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: []