simplygenius-atmos 0.11.3 → 0.11.4

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: 04d15a018baaae4374130fb1128992a038334e7f15669f2444b76b4e235d1175
4
- data.tar.gz: 6ef9631e9148dbee317857276ade4be8ce832dbe4321521a03f048067cd4b8b3
3
+ metadata.gz: 9ca7971744542f8e7e8b9892a57425edbef56c418b22617295f539a4e737a62d
4
+ data.tar.gz: 7a2e7501ec94b617eb0bb3c1d91a37b76884ca57ebc079d4a14e367b41827063
5
5
  SHA512:
6
- metadata.gz: 7c08a8603f993acc6d7eae4e60fdda61620cfe57f8bb1317bb7cb8cf84ac2eefbe0b63b59629f45f168d548b694e9f94e4f034628de8f822ffc824114d6318d0
7
- data.tar.gz: e926bfa00c22e54cafcabd80f0174eb0178bab25a1cbcfb8632aea1d5f607ba0300dbf59d082d462453a7b7923cb066deb9e49760c5ef65e37d68f16c03010d6
6
+ metadata.gz: 4fff280b4474c41e9329bc49ecfdd7ae215fbb2fd33b45161e94056126710bdca0d8461379029ed45ae23672724cce10cd61d7b79413190738d8b8a3121ad42d
7
+ data.tar.gz: 6d9801ed068c5c007d67e7f0c086134db4aa396a7e6b9c0a983cfa2ac3711ac7c4bd8eeb6092b82011362ed25d79a4bb9f7dfce243470ce928232a256f88efee
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ 0.11.4 (11/27/2019)
2
+ -------------------
3
+
4
+ * add the ability to spawn a console for any service container [0d1764e](https://github.com/simplygenius/atmos/commit/0d1764e)
5
+ * allow escaping atmos interpolations [3d8b59e](https://github.com/simplygenius/atmos/commit/3d8b59e)
6
+
7
+
1
8
  0.11.3 (11/12/2019)
2
9
  -------------------
3
10
 
@@ -14,14 +14,14 @@ module SimplyGenius
14
14
  "Manages containers in the cloud provider"
15
15
  end
16
16
 
17
- subcommand "push", "Only push a container image without activating it" do
17
+ option ["-c", "--cluster"],
18
+ "CLUSTER", "The cluster name\n",
19
+ required: true
18
20
 
19
- option ["-c", "--cluster"],
20
- "CLUSTER", "The cluster name\n",
21
- required: true
21
+ option ["-r", "--role"],
22
+ "ROLE", "The role to assume when deploying\n"
22
23
 
23
- option ["-r", "--role"],
24
- "ROLE", "The role to assume when deploying\n"
24
+ subcommand "push", "Only push a container image without activating it" do
25
25
 
26
26
  option ["-i", "--image"],
27
27
  "IMAGE", "The local container image to deploy\nDefaults to service/task name"
@@ -54,15 +54,8 @@ module SimplyGenius
54
54
 
55
55
  subcommand "activate", "Activate a container that has already been pushed" do
56
56
 
57
- option ["-c", "--cluster"],
58
- "CLUSTER", "The cluster name\n",
59
- required: true
60
-
61
- option ["-r", "--role"],
62
- "ROLE", "The role to assume when deploying\n"
63
-
64
57
  option ["-v", "--revision"],
65
- "REVISION", "Use the given revision of the pushed image to activate\n"
58
+ "REVISION", "Use the given revision of the pushed image\nto activate\n"
66
59
 
67
60
  option ["-l", "--list"],
68
61
  :flag, "List the most recent pushed images\n"
@@ -122,13 +115,6 @@ module SimplyGenius
122
115
 
123
116
  subcommand "deploy", "Push and activate a container" do
124
117
 
125
- option ["-c", "--cluster"],
126
- "CLUSTER", "The cluster name\n",
127
- required: true
128
-
129
- option ["-r", "--role"],
130
- "ROLE", "The role to assume when deploying\n"
131
-
132
118
  option ["-i", "--image"],
133
119
  "IMAGE", "The local container image to deploy\nDefaults to service/task name"
134
120
 
@@ -164,6 +150,44 @@ module SimplyGenius
164
150
  end
165
151
  end
166
152
 
153
+ subcommand "console", "Spawn a console and attach to it" do
154
+
155
+ option ["-p", "--persist"],
156
+ :flag, "Leave the task running after disconnect\n"
157
+
158
+ parameter "NAME",
159
+ "The name of the service (or task) to attach\nthe console to"
160
+
161
+ def execute
162
+ Atmos.config.provider.auth_manager.authenticate(ENV, role: role) do |auth_env|
163
+ ClimateControl.modify(auth_env) do
164
+ mgr = Atmos.config.provider.container_manager
165
+ remote_command = Array(Atmos.config['atmos.container.console.remote_command'])
166
+ remote_persist_command = Array(Atmos.config['atmos.container.console.remote_persist_command'])
167
+ log_pattern = Atmos.config['atmos.container.console.remote_log_pattern']
168
+ local_command = Atmos.config['atmos.container.console.local_command']
169
+
170
+ cmd = persist? ? remote_persist_command : remote_command
171
+ logger.debug "Running remote command: #{cmd.join(" ")}"
172
+ result = mgr.run_task(cluster, name, command: cmd, waiter_log_pattern: log_pattern)
173
+ logger.debug "Run task result: #{result}"
174
+ begin
175
+ match = result[:log_match]
176
+ local_command = local_command.collect {|c| match.names.each {|n| c = c.gsub("<#{n}>", match[n]) }; c }
177
+ system(*local_command)
178
+ ensure
179
+ if persist?
180
+ logger.info "Console disconnected, you can reconnect with: #{local_command.join(" ")}"
181
+ else
182
+ logger.info "Console complete, stopping task"
183
+ mgr.stop_task(cluster, result[:task_id])
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+
167
191
  end
168
192
 
169
193
  end
@@ -14,7 +14,7 @@ module SimplyGenius
14
14
  subcommand "create", "Create a new user" do
15
15
 
16
16
  option ["-f", "--force"],
17
- :flag, "forces deletion/updates for pre-existing resources\n",
17
+ :flag, "forces deletion/updates for pre-existing\nresources",
18
18
  default: false
19
19
 
20
20
  option ["-l", "--login"],
@@ -121,8 +121,6 @@ module SimplyGenius
121
121
 
122
122
  private
123
123
 
124
- INTERP_PATTERN = /(\#\{([^\}]+)\})/
125
-
126
124
  def config_merge(lhs, rhs, debug_state=[])
127
125
  result = nil
128
126
 
@@ -1,6 +1,7 @@
1
1
  require_relative '../../../atmos'
2
2
  require 'aws-sdk-ecs'
3
3
  require 'aws-sdk-ecr'
4
+ require 'aws-sdk-cloudwatchlogs'
4
5
  require 'open3'
5
6
 
6
7
  module SimplyGenius
@@ -159,7 +160,87 @@ module SimplyGenius
159
160
  return result
160
161
  end
161
162
 
162
- private
163
+ def run_task(cluster, name, command:, waiter_log_pattern: nil, launch_type: "FARGATE")
164
+ result = {}
165
+
166
+ ecs = ::Aws::ECS::Client.new
167
+ resp = nil
168
+
169
+ task_opts = {
170
+ count: 1,
171
+ cluster: cluster,
172
+ task_definition: name,
173
+ launch_type: launch_type,
174
+ overrides: {container_overrides: [{name: name, command: command}]}
175
+ }
176
+
177
+ defn_arn = nil
178
+ resp = ecs.describe_services(cluster: cluster, services: [name])
179
+ if resp.services.size > 0
180
+ svc = resp.services.first
181
+ task_opts[:launch_type] = svc.launch_type
182
+ task_opts[:network_configuration] = svc.network_configuration.to_h
183
+ defn_arn = svc.task_definition
184
+ logger.info "Running service task as '#{task_opts[:launch_type]}'"
185
+ else
186
+ resp = ecs.list_task_definitions(family_prefix: name, sort: 'DESC')
187
+ defn_arn = resp.task_definition_arns.first
188
+ logger.info "Running task as '#{task_opts[:launch_type]}'"
189
+ end
190
+
191
+ resp = ecs.describe_task_definition(task_definition: defn_arn)
192
+ defn = resp.task_definition
193
+ raise "Invalid Launch type '#{launch_type}'" unless (defn.requires_compatibilities + defn.compatibilities).include?(launch_type)
194
+
195
+ log_config = defn.container_definitions.first.log_configuration
196
+ log_group = nil
197
+ log_stream_prefix = nil
198
+ if log_config && log_config.log_driver == "awslogs"
199
+ log_group = log_config.options["awslogs-group"]
200
+ log_stream_prefix = log_config.options["awslogs-stream-prefix"]
201
+ end
202
+ if waiter_log_pattern && log_group.nil?
203
+ logger.error "Cannot wait on a log unless task definition uses cloudwatch for logging"
204
+ waiter_log_pattern = nil
205
+ end
206
+
207
+ resp = ecs.run_task(**task_opts)
208
+ task_arn = result[:task_arn] = resp.tasks.first.task_arn
209
+ task_id = result[:task_id] = task_arn.split('/').last
210
+
211
+ logger.info "Waiting for task to start"
212
+ ecs.wait_until(:tasks_running, cluster: cluster, tasks: [task_id])
213
+
214
+ if waiter_log_pattern
215
+ cwl = ::Aws::CloudWatchLogs::Client.new
216
+
217
+ waiter_regexp = Regexp.new(waiter_log_pattern)
218
+ log_stream = "#{log_stream_prefix}/#{name}/#{task_id}"
219
+ logger.info "Task started, looking for log pattern in group=#{log_group} stream=#{log_stream}"
220
+ log_token = nil
221
+ 10.times do
222
+ resp = cwl.get_log_events(log_group_name: log_group, log_stream_name: log_stream, start_from_head: true, next_token: log_token)
223
+ resp.events.each do |e|
224
+ logger.debug("Task log #{e.timestamp}: #{e.message}")
225
+ if e.message =~ waiter_regexp
226
+ result[:log_match] = Regexp.last_match
227
+ return result # return, not break due to doubly nested iterator
228
+ end
229
+ end
230
+ log_token = resp.next_forward_token
231
+ sleep 1
232
+ end
233
+ end
234
+
235
+ return result
236
+ end
237
+
238
+ def stop_task(cluster, task)
239
+ ecs = ::Aws::ECS::Client.new
240
+ resp = ecs.stop_task(cluster: cluster, task: task)
241
+ end
242
+
243
+ private
163
244
 
164
245
  def run(*args, **opts)
165
246
  logger.debug("Running: #{args}")
@@ -12,7 +12,7 @@ module SimplyGenius
12
12
  disable_warnings
13
13
 
14
14
  PATH_PATTERN = /[\.\[\]]/
15
- INTERP_PATTERN = /(\#\{([^\}]+)\})/
15
+ INTERP_PATTERN = /(\#?\#\{([^\}]+)\})/
16
16
 
17
17
  attr_accessor :_root_, :error_resolver, :enable_expansion
18
18
 
@@ -84,12 +84,16 @@ module SimplyGenius
84
84
  result.scan(INTERP_PATTERN).each do |substr, statement|
85
85
  # TODO: add an explicit check for cycles instead of relying on Stack error
86
86
  begin
87
- # TODO: be consistent with dot notation between eval and
88
- # notation_get. eval ends up calling Hashie method_missing,
89
- # which returns nil if a key doesn't exist, causing a nil
90
- # exception for next item in chain, while notation_get returns
91
- # nil gracefully for the entire chain (preferred)
92
- val = eval(statement, binding, __FILE__)
87
+ if substr.start_with?('##')
88
+ val = substr[1..-1]
89
+ else
90
+ # TODO: be consistent with dot notation between eval and
91
+ # notation_get. eval ends up calling Hashie method_missing,
92
+ # which returns nil if a key doesn't exist, causing a nil
93
+ # exception for next item in chain, while notation_get returns
94
+ # nil gracefully for the entire chain (preferred)
95
+ val = eval(statement, binding, __FILE__)
96
+ end
93
97
  rescue SystemStackError => e
94
98
  raise ConfigInterpolationError.new(format_error("Cycle in interpolated config", substr))
95
99
  rescue StandardError => e
@@ -1,5 +1,5 @@
1
1
  module SimplyGenius
2
2
  module Atmos
3
- VERSION = "0.11.3"
3
+ VERSION = "0.11.4"
4
4
  end
5
5
  end
@@ -64,6 +64,51 @@ atmos:
64
64
  # a GUI notification
65
65
  force_inline: false
66
66
 
67
+ # Configure the container components accessible with 'atmos container ....'
68
+ container:
69
+ # Configuration for the console subcommand
70
+ # For this to work, your service container needs to have a recent (>= 2.4)
71
+ # tmate installed. For example in a debian container:
72
+ #
73
+ # # Install locales package for tmate
74
+ # RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -q -y locales
75
+ #
76
+ # # To set utf-8 locale for tmate
77
+ # RUN sed -i -e 's/# \(en_US\.UTF-8 .*\)/\1/' /etc/locale.gen && \
78
+ # dpkg-reconfigure --frontend=noninteractive locales && \
79
+ # update-locale LANG=en_US.UTF-8
80
+ # ENV LANG en_US.UTF-8
81
+ #
82
+ # # Install tmate
83
+ # RUN curl -Lo /tmp/tmate.tar.xz https://github.com/tmate-io/tmate/releases/download/2.4.0/tmate-2.4.0-static-linux-amd64.tar.xz && \
84
+ # cd /tmp && \
85
+ # tar xf /tmp/tmate.tar.xz && \
86
+ # mv tmate-2.4.0-static-linux-amd64/tmate /usr/bin/tmate && \
87
+ # rm -rf /tmp/tmate*
88
+ #
89
+ # # tmate needs ssh keys
90
+ # RUN ssh-keygen -f ~/.ssh/id_rsa -N '' -t rsa
91
+ #
92
+ console:
93
+ # The remote command to run when initiating a console (exits on disconnect)
94
+ remote_command:
95
+ - bash
96
+ - -c
97
+ - echo "set tmate-foreground-restart 0" > ~/.tmate.conf && tmate -F
98
+ # The remote command to run when initiating a console (persists on disconnect)
99
+ remote_persist_command:
100
+ - bash
101
+ - -c
102
+ - tmate -F
103
+ # The regexp to match against remote command output to extract any tokens
104
+ # needed for the client to connect. The regexp named groups (?<...>) get
105
+ # substituted within the client command (<...>)
106
+ remote_log_pattern: "^ssh session: ssh (?<token>\\w+)@nyc1.tmate.io$"
107
+ # The local command to run when initiating a console
108
+ local_command:
109
+ - ssh
110
+ - <token>@nyc1.tmate.io
111
+
67
112
  # Configure terraform execution details
68
113
  terraform:
69
114
  # Disable module fetch from convenience plan/apply commands
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.3
4
+ version: 0.11.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Conway
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-12 00:00:00.000000000 Z
11
+ date: 2019-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -416,6 +416,20 @@ dependencies:
416
416
  - - ">="
417
417
  - !ruby/object:Gem::Version
418
418
  version: '0'
419
+ - !ruby/object:Gem::Dependency
420
+ name: aws-sdk-cloudwatchlogs
421
+ requirement: !ruby/object:Gem::Requirement
422
+ requirements:
423
+ - - ">="
424
+ - !ruby/object:Gem::Version
425
+ version: '0'
426
+ type: :runtime
427
+ prerelease: false
428
+ version_requirements: !ruby/object:Gem::Requirement
429
+ requirements:
430
+ - - ">="
431
+ - !ruby/object:Gem::Version
432
+ version: '0'
419
433
  - !ruby/object:Gem::Dependency
420
434
  name: os
421
435
  requirement: !ruby/object:Gem::Requirement