simplygenius-atmos 0.7.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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/LICENSE +13 -0
  4. data/README.md +212 -0
  5. data/exe/atmos +4 -0
  6. data/exe/atmos-docker +12 -0
  7. data/lib/atmos.rb +12 -0
  8. data/lib/atmos/cli.rb +105 -0
  9. data/lib/atmos/commands/account.rb +65 -0
  10. data/lib/atmos/commands/apply.rb +20 -0
  11. data/lib/atmos/commands/auth_exec.rb +29 -0
  12. data/lib/atmos/commands/base_command.rb +12 -0
  13. data/lib/atmos/commands/bootstrap.rb +72 -0
  14. data/lib/atmos/commands/container.rb +58 -0
  15. data/lib/atmos/commands/destroy.rb +18 -0
  16. data/lib/atmos/commands/generate.rb +90 -0
  17. data/lib/atmos/commands/init.rb +18 -0
  18. data/lib/atmos/commands/new.rb +18 -0
  19. data/lib/atmos/commands/otp.rb +54 -0
  20. data/lib/atmos/commands/plan.rb +20 -0
  21. data/lib/atmos/commands/secret.rb +87 -0
  22. data/lib/atmos/commands/terraform.rb +52 -0
  23. data/lib/atmos/commands/user.rb +74 -0
  24. data/lib/atmos/config.rb +208 -0
  25. data/lib/atmos/exceptions.rb +9 -0
  26. data/lib/atmos/generator.rb +199 -0
  27. data/lib/atmos/generator_factory.rb +93 -0
  28. data/lib/atmos/ipc.rb +132 -0
  29. data/lib/atmos/ipc_actions/notify.rb +27 -0
  30. data/lib/atmos/ipc_actions/ping.rb +19 -0
  31. data/lib/atmos/logging.rb +160 -0
  32. data/lib/atmos/otp.rb +61 -0
  33. data/lib/atmos/provider_factory.rb +19 -0
  34. data/lib/atmos/providers/aws/account_manager.rb +82 -0
  35. data/lib/atmos/providers/aws/auth_manager.rb +208 -0
  36. data/lib/atmos/providers/aws/container_manager.rb +116 -0
  37. data/lib/atmos/providers/aws/provider.rb +51 -0
  38. data/lib/atmos/providers/aws/s3_secret_manager.rb +49 -0
  39. data/lib/atmos/providers/aws/user_manager.rb +211 -0
  40. data/lib/atmos/settings_hash.rb +90 -0
  41. data/lib/atmos/terraform_executor.rb +267 -0
  42. data/lib/atmos/ui.rb +159 -0
  43. data/lib/atmos/utils.rb +50 -0
  44. data/lib/atmos/version.rb +3 -0
  45. data/templates/new/config/atmos.yml +50 -0
  46. data/templates/new/config/atmos/runtime.yml +43 -0
  47. data/templates/new/templates.yml +1 -0
  48. metadata +526 -0
@@ -0,0 +1,29 @@
1
+ require_relative 'base_command'
2
+ require 'climate_control'
3
+
4
+ module Atmos::Commands
5
+
6
+ class AuthExec < BaseCommand
7
+
8
+ def self.description
9
+ "Exec subprocess with an authenticated environment"
10
+ end
11
+
12
+ option ["-r", "--role"],
13
+ 'ROLE', "overrides assume role name\n"
14
+
15
+ parameter "COMMAND ...", "command to exec", :attribute_name => :command
16
+
17
+ def execute
18
+ Atmos.config.provider.auth_manager.authenticate(ENV, role: role) do |auth_env|
19
+ result = system(auth_env, *command)
20
+ if ! result
21
+ logger.error("Process failed: #{command}")
22
+ exit(1)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ require_relative '../../atmos'
2
+ require_relative '../../atmos/ui'
3
+ require 'clamp'
4
+
5
+ module Atmos::Commands
6
+
7
+ class BaseCommand < Clamp::Command
8
+ include GemLogger::LoggerSupport
9
+ include Atmos::UI
10
+ end
11
+
12
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'base_command'
2
+
3
+ module Atmos::Commands
4
+
5
+ class Bootstrap < BaseCommand
6
+
7
+ def self.description
8
+ "Sets up the initial aws account for use by atmos"
9
+ end
10
+
11
+ option ["-f", "--force"],
12
+ :flag, "forces bootstrap\n"
13
+
14
+ def execute
15
+
16
+ tf_init_dir = File.join(Atmos.config.tf_working_dir('bootstrap'), '.terraform')
17
+ tf_initialized = File.exist?(tf_init_dir)
18
+ backend_initialized = File.exist?(File.join(tf_init_dir, 'terraform.tfstate'))
19
+
20
+ rebootstrap_msg = <<~EOF
21
+ Bootstrap should only be performed when provisioning an account for the first
22
+ time. Try 'atmos terraform init'
23
+ EOF
24
+
25
+ if !force? && tf_initialized
26
+ signal_usage_error(rebootstrap_msg)
27
+ end
28
+
29
+ Atmos.config.provider.auth_manager.authenticate(ENV, bootstrap: true) do |auth_env|
30
+ begin
31
+ exe = Atmos::TerraformExecutor.new(process_env: auth_env, working_group: 'bootstrap')
32
+
33
+ skip_backend = true
34
+ skip_secrets = true
35
+ if backend_initialized
36
+ skip_backend = false
37
+ skip_secrets = false
38
+ end
39
+
40
+ # Cases
41
+ # 1) bootstrap of new account - success
42
+ # 2) repeating bootstrap of new account due to failure partway - success
43
+ # 3) try to rebootstrap existing account on fresh checkout - should fail trying to create resources of same name, check output for this?
44
+ # 4) bootstrap new account with no-default secrets
45
+
46
+ # Need to init before we can create the resources to store state in bootstrap
47
+ exe.run("init", "-input=false", "-lock=false",
48
+ skip_backend: true, skip_secrets: true)
49
+
50
+ # Bootstrap to create the resources needed to store state
51
+ exe.run("apply", "-input=false",
52
+ skip_backend: true, skip_secrets: true)
53
+
54
+ # Need to init to setup the backend state after we create the resources
55
+ # to store state in bootstrap
56
+ exe.run("init", "-input=false", "-force-copy", skip_secrets: true)
57
+
58
+ # Might as well init the non-bootstrap case as well once the state
59
+ # storage has been setup in bootstrap
60
+ exe = Atmos::TerraformExecutor.new(process_env: auth_env)
61
+ exe.run("init", "-input=false", skip_secrets: true)
62
+
63
+ rescue Atmos::TerraformExecutor::ProcessFailed => e
64
+ logger.error(e.message)
65
+ logger.error(rebootstrap_msg)
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,58 @@
1
+ require_relative 'base_command'
2
+ require_relative '../../atmos/settings_hash'
3
+ require 'climate_control'
4
+
5
+ module Atmos::Commands
6
+
7
+ class Container < BaseCommand
8
+
9
+ def self.description
10
+ "Manages containers in the cloud provider"
11
+ end
12
+
13
+ subcommand "deploy", "Deploy a container" do
14
+
15
+ option ["-c", "--cluster"],
16
+ "CLUSTER", "The cluster name\n",
17
+ required: true
18
+
19
+ option ["-r", "--role"],
20
+ "ROLE", "The role to assume when deploying\n"
21
+
22
+ option ["-i", "--image"],
23
+ "IMAGE", "The local container image to deploy\nDefaults to service/task name"
24
+
25
+ option ["-t", "--task"],
26
+ :flag, "Deploy as a task, not a service\n"
27
+
28
+ option ["-v", "--revision"],
29
+ "REVISION", "Use as the remote image revision\n"
30
+
31
+ parameter "NAME",
32
+ "The name of the service (or task) to deploy"
33
+
34
+ def default_image
35
+ name
36
+ end
37
+
38
+ def execute
39
+ Atmos.config.provider.auth_manager.authenticate(ENV, role: role) do |auth_env|
40
+ ClimateControl.modify(auth_env) do
41
+ mgr = Atmos.config.provider.container_manager
42
+
43
+ result = mgr.push(name, image, revision: revision)
44
+ if task?
45
+ result = result.merge(mgr.deploy_task(name, result[:remote_image]))
46
+ else
47
+ result = result.merge(mgr.deploy_service(cluster, name, result[:remote_image]))
48
+ end
49
+
50
+ logger.info "Container deployed:\n #{display result}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'terraform'
2
+
3
+ module Atmos::Commands
4
+
5
+ class Destroy < Atmos::Commands::Terraform
6
+
7
+ def self.description
8
+ "Runs terraform destroy"
9
+ end
10
+
11
+ def execute
12
+ @terraform_arguments.insert(0, "destroy")
13
+ super
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,90 @@
1
+ require_relative 'base_command'
2
+ require_relative '../../atmos/generator_factory'
3
+ require_relative '../../atmos/utils'
4
+
5
+ module Atmos::Commands
6
+
7
+ # From https://github.com/rubber/rubber/blob/master/lib/rubber/commands/vulcanize.rb
8
+ class Generate < BaseCommand
9
+
10
+ def self.description
11
+ <<~EOF
12
+ Installs configuration templates used by atmos to create infrastructure
13
+ resources e.g.
14
+
15
+ atmos generate aws-vpc
16
+
17
+ use --list to get a list of the template names for a given sourceroot
18
+ EOF
19
+ end
20
+
21
+ option ["-f", "--force"],
22
+ :flag, "Overwrite files that already exist"
23
+ option ["-n", "--dryrun"],
24
+ :flag, "Run but do not make any changes"
25
+ option ["-q", "--quiet"],
26
+ :flag, "Supress status output"
27
+ option ["-s", "--skip"],
28
+ :flag, "Skip files that already exist"
29
+ option ["-d", "--[no-]dependencies"],
30
+ :flag, "Walk dependencies, or not", default: true
31
+ option ["-l", "--list"],
32
+ :flag, "list available templates\n"
33
+ option ["-p", "--sourcepath"],
34
+ "PATH", "find templates at given path or github url\n",
35
+ multivalued: true
36
+
37
+ parameter "TEMPLATE ...", "atmos template(s)", required: false
38
+
39
+ def execute
40
+ signal_usage_error "template name is required" if template_list.blank? && ! list?
41
+
42
+ # don't want to fail for new repo
43
+ if Atmos.config && Atmos.config.is_atmos_repo?
44
+ config_sourcepaths = Atmos.config['template_sources'].try(:collect, &:location) || []
45
+ sourcepath_list.concat(config_sourcepaths)
46
+ end
47
+
48
+ # Always search for templates against the bundled templates directory
49
+ sourcepath_list << File.expand_path('../../../../templates', __FILE__)
50
+
51
+ g = Atmos::GeneratorFactory.create(sourcepath_list,
52
+ force: force?,
53
+ pretend: dryrun?,
54
+ quiet: quiet?,
55
+ skip: skip?,
56
+ dependencies: dependencies?)
57
+ if list?
58
+ logger.info "Valid templates are:"
59
+ list_templates(g, template_list).each {|l| logger.info(l) }
60
+ else
61
+ g.generate(template_list)
62
+ end
63
+
64
+ end
65
+
66
+ def list_templates(generator, name_filters)
67
+ # Format templates into comma-separated paragraph with limt of 70 characters per line
68
+ filtered_names = generator.valid_templates.select do |name|
69
+ name_filters.blank? || name_filters.any? {|f| name =~ /#{f}/ }
70
+ end
71
+
72
+ lines = ['']
73
+ filtered_names.each do |template_name|
74
+ line = lines.last
75
+ if line.size == 0
76
+ line << template_name
77
+ elsif line.size + template_name.size > 68
78
+ line << ','
79
+ lines << template_name # new line
80
+ else
81
+ line << ", " + template_name
82
+ end
83
+ end
84
+
85
+ return lines
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'terraform'
2
+
3
+ module Atmos::Commands
4
+
5
+ class Init < Atmos::Commands::Terraform
6
+
7
+ def self.description
8
+ "Runs terraform init"
9
+ end
10
+
11
+ def execute
12
+ @terraform_arguments.insert(0, "init")
13
+ super
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'generate'
2
+
3
+ module Atmos::Commands
4
+
5
+ class New < Atmos::Commands::Generate
6
+
7
+ def self.description
8
+ "Sets up a new atmos project in the current directory"
9
+ end
10
+
11
+ def execute
12
+ template_list << "new"
13
+ super
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,54 @@
1
+ require_relative 'base_command'
2
+ require_relative '../../atmos/otp'
3
+ require 'clipboard'
4
+
5
+ module Atmos::Commands
6
+
7
+ class Otp < BaseCommand
8
+
9
+ def self.description
10
+ "Generates an otp token for the given user"
11
+ end
12
+
13
+ option ["-s", "--secret"],
14
+ 'SECRET', "The otp secret\nWill save for future use"
15
+
16
+ option ["-c", "--clipboard"],
17
+ :flag,
18
+ <<~EOF
19
+ Automatically copy the token to the system
20
+ clipboard. For dependencies see:
21
+ https://github.com/janlelis/clipboard
22
+ EOF
23
+
24
+ parameter "NAME",
25
+ "The otp name (IAM username)"
26
+
27
+ def execute
28
+ code = nil
29
+ if secret
30
+ Atmos::Otp.instance.add(name, secret)
31
+ code = Atmos::Otp.instance.generate(name)
32
+ Atmos::Otp.instance.save
33
+ else
34
+ code = Atmos::Otp.instance.generate(name)
35
+ end
36
+
37
+ if code.nil?
38
+ signal_usage_error <<~EOF
39
+ No otp secret has been setup for #{name}
40
+ Use the -m flag to 'atmos user create' to create/activate one
41
+ or associate an existing secret with 'atmos otp -s <secret> <name>'
42
+ EOF
43
+ else
44
+ puts code
45
+ end
46
+
47
+ if clipboard?
48
+ Clipboard.copy(code)
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'terraform'
2
+
3
+ module Atmos::Commands
4
+
5
+ class Plan < Atmos::Commands::Terraform
6
+
7
+ def self.description
8
+ "Runs terraform plan"
9
+ end
10
+
11
+ def execute
12
+ args = ["plan"]
13
+ args << "--get-modules" unless Atmos.config["disable_auto_modules"].to_s == "true"
14
+ @terraform_arguments.insert(0, *args)
15
+ super
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'base_command'
2
+ require 'climate_control'
3
+
4
+ module Atmos::Commands
5
+
6
+ class Secret < BaseCommand
7
+
8
+ def self.description
9
+ "Manages application secrets"
10
+ end
11
+
12
+ subcommand "get", "Gets the secret value" do
13
+
14
+ parameter "KEY",
15
+ "The secret key"
16
+
17
+ def execute
18
+
19
+ Atmos.config.provider.auth_manager.authenticate(ENV) do |auth_env|
20
+ ClimateControl.modify(auth_env) do
21
+ value = Atmos.config.provider.secret_manager.get(key)
22
+ logger.info "Secret value for #{key}: #{value}"
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ subcommand "set", "Sets the secret value" do
31
+
32
+ parameter "KEY",
33
+ "The secret key"
34
+
35
+ parameter "VALUE",
36
+ "The secret value"
37
+
38
+ def execute
39
+
40
+ Atmos.config.provider.auth_manager.authenticate(ENV) do |auth_env|
41
+ ClimateControl.modify(auth_env) do
42
+ Atmos.config.provider.secret_manager.set(key, value)
43
+ logger.info "Secret set for #{key}"
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ subcommand "list", "Lists all secret keys" do
52
+
53
+ def execute
54
+
55
+ Atmos.config.provider.auth_manager.authenticate(ENV) do |auth_env|
56
+ ClimateControl.modify(auth_env) do
57
+ logger.info "Secret keys are:"
58
+ Atmos.config.provider.secret_manager.to_h.keys.each {|k| logger.info k}
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ subcommand "delete", "Deletes the secret key/value" do
67
+
68
+ parameter "KEY",
69
+ "The secret key"
70
+
71
+ def execute
72
+
73
+ Atmos.config.provider.auth_manager.authenticate(ENV) do |auth_env|
74
+ ClimateControl.modify(auth_env) do
75
+ value = Atmos.config.provider.secret_manager.get(key)
76
+ Atmos.config.provider.secret_manager.delete(key)
77
+ logger.info "Deleted secret: #{key}=#{value}"
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+
87
+ end