simplygenius-atmos 0.11.3 → 0.11.4

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.
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