wrapbox 0.7.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12b4d996dbd5a6dd05a700e8654c369b670246eaaed96514e9783e94be18a506
4
- data.tar.gz: 06db857821782f32d3cbf3bbcd679182287735224c17256b85f57644b13ea69f
3
+ metadata.gz: 207f24c9cc5e7fc32e69652b7ff9f012b32da2203ac5c044b6adb755fa34ba6c
4
+ data.tar.gz: 6a1dcfd5cd2e09cba65449184f2d736074e5436419abe20b54dcb1117c8af81a
5
5
  SHA512:
6
- metadata.gz: 05f3a8b158b5c54b0f010695fd020a3ea381140a88da9b57488cd469091f66da63215b36b6973e0736b3422200495a1f6dd1d199be07ccc94f32c985f3c17762
7
- data.tar.gz: 10a3aecb71fb45e22b9ffe3123e592142ae4fab5ef5e70bb372c328f8c8b302ffcf8caac592305e9f3466e80570752421ed398936a5f3aabf5764faac40334ce
6
+ metadata.gz: a02a9ab56bf3f0d4bdd2d24fab5acef9d41a128bbdd477f0e4cab16f2a00d90cb20d23c93bc6ec5c7416faa059eacb3dd9a46a050559b994ce772b28f1dabaeb
7
+ data.tar.gz: c0e5595b908a11924854830867af67312f851430d4b5dadf058c273178d111d63d8164a4573efc3a20bbbff8b2f6386dfca8bcb1bd829d1624004b96aa87a853
data/lib/wrapbox.rb CHANGED
@@ -4,6 +4,10 @@ module Wrapbox
4
4
  METHOD_ARGS_ENV = "WRAPBOX_METHOD_ARGS".freeze
5
5
 
6
6
  class << self
7
+ def load_config(filename)
8
+ configs.load_yaml(filename)
9
+ end
10
+
7
11
  def configs
8
12
  @configs ||= ConfigRepository.new
9
13
  end
@@ -12,12 +16,12 @@ module Wrapbox
12
16
  yield configs
13
17
  end
14
18
 
15
- def run(*args, config_name: nil, **options)
19
+ def run(*args, runner: nil, config_name: nil, **options)
16
20
  config = @configs.get(config_name)
17
21
  config.run(*args, **options)
18
22
  end
19
23
 
20
- def run_cmd(*args, config_name: nil, **options)
24
+ def run_cmd(*args, runner: nil, config_name: nil, **options)
21
25
  config = @configs.get(config_name)
22
26
  config.run_cmd(*args, **options)
23
27
  end
data/lib/wrapbox/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "thor/group"
2
+ require "wrapbox"
2
3
  require "wrapbox/runner/docker"
3
4
  require "wrapbox/runner/ecs"
4
5
 
@@ -4,6 +4,7 @@ require "active_support/core_ext/string"
4
4
  module Wrapbox
5
5
  Configuration = Struct.new(
6
6
  :name,
7
+ :task_definition_name,
7
8
  :revision,
8
9
  :runner,
9
10
  :cluster,
@@ -32,6 +33,7 @@ module Wrapbox
32
33
  def self.load_config(config)
33
34
  new(
34
35
  config["name"],
36
+ config["task_definition_name"],
35
37
  config["revision"],
36
38
  config["runner"] ? config["runner"].to_sym : :docker,
37
39
  config["cluster"],
@@ -72,12 +74,12 @@ module Wrapbox
72
74
  Wrapbox::Runner.const_get(r.to_s.camelcase).new(to_h)
73
75
  end
74
76
 
75
- def run(class_name, method_name, args, **options)
76
- build_runner.run(class_name, method_name, args, **options)
77
+ def run(class_name, method_name, args, runner: nil, **options)
78
+ build_runner(runner).run(class_name, method_name, args, **options)
77
79
  end
78
80
 
79
- def run_cmd(*cmd, **options)
80
- build_runner.run_cmd(*cmd, **options)
81
+ def run_cmd(*cmd, runner: nil, **options)
82
+ build_runner(runner).run_cmd(*cmd, **options)
81
83
  end
82
84
  end
83
85
  end
@@ -8,7 +8,15 @@ module Wrapbox
8
8
  def self.new(type, **options)
9
9
  raise "log_fetcher config needs `type`" unless type
10
10
  require "wrapbox/log_fetcher/#{type}"
11
- self.const_get(type.classify).new(**options)
11
+ self.const_get(type.camelize).new(**options)
12
+ end
13
+
14
+ def run(task:)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def stop
19
+ raise NotImplementedError
12
20
  end
13
21
  end
14
22
  end
@@ -0,0 +1,104 @@
1
+ module Wrapbox
2
+ module LogFetcher
3
+ class Awslogs
4
+ STOP_WAIT_TIMELIMIT = 10
5
+
6
+ def initialize(log_group:, log_stream_prefix:, filter_pattern: nil, region: nil, access_key_id: nil, secret_access_key: nil, timestamp_format: "%Y-%m-%d %H:%M:%S.%3N", delay: 2, **options)
7
+ begin
8
+ require 'aws-sdk-cloudwatchlogs'
9
+ rescue LoadError
10
+ $stderr.puts "Require aws-sdk-cloudwatchlogs gem"
11
+ exit 1
12
+ end
13
+
14
+ @log_group = log_group
15
+ @log_stream_prefix = log_stream_prefix
16
+ @filter_pattern = filter_pattern
17
+ @region = region
18
+ @access_key_id = access_key_id
19
+ @secret_access_key = secret_access_key
20
+ @timestamp_format = timestamp_format
21
+ @delay = delay
22
+ @options = options.reject { |_, v| v.nil? }
23
+ @displayed_log_stream_names = {}
24
+ @displayed_log_stream_number = 0
25
+ @displayed_event_ids = {}
26
+ end
27
+
28
+ def run(task:)
29
+ @loop_thread = Thread.start do
30
+ main_loop(task)
31
+ end
32
+ end
33
+
34
+ def stop
35
+ @stop = true
36
+ @loop_thread&.join(STOP_WAIT_TIMELIMIT)
37
+ end
38
+
39
+ def main_loop(task)
40
+ task_id = task.task_arn.split("/").last
41
+ log_stream_names = task.containers.map do |container|
42
+ [@log_stream_prefix, container.name, task_id].join("/")
43
+ end
44
+ filter_log_opts = {
45
+ log_group_name: @log_group,
46
+ log_stream_names: log_stream_names,
47
+ filter_pattern: @filter_pattern,
48
+ interleaved: true,
49
+ }.compact
50
+ @max_timestamp = ((Time.now.to_f - 120) * 1000).round
51
+
52
+ until @stop do
53
+ filter_log_opts[:start_time] = @max_timestamp + 1
54
+ resp = client.filter_log_events(filter_log_opts) rescue nil
55
+ resp&.each do |r|
56
+ r.events.each do |ev|
57
+ next if @displayed_event_ids.member?(ev.event_id)
58
+ display_message(ev)
59
+ @displayed_event_ids[ev.event_id] = ev.timestamp
60
+ @max_timestamp = ev.timestamp if @max_timestamp < ev.timestamp
61
+ end
62
+ end
63
+ Thread.start do
64
+ @displayed_event_ids.each do |event_id, ts|
65
+ if ts < (Time.now.to_f - 600) * 1000
66
+ @displayed_event_ids.delete(event_id)
67
+ end
68
+ end
69
+ end.tap do
70
+ sleep @delay
71
+ end.join
72
+ end
73
+ end
74
+
75
+ COLOR_ESCAPE_SEQUENCES = [33, 31, 32, 34, 35, 36]
76
+ def display_message(ev, output: $stdout)
77
+ num = @displayed_log_stream_names.fetch(ev.log_stream_name) do |key|
78
+ current = @displayed_log_stream_number
79
+ @displayed_log_stream_names[key] = current
80
+ @displayed_log_stream_number += 1
81
+ current
82
+ end
83
+
84
+ sequence_number = COLOR_ESCAPE_SEQUENCES[num % COLOR_ESCAPE_SEQUENCES.length]
85
+
86
+ time = Time.at(ev.timestamp / 1000.0)
87
+ output.puts("\e[#{sequence_number}m#{time.strftime(@timestamp_format)} #{ev.log_stream_name}\e[0m #{ev.message}")
88
+ end
89
+
90
+ private
91
+
92
+ def client
93
+ return @client if @client
94
+
95
+ options = {
96
+ region: @region,
97
+ access_key_id: @access_key_id,
98
+ secret_access_key: @secret_access_key,
99
+ }.compact
100
+ @client = Aws::CloudWatchLogs::Client.new(**options)
101
+ end
102
+ end
103
+ end
104
+ end
@@ -17,7 +17,7 @@ module Wrapbox
17
17
  @options = options.reject { |_, v| v.nil? }
18
18
  end
19
19
 
20
- def run
20
+ def run(task:)
21
21
  @started_at = Time.now
22
22
  @loop_thread = Thread.start(&method(:main_loop))
23
23
  end
@@ -139,10 +139,8 @@ module Wrapbox
139
139
  method_option :environments, aliases: "-e"
140
140
  method_option :ignore_signal, type: :boolean, default: false, desc: "Even if receive a signal (like TERM, INT, QUIT), Docker container continue running"
141
141
  def run_cmd(*args)
142
- repo = Wrapbox::ConfigRepository.new.tap { |r| r.load_yaml(options[:config]) }
143
- config = repo.get(options[:config_name])
144
- config.runner = :docker
145
- runner = config.build_runner
142
+ Wrapbox.load_config(options[:config])
143
+ config = Wrapbox.configs[options[:config_name]]
146
144
  environments = options[:environments].to_s.split(/,\s*/).map { |kv| kv.split("=") }.map do |k, v|
147
145
  {name: k, value: v}
148
146
  end
@@ -151,7 +149,7 @@ module Wrapbox
151
149
  else
152
150
  container_definition_overrides = {}
153
151
  end
154
- unless runner.run_cmd(args, environments: environments, container_definition_overrides: container_definition_overrides, ignore_signal: options[:ignore_signal])
152
+ unless config.run_cmd(args, runner: "docker", environments: environments, container_definition_overrides: container_definition_overrides, ignore_signal: options[:ignore_signal])
155
153
  exit 1
156
154
  end
157
155
  end
@@ -48,6 +48,7 @@ module Wrapbox
48
48
 
49
49
  def initialize(options)
50
50
  @name = options[:name]
51
+ @task_definition_name = options[:task_definition_name]
51
52
  @revision = options[:revision]
52
53
  @cluster = options[:cluster]
53
54
  @region = options[:region]
@@ -75,7 +76,7 @@ module Wrapbox
75
76
  @task_definition_info = options[:task_definition]
76
77
 
77
78
  if !@container_definitions.empty?
78
- @task_definition_name = "wrapbox_#{@name}"
79
+ @task_definition_name ||= "wrapbox_#{@name}"
79
80
  @main_container_name = @container_definitions[0][:name] || @task_definition_name
80
81
  elsif @task_definition_info
81
82
  @task_definition_name = @task_definition_info[:task_definition_name]
@@ -107,6 +108,7 @@ module Wrapbox
107
108
  :execution_role_arn,
108
109
  :cluster,
109
110
  :timeout,
111
+ :launch_type,
110
112
  :launch_timeout,
111
113
  :launch_retry,
112
114
  :retry_interval,
@@ -114,7 +116,7 @@ module Wrapbox
114
116
  :max_retry_interval,
115
117
  :execution_retry
116
118
 
117
- def initialize(environments: [], task_role_arn: nil, cluster: nil, timeout: 3600 * 24, launch_timeout: 60 * 10, launch_retry: 10, retry_interval: 1, retry_interval_multiplier: 2, max_retry_interval: 120, execution_retry: 0)
119
+ def initialize(environments: [], task_role_arn: nil, cluster: nil, timeout: 3600 * 24, launch_type: "EC2", launch_timeout: 60 * 10, launch_retry: 10, retry_interval: 1, retry_interval_multiplier: 2, max_retry_interval: 120, execution_retry: 0)
118
120
  b = binding
119
121
  method(:initialize).parameters.each do |param|
120
122
  instance_variable_set("@#{param[1]}", b.local_variable_get(param[1]))
@@ -186,8 +188,6 @@ module Wrapbox
186
188
  task = create_task(task_definition_arn, class_name, method_name, args, command, parameter)
187
189
  return unless task # only Task creation aborted by SignalException
188
190
 
189
- @log_fetcher.run if @log_fetcher
190
-
191
191
  @logger.debug("Launch Task: #{task.task_arn}")
192
192
 
193
193
  wait_task_stopped(cl, task.task_arn, parameter.timeout)
@@ -237,16 +237,18 @@ module Wrapbox
237
237
 
238
238
  def create_task(task_definition_arn, class_name, method_name, args, command, parameter)
239
239
  cl = parameter.cluster || self.cluster
240
+ launch_type = parameter.launch_type || self.launch_type
240
241
  args = Array(args)
241
242
 
242
243
  launch_try_count = 0
243
244
  current_retry_interval = parameter.retry_interval
244
245
 
245
246
  begin
246
- run_task_options = build_run_task_options(task_definition_arn, class_name, method_name, args, command, cl, parameter.environments, parameter.task_role_arn)
247
+ run_task_options = build_run_task_options(task_definition_arn, class_name, method_name, args, command, cl, launch_type, parameter.environments, parameter.task_role_arn)
247
248
  @logger.debug("Task Options: #{run_task_options}")
248
249
  resp = client.run_task(run_task_options)
249
250
  task = resp.tasks[0]
251
+ @log_fetcher.run(task: task) if @log_fetcher
250
252
 
251
253
  resp.failures.each do |failure|
252
254
  @logger.debug("Failure: Arn=#{failure.arn}, Reason=#{failure.reason}")
@@ -438,7 +440,7 @@ module Wrapbox
438
440
  )
439
441
  end
440
442
 
441
- def build_run_task_options(task_definition_arn, class_name, method_name, args, command, cluster, environments, task_role_arn)
443
+ def build_run_task_options(task_definition_arn, class_name, method_name, args, command, cluster, launch_type, environments, task_role_arn)
442
444
  env = environments
443
445
  env += [
444
446
  {
@@ -502,16 +504,15 @@ module Wrapbox
502
504
  method_option :environments, aliases: "-e"
503
505
  method_option :task_role_arn
504
506
  method_option :timeout, type: :numeric
507
+ method_option :launch_type, type: :string, default: "EC2", enum: ["EC2", "FARGATE"]
505
508
  method_option :launch_timeout, type: :numeric
506
509
  method_option :launch_retry, type: :numeric
507
510
  method_option :execution_retry, type: :numeric
508
511
  method_option :max_retry_interval, type: :numeric
509
512
  method_option :ignore_signal, type: :boolean, default: false, desc: "Even if receive a signal (like TERM, INT, QUIT), ECS Tasks continue running"
510
513
  def run_cmd(*args)
511
- repo = Wrapbox::ConfigRepository.new.tap { |r| r.load_yaml(options[:config]) }
512
- config = repo.get(options[:config_name])
513
- config.runner = :ecs
514
- runner = config.build_runner
514
+ Wrapbox.load_config(options[:config])
515
+ config = Wrapbox.configs[options[:config_name]]
515
516
  environments = options[:environments].to_s.split(/,\s*/).map { |kv| kv.split("=") }.map do |k, v|
516
517
  {name: k, value: v}
517
518
  end
@@ -519,6 +520,7 @@ module Wrapbox
519
520
  cluster: options[:cluster],
520
521
  task_role_arn: options[:task_role_arn],
521
522
  timeout: options[:timeout],
523
+ launch_type: options[:launch_type],
522
524
  launch_timeout: options[:launch_timeout],
523
525
  launch_retry: options[:launch_retry],
524
526
  execution_retry: options[:execution_retry],
@@ -530,7 +532,7 @@ module Wrapbox
530
532
  else
531
533
  container_definition_overrides = {}
532
534
  end
533
- unless runner.run_cmd(args, environments: environments, container_definition_overrides: container_definition_overrides, **run_options)
535
+ unless config.run_cmd(args, runner: "ecs", environments: environments, container_definition_overrides: container_definition_overrides, **run_options)
534
536
  exit 1
535
537
  end
536
538
  end
@@ -1,3 +1,3 @@
1
1
  module Wrapbox
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wrapbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - joker1007
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-10 00:00:00.000000000 Z
11
+ date: 2018-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-ecs
@@ -203,6 +203,7 @@ files:
203
203
  - lib/wrapbox/configuration.rb
204
204
  - lib/wrapbox/job.rb
205
205
  - lib/wrapbox/log_fetcher.rb
206
+ - lib/wrapbox/log_fetcher/awslogs.rb
206
207
  - lib/wrapbox/log_fetcher/papertrail.rb
207
208
  - lib/wrapbox/runner/docker.rb
208
209
  - lib/wrapbox/runner/ecs.rb