simplygenius-atmos 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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