ecs-rails 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: 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: []