putpaws 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []