ecs-rails-console 0.0.2

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: 89bf0d51f3784badf31b3b907df33c184653dff3e03b4907bde2a80f9558c750
4
+ data.tar.gz: 53630b5f467b6fd0d0f2c596fa636a710ab6384affd78e713bb970ce197fb177
5
+ SHA512:
6
+ metadata.gz: 365b37aeb0780b5f724e4f702650af90d7698793e8fb4edb9e02f2a41560058a7f3dd72b86c21f6c9f40e861cc2a2ef97e8aa075f4a003a823e8d619ff0c80ae
7
+ data.tar.gz: 84a2fd356d3553979c7dbd833ec2a1b9b249cc80547ca30800e0b22119a7615461d660c96c19e4f5c11df6394e11278b6d227b6707371abefbd427d5f30b07ee
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require 'ecs_rails_console'
6
+
7
+ $LOAD_PATH.push File.expand_path('../lib', __dir__)
8
+
9
+ options = {
10
+ environment: 'production',
11
+ command: 'bin/rails console'
12
+ }
13
+
14
+ opts = OptionParser.new do |option|
15
+ option.banner = 'Usage: ecs_rails_console [options]'
16
+
17
+ option.on('-g', '--generate-config', 'Generate config file') do
18
+ template = File.expand_path('../config/ecs_rails_console.yml', __dir__)
19
+ config_file = 'config/ecs_rails_console.yml'
20
+
21
+ if File.exist?(config_file)
22
+ puts 'Configuration file already exists and will be kept untouched.'
23
+ else
24
+ FileUtils.mkdir_p(File.dirname(config_file))
25
+ FileUtils.cp(template, "#{Dir.pwd}/#{config_file}")
26
+ puts "File generated: #{config_file}"
27
+ end
28
+ exit
29
+ end
30
+
31
+ option.on('-h', '--help', 'Display this help') do
32
+ puts option
33
+ exit
34
+ end
35
+
36
+ option.on('-eENVIRONMENT', '--environment=ENVIRONMENT', 'Rails environment') do |environment|
37
+ options[:environment] = environment
38
+ end
39
+
40
+ option.on('-v', '--version', 'Display version') do
41
+ require 'ecs_rails_console/version'
42
+ puts EcsRailsConsole::VERSION
43
+ exit
44
+ end
45
+ end
46
+
47
+ begin
48
+ opts.parse!
49
+ rescue OptionParser::InvalidOption => e
50
+ puts e
51
+ puts
52
+ puts opts
53
+ exit
54
+ end
55
+
56
+ options[:command] = ARGV.join(' ') unless ARGV.empty?
57
+
58
+ begin
59
+ EcsRailsConsole::Cli.run!(options)
60
+ rescue Gem::LoadError
61
+ puts 'ecs_rails_console is not in your Gemfile.'
62
+ exit 1
63
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EcsRailsConsole
4
+ end
5
+
6
+ require "ecs_rails_console/core"
7
+ require "ecs_rails_console/cli"
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "yaml"
5
+
6
+ module EcsRailsConsole
7
+ CONFIG_FILE = "#{Dir.pwd}/config/ecs_rails_console.yml"
8
+ SSH_OPTIONS = '-tq -oStrictHostKeyChecking=no'
9
+
10
+ class Cli < Core
11
+ def self.run!(options)
12
+ new(options).run!
13
+ end
14
+
15
+ def initialize(options)
16
+ super()
17
+ @environment = options[:environment]
18
+ @command = options[:command]
19
+ end
20
+
21
+ def run!
22
+ puts "Cluster name: #{cluster_name}"
23
+
24
+ task_description = run_task
25
+
26
+ public_ip = get_public_ip(task_description)
27
+
28
+ puts "it is running on: #{public_ip}"
29
+
30
+ system("ssh #{SSH_OPTIONS} #{ssh_user}@#{public_ip} 'cd /app ; #{command}'")
31
+ rescue Aws::ECS::Errors::ExpiredTokenException
32
+ puts "\nHey, it seems your token expired. Authenticate on AWS give another try."
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :environment, :command
38
+
39
+ def aws_credentials
40
+ config.slice(
41
+ "profile",
42
+ "access_key_id",
43
+ "secret_access_key",
44
+ "region"
45
+ ).transform_keys(&:to_sym)
46
+ end
47
+
48
+ def config
49
+ @config ||= YAML.load_file(CONFIG_FILE)[environment] || {}
50
+ end
51
+
52
+ def ssh_user
53
+ config[:ssh_user] || 'root'
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk-ecs"
4
+ require "aws-sdk-ec2"
5
+
6
+ module EcsRailsConsole
7
+ class Core
8
+ private
9
+
10
+ def ecs_client
11
+ @ecs_client ||= Aws::ECS::Client.new(aws_credentials)
12
+ end
13
+
14
+ def ec2_client
15
+ @ec2_client ||= Aws::EC2::Client.new(aws_credentials)
16
+ end
17
+
18
+ def vpc_id
19
+ @vpc_id ||= begin
20
+ id = ec2_client.describe_vpcs(
21
+ {
22
+ filters: [{
23
+ name: "tag:aws:cloudformation:stack-name",
24
+ values: ["#{cluster_name}InfraStack"]
25
+ }]
26
+ }
27
+ )[:vpcs].map(&:vpc_id).first
28
+ abort "Could not find VPC" if id.empty?
29
+
30
+ id
31
+ end
32
+ end
33
+
34
+ def subnet_ids(vpc_id)
35
+ @subnet_ids ||= begin
36
+ ids = ec2_client.describe_subnets(
37
+ {
38
+ filters: [
39
+ {name: "vpc-id", values: [vpc_id]},
40
+ {name: "tag:aws-cdk:subnet-type", values: ["Public"]}
41
+ ]
42
+ }
43
+ )[:subnets].map(&:subnet_id)
44
+ abort "Could not find subnets" if ids.empty?
45
+
46
+ ids
47
+ end
48
+ end
49
+
50
+ def console_security_group_ids(vpc_id)
51
+ @console_security_group_ids ||= begin
52
+ ids = ec2_client.describe_security_groups(
53
+ filters: [
54
+ {name: "vpc-id", values: [vpc_id]},
55
+ {name: "group-name",
56
+ values: config["security_groups"]}
57
+ ]
58
+ )[:security_groups].map(&:group_id)
59
+ abort "Could not find security groups" if ids.empty?
60
+
61
+ ids
62
+ end
63
+ end
64
+
65
+ def task_definition
66
+ @task_definition ||= begin
67
+ task_definition_name_regex = %r{.*/(#{config['task_definition']}):\d+}
68
+ task_definitions = ecs_client.list_task_definitions(status: "ACTIVE")
69
+ definition_arn = task_definitions[:task_definition_arns].detect do |arn|
70
+ arn.match(task_definition_name_regex)
71
+ end
72
+ abort "Could not find console task definition" if definition_arn.empty?
73
+
74
+ definition_arn.match(task_definition_name_regex).captures.first
75
+ end
76
+ end
77
+
78
+ def run_task
79
+ task = ecs_client.run_task(
80
+ {
81
+ cluster: cluster_name,
82
+ launch_type: "FARGATE",
83
+ task_definition: task_definition,
84
+ network_configuration: {
85
+ awsvpc_configuration: {
86
+ subnets: subnet_ids(vpc_id),
87
+ security_groups: console_security_group_ids(vpc_id),
88
+ assign_public_ip: "ENABLED"
89
+ }
90
+ }
91
+ }
92
+ )[:tasks].first
93
+
94
+ task_id = task[:task_arn].match(%r{.*/(\w+)$}).captures.first
95
+
96
+ ecs_client
97
+ .wait_until(:tasks_running, {cluster: cluster_name, tasks: [task_id]})[:tasks]
98
+ .first
99
+ end
100
+
101
+ def get_public_ip(task_description)
102
+ network_interface_id =
103
+ task_description[:attachments]
104
+ .first[:details]
105
+ .detect { |detail| detail[:name] == "networkInterfaceId" }[:value]
106
+
107
+ ec2_client.describe_network_interfaces(
108
+ network_interface_ids: [
109
+ network_interface_id
110
+ ]
111
+ )[:network_interfaces].first[:association][:public_ip]
112
+ end
113
+
114
+ def cluster_name
115
+ @cluster_name ||= config["cluster_name"]
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EcsRailsConsole
4
+ VERSION = "0.0.2"
5
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ecs-rails-console
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Eduardo Ramos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-ec2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.227'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.227'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-ecs
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.75'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.75'
41
+ - !ruby/object:Gem::Dependency
42
+ name: standard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1'
55
+ description: Provide a way to run Rails Console in a container running on AWS ECS
56
+ Fargate.
57
+ email:
58
+ - ramos.eduardo87@gmail.com
59
+ executables:
60
+ - ecs_rails_console
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - bin/ecs_rails_console
65
+ - lib/ecs_rails_console.rb
66
+ - lib/ecs_rails_console/cli.rb
67
+ - lib/ecs_rails_console/core.rb
68
+ - lib/ecs_rails_console/version.rb
69
+ homepage: http://rubygems.org/gems/ecs-rails-console
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '2.7'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.1.4
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Run Rails Console in AWS ECS
92
+ test_files: []