simplygenius-atmos 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/exe/atmos +2 -2
  4. data/lib/{atmos.rb → simplygenius/atmos.rb} +9 -7
  5. data/lib/simplygenius/atmos/cli.rb +116 -0
  6. data/lib/simplygenius/atmos/commands/account.rb +69 -0
  7. data/lib/simplygenius/atmos/commands/apply.rb +24 -0
  8. data/lib/simplygenius/atmos/commands/auth_exec.rb +34 -0
  9. data/lib/simplygenius/atmos/commands/base_command.rb +16 -0
  10. data/lib/simplygenius/atmos/commands/bootstrap.rb +76 -0
  11. data/lib/simplygenius/atmos/commands/container.rb +62 -0
  12. data/lib/simplygenius/atmos/commands/destroy.rb +22 -0
  13. data/lib/simplygenius/atmos/commands/generate.rb +187 -0
  14. data/lib/simplygenius/atmos/commands/init.rb +22 -0
  15. data/lib/simplygenius/atmos/commands/new.rb +22 -0
  16. data/lib/simplygenius/atmos/commands/otp.rb +58 -0
  17. data/lib/simplygenius/atmos/commands/plan.rb +24 -0
  18. data/lib/simplygenius/atmos/commands/secret.rb +91 -0
  19. data/lib/simplygenius/atmos/commands/terraform.rb +56 -0
  20. data/lib/simplygenius/atmos/commands/user.rb +78 -0
  21. data/lib/simplygenius/atmos/config.rb +279 -0
  22. data/lib/simplygenius/atmos/exceptions.rb +13 -0
  23. data/lib/simplygenius/atmos/generator.rb +232 -0
  24. data/lib/simplygenius/atmos/ipc.rb +136 -0
  25. data/lib/simplygenius/atmos/ipc_actions/notify.rb +31 -0
  26. data/lib/simplygenius/atmos/ipc_actions/ping.rb +23 -0
  27. data/lib/simplygenius/atmos/logging.rb +164 -0
  28. data/lib/simplygenius/atmos/otp.rb +62 -0
  29. data/lib/simplygenius/atmos/plugin.rb +27 -0
  30. data/lib/simplygenius/atmos/plugin_manager.rb +120 -0
  31. data/lib/simplygenius/atmos/plugins/output_filter.rb +29 -0
  32. data/lib/simplygenius/atmos/plugins/prompt_notify.rb +21 -0
  33. data/lib/simplygenius/atmos/provider_factory.rb +23 -0
  34. data/lib/simplygenius/atmos/providers/aws/account_manager.rb +83 -0
  35. data/lib/simplygenius/atmos/providers/aws/auth_manager.rb +220 -0
  36. data/lib/simplygenius/atmos/providers/aws/container_manager.rb +118 -0
  37. data/lib/simplygenius/atmos/providers/aws/provider.rb +53 -0
  38. data/lib/simplygenius/atmos/providers/aws/s3_secret_manager.rb +51 -0
  39. data/lib/simplygenius/atmos/providers/aws/user_manager.rb +213 -0
  40. data/lib/simplygenius/atmos/settings_hash.rb +93 -0
  41. data/lib/simplygenius/atmos/source_path.rb +186 -0
  42. data/lib/simplygenius/atmos/template.rb +117 -0
  43. data/lib/simplygenius/atmos/terraform_executor.rb +297 -0
  44. data/lib/simplygenius/atmos/ui.rb +173 -0
  45. data/lib/simplygenius/atmos/utils.rb +54 -0
  46. data/lib/simplygenius/atmos/version.rb +5 -0
  47. data/templates/new/config/atmos.yml +21 -13
  48. data/templates/new/config/atmos/recipes.yml +16 -0
  49. data/templates/new/config/atmos/runtime.yml +9 -0
  50. metadata +46 -40
  51. data/lib/atmos/cli.rb +0 -105
  52. data/lib/atmos/commands/account.rb +0 -65
  53. data/lib/atmos/commands/apply.rb +0 -20
  54. data/lib/atmos/commands/auth_exec.rb +0 -29
  55. data/lib/atmos/commands/base_command.rb +0 -12
  56. data/lib/atmos/commands/bootstrap.rb +0 -72
  57. data/lib/atmos/commands/container.rb +0 -58
  58. data/lib/atmos/commands/destroy.rb +0 -18
  59. data/lib/atmos/commands/generate.rb +0 -90
  60. data/lib/atmos/commands/init.rb +0 -18
  61. data/lib/atmos/commands/new.rb +0 -18
  62. data/lib/atmos/commands/otp.rb +0 -54
  63. data/lib/atmos/commands/plan.rb +0 -20
  64. data/lib/atmos/commands/secret.rb +0 -87
  65. data/lib/atmos/commands/terraform.rb +0 -52
  66. data/lib/atmos/commands/user.rb +0 -74
  67. data/lib/atmos/config.rb +0 -208
  68. data/lib/atmos/exceptions.rb +0 -9
  69. data/lib/atmos/generator.rb +0 -199
  70. data/lib/atmos/generator_factory.rb +0 -93
  71. data/lib/atmos/ipc.rb +0 -132
  72. data/lib/atmos/ipc_actions/notify.rb +0 -27
  73. data/lib/atmos/ipc_actions/ping.rb +0 -19
  74. data/lib/atmos/logging.rb +0 -160
  75. data/lib/atmos/otp.rb +0 -61
  76. data/lib/atmos/provider_factory.rb +0 -19
  77. data/lib/atmos/providers/aws/account_manager.rb +0 -82
  78. data/lib/atmos/providers/aws/auth_manager.rb +0 -208
  79. data/lib/atmos/providers/aws/container_manager.rb +0 -116
  80. data/lib/atmos/providers/aws/provider.rb +0 -51
  81. data/lib/atmos/providers/aws/s3_secret_manager.rb +0 -49
  82. data/lib/atmos/providers/aws/user_manager.rb +0 -211
  83. data/lib/atmos/settings_hash.rb +0 -90
  84. data/lib/atmos/terraform_executor.rb +0 -267
  85. data/lib/atmos/ui.rb +0 -159
  86. data/lib/atmos/utils.rb +0 -50
  87. data/lib/atmos/version.rb +0 -3
@@ -0,0 +1,136 @@
1
+ require_relative '../atmos'
2
+ require 'fileutils'
3
+ require 'hashie'
4
+
5
+ module SimplyGenius
6
+ module Atmos
7
+
8
+ class Ipc
9
+ include GemLogger::LoggerSupport
10
+
11
+ def initialize(sock_dir=Dir.tmpdir)
12
+ @sock_dir = sock_dir
13
+ end
14
+
15
+ def listen(&block)
16
+ raise "Already listening" if @server
17
+
18
+ begin
19
+ @socket_path = File.join(@sock_dir, 'atmos-ipc')
20
+ FileUtils.rm_f(@socket_path)
21
+ @server = UNIXServer.open(@socket_path)
22
+ rescue ArgumentError => e
23
+ if e.message =~ /too long unix socket path/ && @sock_dir != Dir.tmpdir
24
+ logger.warn("Using tmp for ipc socket as path too long: #{@socket_path}")
25
+ @sock_dir = Dir.tmpdir
26
+ retry
27
+ end
28
+ end
29
+
30
+ begin
31
+ thread = Thread.new { run }
32
+ block.call(@socket_path)
33
+ ensure
34
+ @server.close
35
+ FileUtils.rm_f(@socket_path)
36
+ @server = nil
37
+ end
38
+ end
39
+
40
+ def generate_client_script
41
+ script_file = File.join(@sock_dir, 'atmos_ipc.rb')
42
+ File.write(script_file, <<~EOF
43
+ #!/usr/bin/env ruby
44
+ require 'socket'
45
+ UNIXSocket.open('#{@socket_path}') {|c| c.puts(ARGV[0] || $stdin.read); puts c.gets }
46
+ EOF
47
+ )
48
+ FileUtils.chmod('+x', script_file)
49
+ return script_file
50
+ end
51
+
52
+ private
53
+
54
+ def run
55
+ logger.debug("Starting ipc thread")
56
+ begin
57
+ while @server && sock = @server.accept
58
+ logger.debug("An ipc client connected")
59
+ line = sock.gets
60
+ logger.debug("Got ipc message: #{line.inspect}")
61
+ response = {}
62
+
63
+ begin
64
+ msg = JSON.parse(line)
65
+ msg = Hashie.symbolize_keys(msg)
66
+
67
+ # enabled by default if enabled is not set (e.g. from provisioner local-exec)
68
+ enabled = msg[:enabled].nil? ? true : ["true", "1"].include?(msg[:enabled].to_s)
69
+
70
+ if enabled
71
+ logger.debug("Dispatching IPC action")
72
+ response = dispatch(msg)
73
+ else
74
+ response[:message] = "IPC action is not enabled"
75
+ logger.debug(response[:error])
76
+ end
77
+ rescue => e
78
+ logger.log_exception(e, "Failed to parse ipc message")
79
+ response[:error] = "Failed to parse ipc message #{e.message}"
80
+ end
81
+
82
+ respond(sock, response)
83
+ sock.close
84
+ end
85
+ rescue IOError, EOFError, Errno::EBADF
86
+ nil
87
+ rescue Exception => e
88
+ logger.log_exception(e, "Ipc failure")
89
+ end
90
+ end
91
+
92
+ def close
93
+ @server.close if @server rescue nil
94
+ end
95
+
96
+ def load_action(name)
97
+ action = nil
98
+ logger.debug("Loading ipc action: #{name}")
99
+ begin
100
+ require "simplygenius/atmos/ipc_actions/#{name}"
101
+ action = "SimplyGenius::Atmos::IpcActions::#{name.camelize}".constantize
102
+ logger.debug("Loaded ipc action #{name}")
103
+ rescue LoadError, NameError => e
104
+ logger.log_exception(e, "Failed to load ipc action")
105
+ end
106
+ return action
107
+ end
108
+
109
+ def dispatch(msg)
110
+ response = {}
111
+ action = load_action(msg[:action])
112
+ if action.nil?
113
+ response[:error] = "Unsupported ipc action: #{msg.to_hash.inspect}"
114
+ logger.warn(response[:error])
115
+ else
116
+ begin
117
+ response = action.new().execute(**msg)
118
+ rescue => e
119
+ response[:error] = "Failure while executing ipc action: #{e.message}"
120
+ logger.log_exception(e, "Failure while executing ipc action")
121
+ end
122
+ end
123
+ return response
124
+ end
125
+
126
+ def respond(sock, response)
127
+ msg = JSON.generate(response)
128
+ logger.debug("Sending ipc response: #{msg.inspect}")
129
+ sock.puts(msg)
130
+ sock.flush
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,31 @@
1
+ require_relative '../../atmos'
2
+ require_relative '../../atmos/ui'
3
+
4
+ module SimplyGenius
5
+ module Atmos
6
+ module IpcActions
7
+
8
+ class Notify
9
+ include GemLogger::LoggerSupport
10
+ include UI
11
+
12
+ def initialize()
13
+ end
14
+
15
+ def execute(**opts)
16
+
17
+ result = {
18
+ 'stdout' => '',
19
+ 'success' => ''
20
+ }
21
+
22
+ return result if Atmos.config["ipc.notify.disable"].to_s == "true"
23
+ return notify(**opts)
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../../atmos'
2
+ require 'open3'
3
+ require 'os'
4
+
5
+ module SimplyGenius
6
+ module Atmos
7
+ module IpcActions
8
+
9
+ class Ping
10
+ include GemLogger::LoggerSupport
11
+
12
+ def initialize()
13
+ end
14
+
15
+ def execute(**opts)
16
+ return opts.merge(action: 'pong')
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,164 @@
1
+ require 'logging'
2
+ require 'gem_logger'
3
+ require 'rainbow'
4
+ require 'delegate'
5
+
6
+ module SimplyGenius
7
+ module Atmos
8
+
9
+ module Logging
10
+
11
+ module GemLoggerConcern
12
+ extend ActiveSupport::Concern
13
+
14
+ def logger
15
+ ::Logging.logger[self.class]
16
+ end
17
+
18
+ module ClassMethods
19
+ def logger
20
+ ::Logging.logger[self]
21
+ end
22
+ end
23
+ end
24
+
25
+ class CaptureStream < SimpleDelegator
26
+
27
+ def initialize(logger_name, appender, stream, color=nil)
28
+ super(stream)
29
+ @color = stream.tty? && color ? color : nil
30
+ @logger = ::Logging.logger[logger_name]
31
+ @logger.appenders = [appender]
32
+ @logger.additive = false
33
+ end
34
+
35
+ def strip_color(str)
36
+ str.gsub(/\e\[\d+m/, '')
37
+ end
38
+
39
+ def write(data)
40
+ @logger.info(strip_color(data))
41
+ if @color
42
+ count = 0
43
+ d = data.lines.each do |l|
44
+ cl = Kernel.send(:Rainbow, l).send(@color)
45
+ count += super(cl)
46
+ end
47
+ return count
48
+ else
49
+ return super(data)
50
+ end
51
+ end
52
+ end
53
+
54
+ def self.init_logger
55
+ return if @initialized
56
+ @initialized = true
57
+
58
+ ::Logging.format_as :inspect
59
+ ::Logging.backtrace true
60
+
61
+ ::Logging.color_scheme(
62
+ 'bright',
63
+ lines: {
64
+ debug: :green,
65
+ info: :default,
66
+ warn: :yellow,
67
+ error: :red,
68
+ fatal: [:white, :on_red]
69
+ },
70
+ date: :blue,
71
+ logger: :cyan,
72
+ message: :magenta
73
+ )
74
+
75
+ ::Logging.logger.root.level = :info
76
+ GemLogger.configure do |config|
77
+ config.default_logger = ::Logging.logger.root
78
+ config.logger_concern = Logging::GemLoggerConcern
79
+ end
80
+ end
81
+
82
+
83
+ def self.testing
84
+ @t
85
+ end
86
+
87
+ def self.testing=(t)
88
+ @t = t
89
+ end
90
+
91
+ def self.sio
92
+ ::Logging.logger.root.appenders.find {|a| a.name == 'sio' }
93
+ end
94
+
95
+ def self.contents
96
+ sio.try(:sio).try(:to_s)
97
+ end
98
+
99
+ def self.clear
100
+ sio.try(:clear)
101
+ end
102
+
103
+ def self.setup_logging(debug, color, logfile)
104
+ init_logger
105
+
106
+ ::Logging.logger.root.level = debug ? :debug : :info
107
+ appenders = []
108
+ detail_pattern = '[%d] %-5l %c{2} %m\n'
109
+ plain_pattern = '%m\n'
110
+
111
+ pattern_options = {
112
+ pattern: plain_pattern
113
+ }
114
+ if color
115
+ pattern_options[:color_scheme] = 'bright'
116
+ end
117
+
118
+ if self.testing
119
+
120
+ appender = ::Logging.appenders.string_io(
121
+ 'sio',
122
+ layout: ::Logging.layouts.pattern(pattern_options)
123
+ )
124
+ appenders << appender
125
+
126
+ else
127
+
128
+ appender = ::Logging.appenders.stdout(
129
+ 'stdout',
130
+ layout: ::Logging.layouts.pattern(pattern_options)
131
+ )
132
+ appenders << appender
133
+
134
+ end
135
+
136
+ # Do this after setting up stdout appender so we don't duplicate output
137
+ # to stdout with our capture
138
+ if logfile.present?
139
+
140
+ appender = ::Logging.appenders.file(
141
+ logfile,
142
+ truncate: true,
143
+ layout: ::Logging.layouts.pattern(pattern: detail_pattern)
144
+ )
145
+ appenders << appender
146
+
147
+ if ! $stdout.is_a? CaptureStream
148
+ $stdout = CaptureStream.new("stdout", appender, $stdout)
149
+ $stderr = CaptureStream.new("stderr", appender, $stderr, :red)
150
+ silence_warnings {
151
+ Object.const_set(:STDOUT, $stdout)
152
+ Object.const_set(:STDERR, $stderr)
153
+ }
154
+ end
155
+
156
+ end
157
+
158
+ ::Logging.logger.root.appenders = appenders
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+ end
@@ -0,0 +1,62 @@
1
+ require_relative '../atmos'
2
+ require 'singleton'
3
+ require 'rotp'
4
+
5
+ module SimplyGenius
6
+ module Atmos
7
+
8
+ class Otp
9
+ include Singleton
10
+ include GemLogger::LoggerSupport
11
+
12
+ def initialize
13
+ @secret_file = Atmos.config["otp.secret_file"] || "~/.atmos.yml"
14
+ @secret_file = File.expand_path(@secret_file)
15
+ yml_hash = YAML.load_file(@secret_file) rescue Hash.new
16
+ @secret_store = SettingsHash.new(yml_hash)
17
+ @secret_store[Atmos.config[:org]] ||= {}
18
+ @secret_store[Atmos.config[:org]][:otp] ||= {}
19
+ @scoped_secret_store = @secret_store[Atmos.config[:org]][:otp]
20
+ end
21
+
22
+ def add(name, secret)
23
+ old = @scoped_secret_store[name]
24
+ logger.info "Replacing OTP secret #{name}=#{old}" if old
25
+ @scoped_secret_store[name] = secret
26
+ end
27
+
28
+ def remove(name)
29
+ old = @scoped_secret_store.delete(name)
30
+ @otp.try(:delete, name)
31
+ logger.info "Removed OTP secret #{name}=#{old}" if old
32
+ end
33
+
34
+ def save
35
+ File.write(@secret_file, YAML.dump(@secret_store.to_hash))
36
+ File.chmod(0600, @secret_file)
37
+ end
38
+
39
+ def generate(name)
40
+ otp(name).try(:now)
41
+ end
42
+
43
+ private
44
+
45
+ def otp(name)
46
+ @otp ||= {}
47
+ @otp[name] ||= begin
48
+ secret = @scoped_secret_store[name]
49
+ totp = nil
50
+ if secret
51
+ totp = ROTP::TOTP.new(secret)
52
+ else
53
+ logger.debug "OTP secret does not exist for '#{name}'"
54
+ end
55
+ totp
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../atmos'
2
+ require 'active_support/core_ext/class'
3
+
4
+ Dir.glob(File.join(File.join(__dir__, 'plugins'), '*.rb')) do |f|
5
+ require_relative "plugins/#{File.basename(f).sub(/\.rb$/, "")}"
6
+ end
7
+
8
+ module SimplyGenius
9
+ module Atmos
10
+
11
+ class Plugin
12
+ include GemLogger::LoggerSupport
13
+
14
+ attr_reader :config
15
+
16
+ def initialize(config)
17
+ @config = config
18
+ end
19
+
20
+ def register_output_filter(type, clazz)
21
+ Atmos.config.plugin_manager.register_output_filter(type, clazz)
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end