ecs-rails 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: 6381c88cb4b0507a1f9f667290768c0b0d060b1308108b4699e7b0d6107f048b
4
+ data.tar.gz: 1ca8e857d5ea22a9dae7e07388c5124f85f41a81d20b4a5e169852ce942fcba0
5
+ SHA512:
6
+ metadata.gz: ab8bda757afb402c1cdb8096a24ec51a8973fb78e8be9c6f4241e936b68aff153f3d913fa5c15d7336421365972e750130bb36ce91a3da410cf6077cd47321f8
7
+ data.tar.gz: 87c30bb9091b7fff68cb535770d22dd4f6d8da21ee9c119eeec06e649f36a8e42f9e9a27d8c2815c67d2894904ff7b38c3c879c79465f3f34e28773c0406f251
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .env*
3
+ .byebug_history
4
+ Gemfile.lock
5
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ gem 'byebug'
8
+ gem 'rspec', '~> 3.0'
data/bin/ecs ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'byebug'
4
+
5
+ require 'optparse'
6
+ require_relative '../lib/ecs-rails'
7
+
8
+ EcsRails.aws_region = ENV['AWS_REGION']
9
+ EcsRails.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
10
+ EcsRails.aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
11
+ EcsRails.container_name = ENV['CONTAINER_NAME'] || 'webapp'
12
+
13
+ options = {}
14
+ OptionParser.new do |opts|
15
+ opts.banner = "Usage: ecsconsole [options]"
16
+
17
+ opts.on("-c", "--cluster CLUSTER_STRING", "Give a substring present in your cluster name. Ex: 'ecs console -c prod' to connect to cluster webapp-cluster-prod-e950a13") do |c|
18
+ options[:cluster] = c
19
+ end
20
+
21
+ opts.on("-s", "--service SERVICE_STRING", "Give a substring present in your service name. Ex: 'ecs console -c prod -s app' to connect to service webapp-app-prod-7c7cad7") do |c|
22
+ options[:service] = c
23
+ end
24
+ end.parse!
25
+
26
+ command_keyword = ARGV[0]
27
+
28
+ # byebug
29
+
30
+ # command = EcsRails::CommandFactory.new(command_keyword, options).command
31
+ # command.call
32
+
33
+ EcsRails::CommandExecutor.call(command_keyword, options)
data/ecs-rails.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/ecs-rails/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ecs-rails"
7
+ s.version = EcsRails::VERSION
8
+ s.summary = "Connect to your AWS ECS tasks"
9
+ s.description = "Connect to your AWS ECS tasks"
10
+ s.authors = ["Franck D'agostini"]
11
+ s.email = "franck.dagostini@gmail.com"
12
+ s.homepage = "https://rubygems.org/gems/ecs-rails"
13
+ s.license = "MIT"
14
+ s.required_ruby_version = '>= 2.7.0'
15
+ s.require_paths = ['lib']
16
+
17
+ s.files = `git ls-files -z`.split("\x0")
18
+
19
+ s.add_development_dependency 'byebug', '~> 11.1'
20
+ s.add_development_dependency 'rspec', '~> 3.6', '>= 3.6.0'
21
+
22
+ s.add_runtime_dependency "aws-sdk-ecs", "~> 1.132", "> 1.132"
23
+ s.add_runtime_dependency "aws-sdk-ec2", "~> 1.425", "> 1.425"
24
+ s.add_runtime_dependency "tty-prompt", "~> 0.23", "> 0.23"
25
+ end
@@ -0,0 +1,20 @@
1
+ module EcsRails
2
+ class Client
3
+
4
+ def client
5
+ Aws::ECS::Client.new(aws_credentials)
6
+ end
7
+
8
+ private
9
+
10
+ def aws_credentials
11
+ @aws_credentials ||= {
12
+ region: ENV['AWS_REGION'],
13
+ access_key_id: ENV['AWS_ACCESS_KEY_ID'],
14
+ secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
15
+ }
16
+ end
17
+
18
+ end
19
+ end
20
+
@@ -0,0 +1,55 @@
1
+ module EcsRails
2
+ class ClusterSelector
3
+ def initialize(client, filter = nil)
4
+ @client = client
5
+ @filter = filter
6
+ end
7
+
8
+ def cluster
9
+ clusters.find { |cluster| cluster.cluster_name == cluster_name }
10
+ end
11
+
12
+ def call
13
+ puts('Fetching clusters...')
14
+ clusters = client.list_clusters.cluster_arns
15
+
16
+ if clusters.empty?
17
+ puts('No clusters found.')
18
+ exit
19
+ end
20
+
21
+ if filter
22
+ filtered_clusters = clusters.select { |cluster| cluster.include?(filter) }
23
+
24
+ if filtered_clusters.empty?
25
+ puts("No clusters found with this keyword '#{filter}'.")
26
+ exit
27
+ end
28
+
29
+ connecting_to_cluster(filtered_clusters.first)
30
+ return filtered_clusters.first
31
+ else
32
+ ask_for_cluster(clusters)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :client, :filter
39
+
40
+ def connecting_to_cluster(cluster_name)
41
+ puts('')
42
+ puts("Connecting to cluster '#{cluster_name}':")
43
+ puts('')
44
+ puts('=' * 50)
45
+ end
46
+
47
+ def ask_for_cluster(clusters)
48
+ prompt = TTY::Prompt.new
49
+ choices = clusters.map { |cluster| cluster.split('/').last }
50
+ choice = prompt.enum_select("Select a cluster:", choices)
51
+ clusters.grep(Regexp.new(choice)).first
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,48 @@
1
+ module EcsRails
2
+ class Command
3
+
4
+ def initialize(cluster_name = nil, service_name = nil)
5
+ @cluster_name = cluster_name
6
+ @service_name = service_name
7
+ end
8
+
9
+ def call
10
+ raise NotImplementedError
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :cluster_name, :service_name
16
+
17
+ def client
18
+ client = EcsRails::Client.new.client
19
+ end
20
+
21
+ def container_name
22
+ EcsRails.container_name
23
+ end
24
+
25
+ def region
26
+ EcsRails.aws_region
27
+ end
28
+
29
+ def selected_cluster
30
+ @selected_cluster ||= EcsRails::ClusterSelector.new(client, cluster_name).call
31
+ end
32
+
33
+ def selected_service
34
+ puts("Selected cluster: #{selected_cluster}")
35
+ @selected_service ||= EcsRails::ServiceSelector.new(client, selected_cluster, service_name).call
36
+ end
37
+
38
+ def task_id
39
+ puts("Selected service: #{selected_service}")
40
+ @task_id ||= EcsRails::TaskSelector.new(client, selected_cluster, selected_service).call
41
+ end
42
+
43
+ def test_mode?
44
+ defined?(RSpec)
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,26 @@
1
+ module EcsRails
2
+ class CommandFactory
3
+
4
+ def initialize(command_keyword, options = {})
5
+ @command_keyword = command_keyword
6
+ @cluster_name = options[:cluster]
7
+ @service_name = options[:service]
8
+ end
9
+
10
+ def command
11
+ case command_keyword
12
+ when 'console'
13
+ EcsRails::Console.new(cluster_name, service_name)
14
+ when 'bash'
15
+ EcsRails::Bash.new(cluster_name, service_name)
16
+ else
17
+ EcsRails::NullCommand.new
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :command_keyword, :cluster_name, :service_name
24
+
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module EcsRails
2
+ class Console < Command
3
+
4
+ def call
5
+ return full_command if test_mode?
6
+
7
+ puts "Executing command: #{full_command}"
8
+ system(full_command)
9
+ end
10
+
11
+ private
12
+
13
+ def full_command
14
+ @full_command ||= "aws ecs execute-command --region #{region} --cluster #{selected_cluster} --task #{task_id} --container #{container_name} --command \'#{command}\' --interactive"
15
+ end
16
+
17
+
18
+ def command
19
+ "bundle exec rails console"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ module EcsRails
2
+ class EcsRailsConfiguration
3
+ attr_accessor :aws_region
4
+ attr_accessor :aws_access_key_id
5
+ attr_accessor :aws_secret_access_key
6
+ attr_accessor :container_name
7
+
8
+ def self.setup
9
+ new.tap do |config|
10
+ yield config if block_given?
11
+ end
12
+ end
13
+
14
+ def initialize
15
+ @aws_region = 'us-east-1'
16
+ @aws_access_key_id = nil
17
+ @aws_secret_access_key = nil
18
+ @container_name = nil
19
+ end
20
+
21
+ def aws_region=(region)
22
+ @aws_region = region
23
+ end
24
+
25
+ def aws_access_key_id=(access_key_id)
26
+ @aws_access_key_id = access_key_id
27
+ end
28
+
29
+ def aws_secret_access_key=(secret_access_key)
30
+ @aws_secret_access_key = secret_access_key
31
+ end
32
+
33
+ def container_name=(container_name)
34
+ @container_name = container_name
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ module EcsRails
2
+ class NullCommand
3
+
4
+ def call
5
+ puts 'You must specify a command (console, bash, logs, …)'
6
+ exit 1
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,59 @@
1
+ module EcsRails
2
+ class ServiceSelector
3
+
4
+ def initialize(client, cluster, service_filter = nil)
5
+ @client = client
6
+ @cluster = cluster
7
+ @service_filter = service_filter
8
+ end
9
+
10
+ def call
11
+ puts('Fetching services...')
12
+ services = client.list_services(cluster: cluster).service_arns
13
+
14
+ if services.empty?
15
+ puts('No services found in the cluster.')
16
+ exit
17
+ end
18
+
19
+ if services.size == 1
20
+ service = services.first
21
+ connecting_to_service(service)
22
+ return service
23
+ end
24
+
25
+ if service_filter
26
+ filtered_services = services.select { |service| service.include?(service_filter) }
27
+
28
+ if filtered_services.empty?
29
+ puts("No service found with this keyword '#{service_filter}'.")
30
+ exit
31
+ end
32
+
33
+ connecting_to_service(filtered_services.first)
34
+ return filtered_services.first
35
+ else
36
+ ask_for_service(services)
37
+ end
38
+ end
39
+
40
+
41
+ private
42
+
43
+ attr_reader :client, :cluster, :service_filter
44
+
45
+ def connecting_to_service(service_name)
46
+ puts('')
47
+ puts("Connecting to service '#{service_name}':")
48
+ puts('')
49
+ puts('=' * 50)
50
+ end
51
+
52
+ def ask_for_service(services)
53
+ prompt = TTY::Prompt.new
54
+ choices = services.map { |service| service.split('/').last }
55
+ choice = prompt.enum_select("Select a service:", choices)
56
+ services.grep(Regexp.new(choice)).first
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,52 @@
1
+ module EcsRails
2
+ class TaskSelector
3
+
4
+ def initialize(client, cluster_name, service_name)
5
+ @client = client
6
+ @cluster_name = cluster_name
7
+ @service_name = service_name
8
+ end
9
+
10
+ def call
11
+ puts('Fetching tasks...')
12
+ service_descriptions = client.describe_services(cluster: cluster_name, services: [service_name]).services
13
+
14
+ service_descriptions.each do |service|
15
+ service.deployments.each do |deployment|
16
+ task_arns = client.list_tasks(cluster: cluster_name, service_name: service.service_name, desired_status: 'RUNNING').task_arns
17
+ next if task_arns.empty?
18
+
19
+ tasks = client.describe_tasks(cluster: cluster_name, tasks: task_arns).tasks
20
+
21
+ # Filter tasks by deployment's task definition
22
+ tasks_for_deployment = tasks.select { |task| task.task_definition_arn == deployment.task_definition }
23
+
24
+ if tasks_for_deployment.empty?
25
+ puts(" No tasks found for this deployment.")
26
+ elsif tasks_for_deployment.size == 1
27
+ puts(" Task: #{task_arns.first}")
28
+ return task_arns.first
29
+ else
30
+ tasks_for_deployment.each_with_index do |task, index|
31
+ puts("#{index+1}. Task: #{task.task_arn.split('/').last}, Last Status: #{task.last_status}, Desired Status: #{task.desired_status}")
32
+ end
33
+ end
34
+
35
+ selection = $stdin.gets.chomp.to_i
36
+ puts("Selected task: #{tasks_for_deployment[selection - 1].task_arn}")
37
+
38
+ unless selection.between?(1, task_arns.size)
39
+ puts('Invalid selection.')
40
+ exit
41
+ end
42
+
43
+ return task_arns[selection - 1]
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :client, :cluster_name, :service_name
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EcsRails
4
+ VERSION = '0.0.1'
5
+ end
data/lib/ecs-rails.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-ecs'
4
+
5
+ # version
6
+ require_relative 'ecs-rails/version'
7
+
8
+ # configuration
9
+ require_relative 'ecs-rails/ecs_rails_configuration'
10
+
11
+ # client
12
+ require_relative 'ecs-rails/client'
13
+
14
+ # selectors
15
+ require_relative 'ecs-rails/cluster_selector'
16
+ require_relative 'ecs-rails/service_selector'
17
+ require_relative 'ecs-rails/task_selector'
18
+
19
+ # commands
20
+ require_relative 'ecs-rails/command_executor'
21
+ require_relative 'ecs-rails/command_factory'
22
+ require_relative 'ecs-rails/command'
23
+ require_relative 'ecs-rails/console'
24
+ require_relative 'ecs-rails/bash'
25
+ require_relative 'ecs-rails/null_command'
26
+
27
+
28
+ # prompt
29
+ require "tty-prompt"
30
+
31
+ module EcsRails
32
+
33
+ @configuration = EcsRails::EcsRailsConfiguration.setup
34
+
35
+ class << self
36
+ extend Forwardable
37
+
38
+ def_delegators :@configuration, :aws_region, :aws_region=
39
+ def_delegators :@configuration, :aws_access_key_id, :aws_access_key_id=
40
+ def_delegators :@configuration, :aws_secret_access_key, :aws_secret_access_key=
41
+ def_delegators :@configuration, :container_name, :container_name=
42
+ end
43
+
44
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ecs-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Franck D'agostini
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: byebug
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '11.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '11.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.6'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 3.6.0
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.6'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.6.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: aws-sdk-ecs
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.132'
54
+ - - ">"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.132'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '1.132'
64
+ - - ">"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.132'
67
+ - !ruby/object:Gem::Dependency
68
+ name: aws-sdk-ec2
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.425'
74
+ - - ">"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.425'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.425'
84
+ - - ">"
85
+ - !ruby/object:Gem::Version
86
+ version: '1.425'
87
+ - !ruby/object:Gem::Dependency
88
+ name: tty-prompt
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '0.23'
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.23'
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.23'
104
+ - - ">"
105
+ - !ruby/object:Gem::Version
106
+ version: '0.23'
107
+ description: Connect to your AWS ECS tasks
108
+ email: franck.dagostini@gmail.com
109
+ executables: []
110
+ extensions: []
111
+ extra_rdoc_files: []
112
+ files:
113
+ - ".gitignore"
114
+ - Gemfile
115
+ - bin/ecs
116
+ - ecs-rails.gemspec
117
+ - lib/ecs-rails.rb
118
+ - lib/ecs-rails/client.rb
119
+ - lib/ecs-rails/cluster_selector.rb
120
+ - lib/ecs-rails/command.rb
121
+ - lib/ecs-rails/command_factory.rb
122
+ - lib/ecs-rails/console.rb
123
+ - lib/ecs-rails/ecs_rails_configuration.rb
124
+ - lib/ecs-rails/null_command.rb
125
+ - lib/ecs-rails/service_selector.rb
126
+ - lib/ecs-rails/task_selector.rb
127
+ - lib/ecs-rails/version.rb
128
+ homepage: https://rubygems.org/gems/ecs-rails
129
+ licenses:
130
+ - MIT
131
+ metadata: {}
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: 2.7.0
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubygems_version: 3.4.12
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Connect to your AWS ECS tasks
151
+ test_files: []