ECSD 0.9.0
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 +7 -0
- data/CHANGELOG.md +3 -0
- data/README.md +61 -0
- data/lib/ecsd/config.rb +59 -0
- data/lib/ecsd/constants.rb +10 -0
- data/lib/ecsd/core.rb +60 -0
- data/lib/ecsd/errors.rb +7 -0
- data/lib/ecsd/helpers/common.rb +40 -0
- data/lib/ecsd/helpers/ec2.rb +17 -0
- data/lib/ecsd/helpers/ecs.rb +38 -0
- data/lib/ecsd/runner.rb +35 -0
- data/lib/ecsd/template.rb +26 -0
- data/lib/ecsd/version.rb +3 -0
- data/lib/ecsd.rb +20 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c069cbb5aaee9472442db19e06ac7985d348888fe4792e64f507b1e99d385505
|
4
|
+
data.tar.gz: 20ee8759eb119d57eda9f092b601218b0e4653bc5baf38db83fd4fd97f65926a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c6983cfa3baaeb1218206de55ee79aa347ffb8066ad93c2c39e7ae343183413780fcef89c4e394eff1d2a20af940a896988965081cae4628293defe818c30791
|
7
|
+
data.tar.gz: c4a1d62a25eb836623cd27c824b5f17e094e90dcc1c4b279e55a7b35a8292242d1f417ca9d40785f43ef719b1269b1306a160c44c4d6b89d6fae40458a073ecd
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# ECSD
|
2
|
+
|
3
|
+
Simple service for generate ec2_sd config files (one file per cluster)
|
4
|
+
to allow ECS cluster to be discovered by prometheus monitoring service.
|
5
|
+
|
6
|
+
Warning! Tested only with EC2 cluster model.
|
7
|
+
Working with fargate cluster model is not guaranteed
|
8
|
+
|
9
|
+
## Getting started
|
10
|
+
|
11
|
+
First create AWS IAM user with roles for describe and list.
|
12
|
+
|
13
|
+
Then add gem to your service:
|
14
|
+
```ruby
|
15
|
+
gem 'ecsd'
|
16
|
+
```
|
17
|
+
Or install directly:
|
18
|
+
```
|
19
|
+
$ gem install ecsd
|
20
|
+
```
|
21
|
+
|
22
|
+
To start discover ECS cluster define config:
|
23
|
+
```ruby
|
24
|
+
require 'ecsd'
|
25
|
+
|
26
|
+
ECSD.config do |c|
|
27
|
+
c.clusters = %w[cluster_name]
|
28
|
+
c.region = 'region'
|
29
|
+
c.credentials = { AWS_ACCESS_KEY_ID: 'ID', AWS_SECRET_ACCESS_KEY: 'SECRET' }
|
30
|
+
c.options = {
|
31
|
+
export_path: '/dir/path/to/export/folder',
|
32
|
+
timeout: 101
|
33
|
+
}
|
34
|
+
c.logger = Logger.new($stdout)
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
- `clusters` - array of cluster names to discover. Same name as ECS cluster
|
39
|
+
- `region` - AWS region name where ECS cluster deployed
|
40
|
+
- `credentials` - `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` from your IAM user
|
41
|
+
- `options`
|
42
|
+
- `export_path` - path to export config folder. Default is `#{service root}/export`
|
43
|
+
- `timeout` - time in seconds between cluster discovery cycles.
|
44
|
+
- `logger` - define your logger or default logger will be set
|
45
|
+
|
46
|
+
Then start discover:
|
47
|
+
```ruby
|
48
|
+
ECSD.start
|
49
|
+
```
|
50
|
+
By default ECSD continuously discovering defined clusters and save templates into
|
51
|
+
corresponded files.
|
52
|
+
|
53
|
+
Also it's possible to start only ony cycle discover:
|
54
|
+
```ruby
|
55
|
+
ECSD.start(cycle: false)
|
56
|
+
```
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
[Fork the project](https://github.com/Ad1n/ECSD) and send pull
|
61
|
+
requests.
|
data/lib/ecsd/config.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# @example
|
2
|
+
# ECSD.config do |c|
|
3
|
+
# c.clusters = %w[cluster0 cluster1 cluster2]
|
4
|
+
# c.region = "aws_region_name"
|
5
|
+
# c.credentials = { AWS_ACCESS_KEY_ID: "ID", AWS_SECRET_ACCESS_KEY: "SECRET" }
|
6
|
+
# c.options = {
|
7
|
+
# export_path: 'dir/path/to/export_config/folder',
|
8
|
+
# timeout: 101
|
9
|
+
# }
|
10
|
+
# c.logger = Logger.new($stdout)
|
11
|
+
# end
|
12
|
+
module ECSD
|
13
|
+
unless defined?(CoreConfig)
|
14
|
+
class CoreConfig < Struct.new(:credentials, :region, :clusters, :options, :logger)
|
15
|
+
# @param payload [Hash] { AWS_ACCESS_KEY_ID: 'XXX', AWS_SECRET_ACCESS_KEY: 'XXX' }
|
16
|
+
def credentials=(payload)
|
17
|
+
validate_credentials!(payload)
|
18
|
+
super(::Aws::Credentials.new(
|
19
|
+
payload[:AWS_ACCESS_KEY_ID],
|
20
|
+
payload[:AWS_SECRET_ACCESS_KEY]
|
21
|
+
))
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param payload [Hash] { export_path: 'path/to/folder', timeout: 0 }
|
25
|
+
def options=(payload)
|
26
|
+
payload[:export_path] ||= ECSD::DEFAULT_EXPORT_PATH
|
27
|
+
payload[:timeout] ||= 0
|
28
|
+
super(payload)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Removing the secret credentials from the default inspect string.
|
34
|
+
# @api private
|
35
|
+
def inspect
|
36
|
+
"#<#{self.class.name} clusters=#{clusters.inspect} region=#{region.inspect} options=#{options.inspect} logger=#{logger.inspect}>"
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_credentials!(p)
|
40
|
+
raise Config::ValidationError, 'Please, provide AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY' if p.values.size != 2
|
41
|
+
raise Config::ValidationError, 'Invalid AWS credentials' unless p.values.all? String
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def config
|
47
|
+
yield @config if block_given?
|
48
|
+
|
49
|
+
@config
|
50
|
+
end
|
51
|
+
module_function :config
|
52
|
+
|
53
|
+
def logger
|
54
|
+
@config.logger || Logger.new($stdout)
|
55
|
+
end
|
56
|
+
module_function :logger
|
57
|
+
|
58
|
+
@config ||= CoreConfig.new
|
59
|
+
end
|
data/lib/ecsd/core.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module ECSD
|
2
|
+
class Core
|
3
|
+
include Helpers::EC2
|
4
|
+
include Helpers::ECS
|
5
|
+
include Helpers::Common
|
6
|
+
|
7
|
+
attr_reader :ecs, :ec2, :cluster
|
8
|
+
|
9
|
+
def initialize(cluster_name)
|
10
|
+
@ecs = ::Aws::ECS::Client.new(
|
11
|
+
credentials: ECSD.config.credentials,
|
12
|
+
region: ECSD.config.region
|
13
|
+
)
|
14
|
+
@ec2 = ::Aws::EC2::Client.new(
|
15
|
+
credentials: ECSD.config.credentials,
|
16
|
+
region: ECSD.config.region
|
17
|
+
)
|
18
|
+
@cluster = cluster_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle!
|
22
|
+
task_arns = task_arns(cluster)
|
23
|
+
tasks = connected_tasks(cluster, task_arns)
|
24
|
+
handle_tasks(tasks)
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_tasks(tasks)
|
28
|
+
ECSD.logger.info <<~TEXT
|
29
|
+
|
30
|
+
Handle cluster: [#{cluster}]
|
31
|
+
Started at: #{Time.now}
|
32
|
+
TEXT
|
33
|
+
tasks.map do |t|
|
34
|
+
instance_id = instance_id(cluster, t)
|
35
|
+
task_definition_name, task_revision = task_definition_data(t)
|
36
|
+
task_container = task_container(t)
|
37
|
+
|
38
|
+
TEMPLATE.call(
|
39
|
+
ip_addr: instance_ip_addr(instance_id),
|
40
|
+
dyn_port: dyn_port(task_container),
|
41
|
+
task_id: task_id(t),
|
42
|
+
task_definition_name: task_definition_name,
|
43
|
+
task_revision: task_revision,
|
44
|
+
cluster_name: cluster_name(t),
|
45
|
+
container_name: task_container.name,
|
46
|
+
instance_id: instance_id
|
47
|
+
)
|
48
|
+
end.then do |r|
|
49
|
+
write_to_file(cluster, r)
|
50
|
+
|
51
|
+
ECSD.logger.info <<~TEXT
|
52
|
+
|
53
|
+
Handled cluster: [#{cluster}]
|
54
|
+
Tasks in cluster: #{r.count}
|
55
|
+
Finished at: #{Time.now}
|
56
|
+
TEXT
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/ecsd/errors.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module ECSD
|
2
|
+
module Helpers
|
3
|
+
module Common
|
4
|
+
def write_to_file(cluster, data, path = ECSD.config.options[:export_path] || DEFAULT_EXPORT_PATH)
|
5
|
+
File.open("#{path}/#{cluster.downcase}.yml", 'w') do |f|
|
6
|
+
f.write(data.join)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def task_id(task)
|
11
|
+
task.task_arn
|
12
|
+
.split('/')
|
13
|
+
.last
|
14
|
+
end
|
15
|
+
|
16
|
+
def task_definition_data(task)
|
17
|
+
task.task_definition_arn
|
18
|
+
.split('/')
|
19
|
+
.last
|
20
|
+
.split(':')
|
21
|
+
end
|
22
|
+
|
23
|
+
def cluster_name(task)
|
24
|
+
task.cluster_arn
|
25
|
+
.split('/')
|
26
|
+
.last
|
27
|
+
end
|
28
|
+
|
29
|
+
def task_container(task)
|
30
|
+
task.containers.find { |c| c.task_arn == task.task_arn }
|
31
|
+
end
|
32
|
+
|
33
|
+
def dyn_port(task_container)
|
34
|
+
task_container.network_bindings
|
35
|
+
.first
|
36
|
+
.host_port
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ECSD
|
2
|
+
module Helpers
|
3
|
+
module EC2
|
4
|
+
# @param instance_id [String]
|
5
|
+
# @return [String] private ip
|
6
|
+
def instance_ip_addr(instance_id)
|
7
|
+
ec2.describe_instances(instance_ids: Array(instance_id))
|
8
|
+
.data
|
9
|
+
.reservations
|
10
|
+
.first
|
11
|
+
.instances
|
12
|
+
.first
|
13
|
+
.private_ip_address
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ECSD
|
2
|
+
module Helpers
|
3
|
+
module ECS
|
4
|
+
# @param cluster [String] cluster name
|
5
|
+
# @return [Array<Array<String>>] batched task_arns by 100
|
6
|
+
def task_arns(cluster)
|
7
|
+
ecs.list_tasks(cluster: cluster).each.with_object([]) do |response, task_arns|
|
8
|
+
task_arns << response[:task_arns]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param cluster [String] cluster name
|
13
|
+
# @param task_arns [Array<Array<String>>] batched task_arns by 100
|
14
|
+
# @return [Array<Array<Aws::ECS::Types:Task>>] connected tasks
|
15
|
+
def connected_tasks(cluster, task_arns)
|
16
|
+
task_arns.each.with_object([]) do |pt_batch, connected_tasks|
|
17
|
+
connected_tasks << ecs.describe_tasks(cluster: cluster, tasks: pt_batch)
|
18
|
+
.data
|
19
|
+
.tasks
|
20
|
+
.select { |t| t.connectivity == ECSD::Constants::CONNECTIVITY[:connected] }
|
21
|
+
end.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param cluster [String] cluster name
|
25
|
+
# @param task [Aws::ECS::Types::Task]
|
26
|
+
# @return [String]
|
27
|
+
def instance_id(cluster, task)
|
28
|
+
ecs.describe_container_instances(
|
29
|
+
cluster: cluster,
|
30
|
+
container_instances: Array(task.container_instance_arn)
|
31
|
+
).data
|
32
|
+
.container_instances
|
33
|
+
.first
|
34
|
+
.ec2_instance_id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ecsd/runner.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module ECSD
|
2
|
+
# @info Start ECS clusters discovery
|
3
|
+
def start(cycle: :infinite)
|
4
|
+
case cycle
|
5
|
+
when :infinite
|
6
|
+
loop { invoke!; wait }
|
7
|
+
else
|
8
|
+
invoke!
|
9
|
+
end
|
10
|
+
end
|
11
|
+
module_function :start
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @info one discovery cycle invocation
|
16
|
+
def invoke!
|
17
|
+
config.clusters.each do |cluster|
|
18
|
+
core(cluster).handle!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
module_function :invoke!
|
22
|
+
|
23
|
+
# @info Initialize core component
|
24
|
+
# @param cluster [String] cluster name
|
25
|
+
def core(cluster)
|
26
|
+
ECSD::Core.new(cluster)
|
27
|
+
end
|
28
|
+
module_function :core
|
29
|
+
|
30
|
+
# @info Time in seconds between discovery iterations
|
31
|
+
def wait
|
32
|
+
sleep ECSD.config.options[:timeout]
|
33
|
+
end
|
34
|
+
module_function :wait
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ECSD
|
4
|
+
TEMPLATE = lambda do |ip_addr:,
|
5
|
+
dyn_port:,
|
6
|
+
task_id:,
|
7
|
+
task_definition_name:,
|
8
|
+
task_revision:,
|
9
|
+
cluster_name:,
|
10
|
+
container_name:,
|
11
|
+
instance_id:,
|
12
|
+
metrics_path: DEFAULT_METRICS_PATH|
|
13
|
+
<<~TEXT
|
14
|
+
- targets:
|
15
|
+
- #{ip_addr}:#{dyn_port}
|
16
|
+
labels:
|
17
|
+
task_id: #{task_id}
|
18
|
+
task_definition_name: #{task_definition_name}
|
19
|
+
task_revision: #{task_revision}
|
20
|
+
cluster_name: #{cluster_name}
|
21
|
+
container_name: #{container_name}
|
22
|
+
instance_id: #{instance_id}
|
23
|
+
__metrics_path__: #{metrics_path}
|
24
|
+
TEXT
|
25
|
+
end
|
26
|
+
end
|
data/lib/ecsd/version.rb
ADDED
data/lib/ecsd.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-sdk-ecs'
|
4
|
+
require 'aws-sdk-ec2'
|
5
|
+
|
6
|
+
require_relative 'ecsd/version'
|
7
|
+
require_relative 'ecsd/errors'
|
8
|
+
require_relative 'ecsd/constants'
|
9
|
+
require_relative 'ecsd/template'
|
10
|
+
require_relative 'ecsd/config'
|
11
|
+
require_relative 'ecsd/helpers/ec2'
|
12
|
+
require_relative 'ecsd/helpers/ecs'
|
13
|
+
require_relative 'ecsd/helpers/common'
|
14
|
+
require_relative 'ecsd/runner'
|
15
|
+
require_relative 'ecsd/core'
|
16
|
+
|
17
|
+
#:nodoc
|
18
|
+
module ECSD
|
19
|
+
include Constants
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ECSD
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anton Shevtsov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-28 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.298'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.298'
|
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.95'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.95'
|
41
|
+
description: Generate ec2 yaml config files to allow ECS cluster to be discovered
|
42
|
+
by prometheus monitoring service
|
43
|
+
email: shevtsovav@bk.ru
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- CHANGELOG.md
|
49
|
+
- README.md
|
50
|
+
- lib/ecsd.rb
|
51
|
+
- lib/ecsd/config.rb
|
52
|
+
- lib/ecsd/constants.rb
|
53
|
+
- lib/ecsd/core.rb
|
54
|
+
- lib/ecsd/errors.rb
|
55
|
+
- lib/ecsd/helpers/common.rb
|
56
|
+
- lib/ecsd/helpers/ec2.rb
|
57
|
+
- lib/ecsd/helpers/ecs.rb
|
58
|
+
- lib/ecsd/runner.rb
|
59
|
+
- lib/ecsd/template.rb
|
60
|
+
- lib/ecsd/version.rb
|
61
|
+
homepage:
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 2.4.0
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubygems_version: 3.1.4
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: ECS(EC2 model) discovery
|
84
|
+
test_files: []
|