putpaws 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2ac7d1e3c9a05d7cabf6ed3ddb76b68eebf5e238ee14cd2d220bfeaf88226570
4
+ data.tar.gz: cbcff987bb94042328f0b65d5f693d8aabd75231db1766456d4fe4ed803433e8
5
+ SHA512:
6
+ metadata.gz: 76acfa6f6d0b7a514c31a0f63819cc6e1ea587a48a07a465a321afc6743bdc2b645a1d1f22aa2eb3c7c460e44cdf11d51abbc6bc11f5efbc222950a4231ae56e
7
+ data.tar.gz: daeea4af00b61ebd35b17daf562343b8839791d94dbb5ae356635d3f39d8d09d754ef971b325e979a24c846c1ea0efe878b0c910c54459a507306759a4448d47
data/bin/putpaws ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require "putpaws/all"
3
+ Putpaws::Application.new.run
data/lib/Putpawsfile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env putpaws
2
+ require "putpaws/setup"
3
+
4
+ require "putpaws/hello"
5
+ require "putpaws/ecs/ecs_task"
6
+ require "putpaws/cloud_watch/log_task"
@@ -0,0 +1,10 @@
1
+ require "rake"
2
+ # require "io/console"
3
+ # Rake.application.options.trace = true
4
+ require "putpaws/version"
5
+ require "putpaws/application"
6
+
7
+ require "putpaws/application_config"
8
+
9
+ module Putpaws
10
+ end
@@ -0,0 +1,26 @@
1
+ module Putpaws
2
+ class Application < Rake::Application
3
+ def initialize
4
+ super
5
+ @rakefiles = %w{putpawsfile Putpawsfile putpawsfile.rb Putpawsfile.rb}
6
+ end
7
+
8
+ def name
9
+ "putpaws"
10
+ end
11
+
12
+ def run(argv = ARGV)
13
+ Rake.application = self
14
+ super
15
+ end
16
+
17
+ def find_rakefile_location
18
+ putpawsfile = File.expand_path(File.join(File.dirname(__FILE__), "..", "Putpawsfile"))
19
+ if (location = super).nil?
20
+ [putpawsfile, Dir.pwd]
21
+ else
22
+ location
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ class Putpaws::ApplicationConfig < Struct.new(
5
+ :name, :region,
6
+ :cluster, :service, :task_name_prefix, :ecs_region,
7
+ :log_group_prefix, :log_region,
8
+ keyword_init: true)
9
+ def self.load(path_prefix: '.putpaws')
10
+ @application_data ||= begin
11
+ path = Pathname.new(path_prefix).join("application.json").to_s
12
+ data = File.exists?(path) ?
13
+ JSON.parse(File.read(path), symbolize_names: true).to_h :
14
+ {}
15
+ end
16
+ end
17
+
18
+ def self.all
19
+ load.map{|k,v|
20
+ data = v.slice(*self.members)
21
+ new(data.merge(name: k.to_s))
22
+ }
23
+ end
24
+
25
+ def self.find(name)
26
+ application_data = load
27
+ data = application_data[name.to_sym]
28
+ return nil unless data
29
+ data = data.slice(*self.members)
30
+ new(data.merge({name: name.to_s}))
31
+ end
32
+
33
+ def ecs_command_params
34
+ {
35
+ region: ecs_region || region,
36
+ cluster: cluster,
37
+ # service: service,
38
+ task_name_prefix: task_name_prefix,
39
+ }
40
+ end
41
+
42
+ def log_command_params
43
+ {
44
+ region: log_region || region,
45
+ log_group_prefix: log_group_prefix,
46
+ }
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ class Putpaws::CloudWatch::DefaultLogFormatter
2
+ attr_accessor :datetime_format
3
+ def initialize(datetime_format: nil)
4
+ @datetime_format = datetime_format || "%FT%T%:z"
5
+ end
6
+
7
+ def call(event)
8
+ time = Time.at(0, event.timestamp, :millisecond)
9
+ "%s %s\n" % [time.strftime(datetime_format), event.message]
10
+ end
11
+ end
@@ -0,0 +1,98 @@
1
+ require 'aws-sdk-cloudwatchlogs'
2
+
3
+ module Putpaws::CloudWatch
4
+ class LogCommand
5
+ SECONDS = {
6
+ 's' => 1,
7
+ 'm' => 60,
8
+ 'h' => 60 * 60,
9
+ 'd' => 60 * 60 * 24,
10
+ 'w' => 60 * 60 * 24 * 7,
11
+ }
12
+
13
+ def self.config(config)
14
+ new(config.log_command_params)
15
+ end
16
+
17
+ def self.parse_unit_time(ut)
18
+ return nil unless ut
19
+ matched, number, unit = ut.match(/\A(\d+)([smhdw])\z/).to_a
20
+ return nil unless matched
21
+ number.to_i * SECONDS[unit]
22
+ end
23
+
24
+ def self.filter_args(since: nil, since_for: nil)
25
+ since_sec = parse_unit_time(since)
26
+ since_for_sec = parse_unit_time(since_for)
27
+ start_time = Time.now - (since_sec || (60*1))
28
+ end_time = if since_sec
29
+ since_for_sec && (start_time + since_for_sec)
30
+ else
31
+ nil
32
+ end
33
+ args = {
34
+ start_time: start_time.to_f * 1000,
35
+ end_time: end_time && (end_time.to_f * 1000),
36
+ }
37
+ args.select{|k,v| v}
38
+ end
39
+
40
+ attr_reader :client
41
+ attr_reader :region, :log_group_prefix
42
+ attr_accessor :log_group
43
+ def initialize(region:, log_group_prefix: nil)
44
+ @client = Aws::CloudWatchLogs::Client.new({region: region})
45
+ @log_group_prefix = log_group_prefix
46
+ @log_group = nil
47
+ end
48
+
49
+ def list_log_groups
50
+ res = client.describe_log_groups(log_group_name_prefix: log_group_prefix)
51
+ res.log_groups
52
+ end
53
+
54
+ def log_events(**args)
55
+ raise "Log group Not Set" unless log_group
56
+ res = client.filter_log_events({
57
+ log_group_name: log_group,
58
+ **args
59
+ })
60
+ end
61
+
62
+ def tail_log_events(**args)
63
+ res = log_events(**args)
64
+ nt = newest_timestamp([args[:start_time], *res.events.map(&:timestamp)].compact.max)
65
+ events = filter_same_moment_events(res.events, args[:start_time])
66
+ next_args = if res.next_token
67
+ args.merge(next_token: res.next_token)
68
+ else
69
+ args.merge(
70
+ next_token: nil,
71
+ start_time: nt,
72
+ )
73
+ end
74
+ [events, next_args]
75
+ end
76
+
77
+ def newest_timestamp(newest)
78
+ newest = newest.to_i
79
+ if @newest_timestamp
80
+ @newest_timestamp = [@newest_timestamp, newest].max
81
+ else
82
+ @newest_timestamp = newest
83
+ end
84
+ @newest_timestamp
85
+ end
86
+
87
+ def filter_same_moment_events(events, timestamp)
88
+ @event_ids_already_shown ||= []
89
+ filtered_events = events.reject{|e| @event_ids_already_shown.include?(e.event_id)}
90
+ event_ids = events.map(&:event_id).uniq
91
+ unless event_ids.empty?
92
+ @event_ids_already_shown = event_ids
93
+ end
94
+ # pp @event_ids_already_shown
95
+ filtered_events
96
+ end
97
+ end
98
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path("../tasks/log_task.rake", __FILE__)
@@ -0,0 +1,52 @@
1
+ require "tty-prompt"
2
+ require "putpaws/cloud_watch/log_command"
3
+ require "putpaws/cloud_watch/default_log_formatter"
4
+
5
+ namespace :log do
6
+ desc "Set Log Group."
7
+ task :set_log_group do
8
+ aws = Putpaws::CloudWatch::LogCommand.config(fetch(:app))
9
+ log_groups = aws.list_log_groups.map{|a|
10
+ [a.log_group_name, a]
11
+ }.to_h
12
+ raise "Log group not found on your permission" if log_groups.empty?
13
+
14
+ log_group = if log_groups.length == 1
15
+ log_groups.first
16
+ else
17
+ prompt = TTY::Prompt.new
18
+ selected = prompt.select("Choose a log_group you're going to operate", log_groups.keys)
19
+ log_groups[selected]
20
+ end
21
+
22
+ set :log_group, log_group
23
+ end
24
+
25
+ desc "Tail log with follow."
26
+ task tailf: :set_log_group do
27
+ ENV['follow'] = '1'
28
+ Rake::Task['log:tail'].invoke
29
+ end
30
+
31
+ # Check: https://github.com/aws/aws-cli/blob/v2/awscli/customizations/logs/tail.py
32
+ desc "Tail log."
33
+ task tail: :set_log_group do
34
+ log_group = fetch(:log_group)
35
+ log_formatter = fetch(:log_formatter) {Putpaws::CloudWatch::DefaultLogFormatter.new}
36
+ aws = Putpaws::CloudWatch::LogCommand.config(fetch(:app))
37
+ aws.log_group = log_group.log_group_name
38
+
39
+ log_event_args = Putpaws::CloudWatch::LogCommand.filter_args(since: ENV['since'], since_for: ENV['for'])
40
+
41
+ loop do
42
+ events, next_args = aws.tail_log_events(**log_event_args)
43
+ events.each {|a| puts log_formatter.call(a)}
44
+ log_event_args = next_args
45
+ unless log_event_args[:next_token]
46
+ ENV['follow'] ? sleep(5) : break
47
+ end
48
+ rescue Interrupt
49
+ exit
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,14 @@
1
+ require "putpaws/env"
2
+ require "forwardable"
3
+
4
+ module Putpaws::DSL
5
+ extend Forwardable
6
+
7
+ def_delegators :env,
8
+ :fetch, :set, :delete
9
+
10
+ def env
11
+ Putpaws::Env.default
12
+ end
13
+ end
14
+ extend Putpaws::DSL
@@ -0,0 +1 @@
1
+ load File.expand_path("../tasks/ecs_task.rake", __FILE__)
@@ -0,0 +1,64 @@
1
+ require 'aws-sdk-ecs'
2
+
3
+ module Putpaws::Ecs
4
+ class TaskCommand
5
+ def self.config(config)
6
+ new(config.ecs_command_params)
7
+ end
8
+
9
+ attr_reader :ecs_client
10
+ attr_reader :region, :cluster, :task_name_prefix
11
+ attr_accessor :ecs_task
12
+ def initialize(region:, cluster:, task_name_prefix: nil)
13
+ @ecs_client = Aws::ECS::Client.new({region: region})
14
+ @region = region
15
+ @cluster = cluster
16
+ @task_name_prefix = task_name_prefix
17
+ @ecs_task = nil
18
+ end
19
+
20
+ def list_ecs_tasks
21
+ res = ecs_client.list_tasks(cluster: cluster)
22
+ res = ecs_client.describe_tasks(tasks: res.task_arns, cluster: cluster)
23
+ return res.tasks unless task_name_prefix
24
+ res.tasks.select{|t|
25
+ _, name = t.task_definition_arn.split('task-definition/')
26
+ name.start_with?(task_name_prefix)
27
+ }
28
+ end
29
+
30
+ def get_attach_command(container: 'app')
31
+ raise "ECS Task Not Set" unless ecs_task
32
+ ctn = ecs_task.containers.detect{|c| c.name == container}
33
+ task_id = ecs_task.task_arn.split('/').last
34
+ raise "Container: #{container} not found" unless ctn
35
+ res = ecs_client.execute_command({
36
+ cluster: cluster,
37
+ container: container,
38
+ command: '/bin/bash',
39
+ interactive: true,
40
+ task: ecs_task.task_arn,
41
+ })
42
+
43
+ # https://github.com/aws/aws-cli/blob/2a6136010d8656a605d41d1e7b5fdab3c2930cad/awscli/customizations/ecs/executecommand.py#L105
44
+ session_json = {
45
+ "SessionId" => res.session.session_id,
46
+ "StreamUrl" => res.session.stream_url,
47
+ "TokenValue" => res.session.token_value,
48
+ }.to_json
49
+ target_json = {
50
+ "Target" => "ecs:#{cluster}_#{task_id}_#{ctn.runtime_id}"
51
+ }.to_json
52
+ cmd = [
53
+ "session-manager-plugin",
54
+ session_json.dump,
55
+ @region,
56
+ "StartSession",
57
+ 'test',
58
+ target_json.dump,
59
+ "https://ssm.ap-northeast-1.amazonaws.com"
60
+ ]
61
+ cmd.join(' ')
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,29 @@
1
+ require "tty-prompt"
2
+ require "putpaws/ecs/task_command"
3
+
4
+ namespace :ecs do
5
+ desc "Set ECS task."
6
+ task :set_task do
7
+ aws = Putpaws::Ecs::TaskCommand.config(fetch(:app))
8
+ ecs_tasks = aws.list_ecs_tasks.map{|t|
9
+ task_id = t.task_arn.split('/').last
10
+ task_def = t.task_definition_arn.split('/').last
11
+ ["#{task_id} (#{task_def}) #{t.last_status}", t]
12
+ }.to_h
13
+ prompt = TTY::Prompt.new
14
+ selected = prompt.select("Choose a task you're going to operate", ecs_tasks.keys)
15
+ ecs_task = ecs_tasks[selected]
16
+
17
+ set :ecs_task, ecs_task
18
+ end
19
+
20
+ desc "Attach on ECS task. You need to enable ECS Exec on a specified task."
21
+ task attach: :set_task do
22
+ ecs_task = fetch(:ecs_task)
23
+ aws = Putpaws::Ecs::TaskCommand.config(fetch(:app))
24
+ aws.ecs_task = ecs_task
25
+ cmd = aws.get_attach_command
26
+ puts cmd
27
+ system(cmd)
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # require 'delegate'
2
+
3
+ class Putpaws::Env
4
+ def self.default
5
+ @env ||= new({})
6
+ end
7
+
8
+ attr_reader :values
9
+
10
+ def initialize(values)
11
+ @values = values
12
+ end
13
+
14
+ def set(key, value=nil, &block)
15
+ values[key] = block || value
16
+ end
17
+
18
+ def fetch(key, default=nil, &block)
19
+ fetch_for(key, default, &block)
20
+ end
21
+
22
+ def fetch_for(key, default, &block)
23
+ block ? values.fetch(key, &block) : values.fetch(key, default)
24
+ end
25
+
26
+ def delete(key)
27
+ values.delete(key)
28
+ end
29
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path("../tasks/hello.rake", __FILE__)
@@ -0,0 +1,10 @@
1
+ require "putpaws/dsl"
2
+ include Putpaws::DSL
3
+
4
+ apps = Putpaws::ApplicationConfig.all
5
+
6
+ apps.each do |app|
7
+ Rake::Task.define_task(app.name) do
8
+ set(:app, app)
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ desc "Execute remote commands"
2
+ task :hello do
3
+ puts "hello!!"
4
+ end
@@ -0,0 +1,3 @@
1
+ module Putpaws
2
+ VERSION = "0.0.1"
3
+ end
data/lib/putpaws.rb ADDED
File without changes
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: putpaws
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - metheglin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tty-prompt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-ecs
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-cloudwatchlogs
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: aws fargate based infra management
70
+ email: pigmybank@gmail.com
71
+ executables:
72
+ - putpaws
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - bin/putpaws
77
+ - lib/Putpawsfile
78
+ - lib/putpaws.rb
79
+ - lib/putpaws/all.rb
80
+ - lib/putpaws/application.rb
81
+ - lib/putpaws/application_config.rb
82
+ - lib/putpaws/cloud_watch/default_log_formatter.rb
83
+ - lib/putpaws/cloud_watch/log_command.rb
84
+ - lib/putpaws/cloud_watch/log_task.rb
85
+ - lib/putpaws/cloud_watch/tasks/log_task.rake
86
+ - lib/putpaws/dsl.rb
87
+ - lib/putpaws/ecs/ecs_task.rb
88
+ - lib/putpaws/ecs/task_command.rb
89
+ - lib/putpaws/ecs/tasks/ecs_task.rake
90
+ - lib/putpaws/env.rb
91
+ - lib/putpaws/hello.rb
92
+ - lib/putpaws/setup.rb
93
+ - lib/putpaws/tasks/hello.rake
94
+ - lib/putpaws/version.rb
95
+ homepage: https://rubygems.org/gems/putpaws
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '2.7'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.1.4
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Put your paws up!!
118
+ test_files: []