stax 0.0.2 → 0.0.3
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 +4 -4
- data/lib/stax.rb +4 -1
- data/lib/stax/aws/apigw.rb +24 -0
- data/lib/stax/aws/dynamodb.rb +77 -0
- data/lib/stax/aws/ecs.rb +10 -1
- data/lib/stax/aws/firehose.rb +19 -0
- data/lib/stax/aws/lambda.rb +4 -0
- data/lib/stax/aws/logs.rb +4 -0
- data/lib/stax/cfer.rb +18 -0
- data/lib/stax/meta.rb +39 -0
- data/lib/stax/mixin/apigw.rb +41 -0
- data/lib/stax/mixin/dynamodb.rb +44 -7
- data/lib/stax/mixin/dynamodb/backup.rb +41 -0
- data/lib/stax/mixin/dynamodb/throughput.rb +67 -0
- data/lib/stax/mixin/ecs.rb +139 -59
- data/lib/stax/mixin/firehose.rb +34 -0
- data/lib/stax/mixin/lambda.rb +27 -0
- data/lib/stax/mixin/logs.rb +12 -0
- data/lib/stax/stack/crud.rb +5 -0
- data/lib/stax/staxfile.rb +6 -0
- data/lib/stax/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0444c2ae4f050d73ba7cfd720eca476667102bf2
|
4
|
+
data.tar.gz: 764f80db2e413eade8840ef8f04c33d93003e190
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb84fb5b04019ef607f8f4a6658414fdda2b3dbf641a31bbedadfb785ab20f1e76c8d039529c71a52d5f18419ead4c5594297bdfc7303efef8ccb4f8d0ef0191
|
7
|
+
data.tar.gz: f65676b1870954bcc39c5d53a0ecd7b238d5b2fafd7bd2ea5761fc993dece0e33c7b8c2205ebf6a61021b148ba01f84f8856a523fd4d4c2fa6ff3bbfcea6fb41
|
data/lib/stax.rb
CHANGED
@@ -8,6 +8,7 @@ require 'stax/staxfile'
|
|
8
8
|
require 'stax/base'
|
9
9
|
require 'stax/git'
|
10
10
|
require 'stax/cli'
|
11
|
+
require 'stax/meta'
|
11
12
|
require 'stax/subcommand'
|
12
13
|
require 'stax/cfer'
|
13
14
|
require 'stax/stack'
|
@@ -32,4 +33,6 @@ require 'stax/mixin/emr'
|
|
32
33
|
require 'stax/mixin/ssh'
|
33
34
|
require 'stax/mixin/lambda'
|
34
35
|
require 'stax/mixin/dynamodb'
|
35
|
-
require 'stax/mixin/logs'
|
36
|
+
require 'stax/mixin/logs'
|
37
|
+
require 'stax/mixin/apigw'
|
38
|
+
require 'stax/mixin/firehose'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Stax
|
3
|
+
module Aws
|
4
|
+
class APIGateway < Sdk
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def client
|
9
|
+
@_client ||= ::Aws::APIGateway::Client.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def api(id)
|
13
|
+
client.get_rest_api(rest_api_id: id)
|
14
|
+
end
|
15
|
+
|
16
|
+
def stages(id, deployment = nil)
|
17
|
+
client.get_stages(rest_api_id: id, deployment_id: deployment).item
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/stax/aws/dynamodb.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module Stax
|
2
4
|
module Aws
|
3
5
|
class DynamoDB < Sdk
|
@@ -20,6 +22,81 @@ module Stax
|
|
20
22
|
client.describe_table(table_name: name).table.local_secondary_indexes || []
|
21
23
|
end
|
22
24
|
|
25
|
+
def key_schema(name)
|
26
|
+
client.describe_table(table_name: name).table.key_schema
|
27
|
+
end
|
28
|
+
|
29
|
+
## key schema as a hash
|
30
|
+
def keys(name)
|
31
|
+
key_schema(name).each_with_object({}) do |s, h|
|
32
|
+
h[s.key_type.downcase.to_sym] = s.attribute_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def do_scan(opt)
|
37
|
+
exclusive_start_key = nil
|
38
|
+
loop do
|
39
|
+
r = client.scan(opt.merge(exclusive_start_key: exclusive_start_key))
|
40
|
+
yield r
|
41
|
+
exclusive_start_key = r.last_evaluated_key
|
42
|
+
break unless exclusive_start_key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def scan(opt)
|
47
|
+
do_scan(opt) do |r|
|
48
|
+
r.items.each do |item|
|
49
|
+
puts JSON.generate(item)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def count(opt)
|
55
|
+
total = 0
|
56
|
+
do_scan(opt.merge(select: 'COUNT')) do |r|
|
57
|
+
total += r.count
|
58
|
+
end
|
59
|
+
return total
|
60
|
+
end
|
61
|
+
|
62
|
+
def query(opt)
|
63
|
+
exclusive_start_key = nil
|
64
|
+
loop do
|
65
|
+
r = client.query(opt.merge(exclusive_start_key: exclusive_start_key))
|
66
|
+
r.items.each do |item|
|
67
|
+
puts JSON.generate(item)
|
68
|
+
end
|
69
|
+
exclusive_start_key = r.last_evaluated_key
|
70
|
+
break unless exclusive_start_key
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def list_backups(opt = {})
|
75
|
+
last_arn = nil
|
76
|
+
backups = []
|
77
|
+
loop do
|
78
|
+
r = client.list_backups(opt.merge(exclusive_start_backup_arn: last_arn))
|
79
|
+
backups += r.backup_summaries
|
80
|
+
last_arn = r.last_evaluated_backup_arn
|
81
|
+
break unless last_arn
|
82
|
+
end
|
83
|
+
backups
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_backup(table_name, backup_name)
|
87
|
+
client.create_backup(
|
88
|
+
table_name: table_name,
|
89
|
+
backup_name: backup_name,
|
90
|
+
).backup_details
|
91
|
+
end
|
92
|
+
|
93
|
+
def restore_backup(table_name, backup_arn)
|
94
|
+
client.restore_table_from_backup(
|
95
|
+
target_table_name: table_name,
|
96
|
+
backup_arn: backup_arn,
|
97
|
+
).table_description
|
98
|
+
end
|
99
|
+
|
23
100
|
end
|
24
101
|
|
25
102
|
end
|
data/lib/stax/aws/ecs.rb
CHANGED
@@ -16,6 +16,10 @@ module Stax
|
|
16
16
|
client.describe_services(cluster: cluster, services: services).services
|
17
17
|
end
|
18
18
|
|
19
|
+
def update_service(opt)
|
20
|
+
client.update_service(opt).service
|
21
|
+
end
|
22
|
+
|
19
23
|
def task_definition(name)
|
20
24
|
client.describe_task_definition(task_definition: name).task_definition
|
21
25
|
end
|
@@ -27,7 +31,12 @@ module Stax
|
|
27
31
|
end
|
28
32
|
|
29
33
|
def tasks(cluster, status = :RUNNING)
|
30
|
-
|
34
|
+
tasks = list_tasks(cluster, status)
|
35
|
+
if tasks.empty?
|
36
|
+
[]
|
37
|
+
else
|
38
|
+
client.describe_tasks(cluster: cluster, tasks: tasks).tasks
|
39
|
+
end
|
31
40
|
end
|
32
41
|
|
33
42
|
def list_instances(cluster)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Firehose < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::Firehose::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def describe(name)
|
12
|
+
client.describe_delivery_stream(delivery_stream_name: name).delivery_stream_description
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/stax/aws/lambda.rb
CHANGED
data/lib/stax/aws/logs.rb
CHANGED
data/lib/stax/cfer.rb
CHANGED
@@ -5,6 +5,7 @@ module Stax
|
|
5
5
|
class_option :use_previous_value, aliases: '-u', type: :array, default: [], desc: 'params to use previous value'
|
6
6
|
|
7
7
|
no_commands do
|
8
|
+
|
8
9
|
def cfer_parameters
|
9
10
|
{}
|
10
11
|
end
|
@@ -48,11 +49,28 @@ module Stax
|
|
48
49
|
Cfer.generate!(cfer_template, opts)
|
49
50
|
end
|
50
51
|
|
52
|
+
## generate method does puts, so steal stdout into a string
|
53
|
+
def cfer_generate_string
|
54
|
+
capture_stdout do
|
55
|
+
cfer_generate
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
51
59
|
def cfer_tail
|
52
60
|
Cfer.tail!(stack_name, follow: true, number: 1)
|
53
61
|
rescue ::Aws::CloudFormation::Errors::ValidationError => e
|
54
62
|
puts e.message
|
55
63
|
end
|
64
|
+
|
65
|
+
## temporarily grab stdout to a string
|
66
|
+
def capture_stdout
|
67
|
+
stdout, $stdout = $stdout, StringIO.new
|
68
|
+
yield
|
69
|
+
$stdout.string
|
70
|
+
ensure
|
71
|
+
$stdout = stdout
|
72
|
+
end
|
73
|
+
|
56
74
|
end
|
57
75
|
|
58
76
|
end
|
data/lib/stax/meta.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Stax
|
2
|
+
class Cli < Base
|
3
|
+
|
4
|
+
no_commands do
|
5
|
+
## create order: default to stacks from Staxfile, override if needed
|
6
|
+
## delete will be this, in reverse order
|
7
|
+
def stack_order
|
8
|
+
Stax.stack_list
|
9
|
+
end
|
10
|
+
|
11
|
+
def stack_objects
|
12
|
+
stack_order.map(&method(:stack))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'create', 'meta create task'
|
17
|
+
def create
|
18
|
+
stack_objects.each do |s|
|
19
|
+
if s.exists?
|
20
|
+
say("Skipping: #{s.stack_name} exists", :yellow)
|
21
|
+
elsif y_or_n?("Create #{s.stack_name}?", :yellow)
|
22
|
+
s.create
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'delete', 'meta delete task'
|
28
|
+
def delete
|
29
|
+
stack_objects.reverse.each do |s|
|
30
|
+
if s.exists?
|
31
|
+
s.delete
|
32
|
+
else
|
33
|
+
say("#{s.stack_name} does not exist", :green)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'stax/aws/apigw'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module Apigw
|
5
|
+
def self.included(thor)
|
6
|
+
thor.desc(:apigw, 'API Gateway subcommands')
|
7
|
+
thor.subcommand(:apigw, Cmd::Apigw)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Cmd
|
12
|
+
class Apigw < SubCommand
|
13
|
+
|
14
|
+
no_commands do
|
15
|
+
def stack_apis
|
16
|
+
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ApiGateway::RestApi')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'ls', 'list APIS'
|
21
|
+
def ls
|
22
|
+
print_table stack_apis.map { |r|
|
23
|
+
a = Aws::APIGateway.api(r.physical_resource_id)
|
24
|
+
[a.name, a.id, a.endpoint_configuration.types.join(','), a.created_date, a.description]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'stages', 'list API stages'
|
29
|
+
def stages
|
30
|
+
stack_apis.each do |r|
|
31
|
+
api = Aws::APIGateway.api(r.physical_resource_id)
|
32
|
+
debug("Stages for API #{api.name} #{api.id}")
|
33
|
+
print_table Aws::APIGateway.stages(api.id).map { |s|
|
34
|
+
[s.stage_name, s.deployment_id, s.created_date, s.last_updated_date, s.description]
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/stax/mixin/dynamodb.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'yaml'
|
1
2
|
require 'stax/aws/dynamodb'
|
3
|
+
require_relative 'dynamodb/throughput'
|
4
|
+
require_relative 'dynamodb/backup'
|
2
5
|
|
3
6
|
module Stax
|
4
7
|
module DynamoDB
|
@@ -12,16 +15,25 @@ module Stax
|
|
12
15
|
class DynamoDB < SubCommand
|
13
16
|
|
14
17
|
COLORS = {
|
15
|
-
CREATING:
|
16
|
-
UPDATING:
|
17
|
-
DELETING:
|
18
|
-
ACTIVE:
|
18
|
+
CREATING: :yellow,
|
19
|
+
UPDATING: :yellow,
|
20
|
+
DELETING: :red,
|
21
|
+
ACTIVE: :green,
|
22
|
+
DELETED: :red,
|
23
|
+
AVAILABLE: :green,
|
19
24
|
}
|
20
25
|
|
21
26
|
no_commands do
|
22
27
|
def stack_tables
|
23
28
|
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::DynamoDB::Table')
|
24
29
|
end
|
30
|
+
|
31
|
+
## get table names from logical IDs, return all tables if nil
|
32
|
+
def stack_table_names(logical_ids)
|
33
|
+
stack_tables.tap do |tables|
|
34
|
+
tables.select! { |t| logical_ids.include?(t.logical_resource_id) } if logical_ids
|
35
|
+
end.map(&:physical_resource_id)
|
36
|
+
end
|
25
37
|
end
|
26
38
|
|
27
39
|
desc 'tables', 'list tables for stack'
|
@@ -52,9 +64,34 @@ module Stax
|
|
52
64
|
|
53
65
|
desc 'keys ID', 'get hash and range keys of table with ID'
|
54
66
|
def keys(id)
|
55
|
-
print_table Aws::DynamoDB.
|
56
|
-
|
57
|
-
|
67
|
+
print_table Aws::DynamoDB.keys(my.resource(id))
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'scan ID', 'scan table with given logical id from this stack'
|
71
|
+
def scan(id)
|
72
|
+
Aws::DynamoDB.scan(table_name: my.resource(id))
|
73
|
+
end
|
74
|
+
|
75
|
+
desc 'count ID', 'count items in table with given id'
|
76
|
+
def count(id)
|
77
|
+
puts Aws::DynamoDB.count(table_name: my.resource(id))
|
78
|
+
end
|
79
|
+
|
80
|
+
desc 'query ID HASH_VALUE [RANGE_VALUE]', 'query table with id'
|
81
|
+
def query(id, hash_value, range_value = nil)
|
82
|
+
name = my.resource(id)
|
83
|
+
k = Aws::DynamoDB.keys(name)
|
84
|
+
Aws::DynamoDB.query(
|
85
|
+
table_name: name,
|
86
|
+
expression_attribute_values: {
|
87
|
+
':h' => hash_value,
|
88
|
+
':r' => range_value,
|
89
|
+
}.compact,
|
90
|
+
key_condition_expression: [
|
91
|
+
"#{k[:hash]} = :h",
|
92
|
+
range_value ? "#{k[:range]} = :r" : nil,
|
93
|
+
].compact.join(' and '),
|
94
|
+
)
|
58
95
|
end
|
59
96
|
|
60
97
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Stax
|
2
|
+
module Cmd
|
3
|
+
class DynamoDB < SubCommand
|
4
|
+
|
5
|
+
no_commands do
|
6
|
+
def list_backups(name)
|
7
|
+
debug("Backups for #{name}")
|
8
|
+
print_table Aws::DynamoDB.list_backups(table_name: name).map { |b|
|
9
|
+
[b.backup_name, color(b.backup_status, COLORS), b.table_name, b.backup_creation_date_time, human_bytes(b.backup_size_bytes)]
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_backup(table_name, backup_name)
|
14
|
+
backup_name = Time.now.utc.strftime("#{table_name}-%Y%m%d%H%M%S") if backup_name == 'create' # thor option empty
|
15
|
+
debug("Creating backup #{backup_name} from #{table_name}")
|
16
|
+
Aws::DynamoDB.create_backup(table_name, backup_name).tap do |b|
|
17
|
+
puts YAML.dump(stringify_keys(b.to_hash))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'backup ID', 'table backups'
|
23
|
+
method_option :create, aliases: '-c', type: :string, default: nil, desc: 'create new backup from table'
|
24
|
+
def backup(id = nil)
|
25
|
+
name = my.resource(id)
|
26
|
+
if options[:create]
|
27
|
+
create_backup(name, options[:create])
|
28
|
+
else
|
29
|
+
list_backups(name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'restore ARN TABLE', 'restore backup to a new table'
|
34
|
+
def restore(arn, table)
|
35
|
+
debug("Creating table #{table} from backup #{arn}")
|
36
|
+
Aws::DynamoDB.restore_backup(table, arn)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Stax
|
2
|
+
module Cmd
|
3
|
+
class DynamoDB < SubCommand
|
4
|
+
|
5
|
+
no_commands do
|
6
|
+
def print_throughput(ids)
|
7
|
+
output = [['NAME', 'INDEX NAME', 'READ CAPACITY', 'WRITE CAPACITY', 'DECREASES TODAY']]
|
8
|
+
stack_table_names(ids).each do |name|
|
9
|
+
table = Aws::DynamoDB.table(name)
|
10
|
+
t = table.provisioned_throughput
|
11
|
+
output << [table.table_name, nil, t.read_capacity_units, t.write_capacity_units, t.number_of_decreases_today]
|
12
|
+
(table.global_secondary_indexes || []).each do |gsi|
|
13
|
+
t = gsi.provisioned_throughput
|
14
|
+
output << [table.table_name, gsi.index_name, t.read_capacity_units, t.write_capacity_units, t.number_of_decreases_today]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
print_table(output)
|
18
|
+
end
|
19
|
+
|
20
|
+
def update_throughput(ids, read, write)
|
21
|
+
debug("Updating throughput on #{ids ? ids.count : 'all'} tables")
|
22
|
+
stack_table_names(ids).each do |name|
|
23
|
+
puts name
|
24
|
+
table = Aws::DynamoDB.table(name)
|
25
|
+
begin
|
26
|
+
Aws::DynamoDB.client.update_table(
|
27
|
+
table_name: name,
|
28
|
+
provisioned_throughput: {
|
29
|
+
read_capacity_units: read || table.provisioned_throughput.read_capacity_units,
|
30
|
+
write_capacity_units: write || table.provisioned_throughput.write_capacity_units,
|
31
|
+
},
|
32
|
+
global_secondary_index_updates: table.global_secondary_indexes&.map do |gsi|
|
33
|
+
{
|
34
|
+
update: {
|
35
|
+
index_name: gsi.index_name,
|
36
|
+
provisioned_throughput: {
|
37
|
+
read_capacity_units: read || gsi.provisioned_throughput.read_capacity_units,
|
38
|
+
write_capacity_units: write || gsi.provisioned_throughput.write_capacity_units,
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
)
|
44
|
+
rescue ::Aws::DynamoDB::Errors::ValidationException
|
45
|
+
puts 'no change'
|
46
|
+
rescue ::Aws::DynamoDB::Errors::ResourceInUseException => e
|
47
|
+
warn(e.message)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'throughput ID', 'throughput'
|
54
|
+
method_option :tables, aliases: '-t', type: :array, default: nil, desc: 'limit to given table IDs'
|
55
|
+
method_option :read, aliases: '-r', type: :numeric, default: nil, desc: 'set read capacity units'
|
56
|
+
method_option :write, aliases: '-w', type: :numeric, default: nil, desc: 'set write capacity units'
|
57
|
+
def throughput
|
58
|
+
if options[:write] || options[:read]
|
59
|
+
update_throughput(options[:tables], options[:read], options[:write])
|
60
|
+
else
|
61
|
+
print_throughput(options[:tables])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/stax/mixin/ecs.rb
CHANGED
@@ -6,6 +6,42 @@ module Stax
|
|
6
6
|
thor.desc(:ecs, 'ECS subcommands')
|
7
7
|
thor.subcommand(:ecs, Cmd::Ecs)
|
8
8
|
end
|
9
|
+
|
10
|
+
def ecs_cluster_name
|
11
|
+
'default'
|
12
|
+
end
|
13
|
+
|
14
|
+
def ecs_services
|
15
|
+
@_ecs_services ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::ECS::Service')
|
16
|
+
end
|
17
|
+
|
18
|
+
def ecs_task_definitions
|
19
|
+
@_ecs_task_definitions ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::ECS::TaskDefinition')
|
20
|
+
end
|
21
|
+
|
22
|
+
def ecs_service_objects
|
23
|
+
Aws::Ecs.services(ecs_cluster_name, ecs_services.map(&:physical_resource_id))
|
24
|
+
end
|
25
|
+
|
26
|
+
## register a new revision of existing task definition
|
27
|
+
def ecs_update_taskdef(id)
|
28
|
+
taskdef = Aws::Ecs.task_definition(resource(id))
|
29
|
+
debug("Registering new revision of #{taskdef.family}")
|
30
|
+
args = %i[family cpu memory requires_compatibilities task_role_arn execution_role_arn network_mode container_definitions volumes placement_constraints]
|
31
|
+
Aws::Ecs.client.register_task_definition(taskdef.to_hash.slice(*args)).task_definition.tap do |t|
|
32
|
+
puts t.task_definition_arn
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
## update service to use a new task definition
|
37
|
+
def ecs_update_service(id, taskdef)
|
38
|
+
service_name = resource(id).split('/').last
|
39
|
+
taskdef_name = taskdef.task_definition_arn.split('/').last
|
40
|
+
debug("Updating #{service_name} to #{taskdef_name}")
|
41
|
+
Aws::Ecs.update_service(service: service_name, task_definition: taskdef_name).tap do |s|
|
42
|
+
puts s.task_definition
|
43
|
+
end
|
44
|
+
end
|
9
45
|
end
|
10
46
|
|
11
47
|
module Cmd
|
@@ -18,26 +54,14 @@ module Stax
|
|
18
54
|
}
|
19
55
|
|
20
56
|
no_commands do
|
21
|
-
def ecs_cluster_name
|
22
|
-
@_ecs_cluster_name ||= my.stack_name
|
23
|
-
end
|
24
|
-
|
25
|
-
def ecs_task_definitions
|
26
|
-
@_ecs_task_definitions ||= Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ECS::TaskDefinition' )
|
27
|
-
end
|
28
|
-
|
29
57
|
def ecs_task_definition(id)
|
30
58
|
Aws::Cfn.id(my.stack_name, id)
|
31
59
|
end
|
32
|
-
|
33
|
-
def ecs_services
|
34
|
-
@_ecs_services ||= Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ECS::Service')
|
35
|
-
end
|
36
60
|
end
|
37
61
|
|
38
62
|
desc 'clusters', 'ECS cluster for stack'
|
39
63
|
def clusters
|
40
|
-
print_table Aws::Ecs.clusters(ecs_cluster_name).map { |c|
|
64
|
+
print_table Aws::Ecs.clusters(my.ecs_cluster_name).map { |c|
|
41
65
|
[
|
42
66
|
c.cluster_name,
|
43
67
|
color(c.status, COLORS),
|
@@ -50,63 +74,119 @@ module Stax
|
|
50
74
|
|
51
75
|
desc 'services', 'ECS services for stack'
|
52
76
|
def services
|
53
|
-
print_table
|
77
|
+
print_table my.ecs_service_objects.map { |s|
|
54
78
|
[s.service_name, color(s.status, COLORS), s.task_definition.split('/').last, "#{s.running_count}/#{s.desired_count}"]
|
55
79
|
}
|
56
80
|
end
|
57
81
|
|
82
|
+
desc 'events', 'show service events'
|
83
|
+
method_option :number, aliases: '-n', type: :numeric, default: 10, desc: 'number of events to show'
|
84
|
+
def events
|
85
|
+
my.ecs_service_objects.each do |s|
|
86
|
+
debug("Events for #{s.service_name}")
|
87
|
+
print_table s.events.first(options[:number]).map { |e|
|
88
|
+
[set_color(e.created_at, :green), e.message]
|
89
|
+
}.reverse
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'deployments', 'show service deployments'
|
94
|
+
def deployments
|
95
|
+
my.ecs_service_objects.each do |s|
|
96
|
+
debug("Deployments for #{s.service_name}")
|
97
|
+
print_table s.deployments.map { |d|
|
98
|
+
count = "#{d.running_count}/#{d.desired_count} (#{d.pending_count})"
|
99
|
+
[d.id, d.status, count, d.created_at, d.updated_at, d.task_definition.split('/').last]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
58
104
|
desc 'definitions', 'ECS task definitions for stack'
|
59
105
|
def definitions
|
60
|
-
print_table ecs_task_definitions.map { |r|
|
106
|
+
print_table my.ecs_task_definitions.map { |r|
|
61
107
|
t = Aws::Ecs.task_definition(r.physical_resource_id)
|
62
108
|
[r.logical_resource_id, t.family, t.revision, color(t.status, COLORS)]
|
63
109
|
}
|
64
110
|
end
|
65
111
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
112
|
+
desc 'tasks', 'ECS tasks for stack'
|
113
|
+
method_option :status, aliases: '-s', type: :string, default: 'RUNNING', desc: 'status to list'
|
114
|
+
def tasks
|
115
|
+
print_table Aws::Ecs.tasks(my.ecs_cluster_name, options[:status].upcase).map { |t|
|
116
|
+
[
|
117
|
+
t.task_arn.split('/').last,
|
118
|
+
t.task_definition_arn.split('/').last,
|
119
|
+
t.container_instance_arn&.split('/')&.last || '--',
|
120
|
+
color(t.last_status, COLORS),
|
121
|
+
"(#{t.desired_status})",
|
122
|
+
t.started_by,
|
123
|
+
]
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
desc 'containers', 'containers for running tasks'
|
128
|
+
method_option :status, aliases: '-s', type: :string, default: 'RUNNING', desc: 'status to list'
|
129
|
+
def containers
|
130
|
+
debug("Containers for cluster #{my.ecs_cluster_name}")
|
131
|
+
print_table Aws::Ecs.tasks(my.ecs_cluster_name, options[:status].upcase).map { |task|
|
132
|
+
task_defn = task.task_definition_arn.split('/').last
|
133
|
+
task.containers.map { |c|
|
134
|
+
[
|
135
|
+
c.container_arn.split('/').last,
|
136
|
+
c.name,
|
137
|
+
color(c.last_status, COLORS),
|
138
|
+
c.network_interfaces.map(&:private_ipv_4_address).join(','),
|
139
|
+
task_defn,
|
140
|
+
c.exit_code,
|
141
|
+
c.reason,
|
142
|
+
]
|
143
|
+
}
|
144
|
+
}.flatten(1)
|
145
|
+
end
|
146
|
+
|
147
|
+
desc 'instances', 'ECS instances'
|
148
|
+
def instances
|
149
|
+
print_table Aws::Ecs.instances(my.ecs_cluster_name).map { |i|
|
150
|
+
[
|
151
|
+
i.container_instance_arn.split('/').last,
|
152
|
+
i.ec2_instance_id,
|
153
|
+
i.agent_connected,
|
154
|
+
color(i.status, COLORS),
|
155
|
+
i.running_tasks_count,
|
156
|
+
"(#{i.pending_tasks_count})",
|
157
|
+
"agent #{i.version_info.agent_version}",
|
158
|
+
i.version_info.docker_version,
|
159
|
+
]
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
desc 'run_task [ID]', 'run task by id'
|
164
|
+
def run_task(id)
|
165
|
+
Aws::Ecs.run(my.ecs_cluster_name, Aws::Cfn.id(my.stack_name, id)).tap do |tasks|
|
166
|
+
puts tasks.map(&:container_instance_arn)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
desc 'stop_task [TASK]', 'stop task'
|
171
|
+
def stop_task(task)
|
172
|
+
Aws::Ecs.stop(my.ecs_cluster_name, task).tap do |task|
|
173
|
+
puts task.container_instance_arn
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
desc 'scale', 'scale containers for service'
|
178
|
+
method_option :desired, aliases: '-d', type: :numeric, default: nil, desc: 'desired container count'
|
179
|
+
def scale
|
180
|
+
my.ecs_services.each do |s|
|
181
|
+
debug("Scaling service #{s.logical_resource_id}")
|
182
|
+
Aws::Ecs.update_service(
|
183
|
+
service: s.physical_resource_id,
|
184
|
+
desired_count: options[:desired],
|
185
|
+
).tap do |s|
|
186
|
+
puts "desired: #{s.desired_count}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
110
190
|
|
111
191
|
end
|
112
192
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'stax/aws/firehose'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module Firehose
|
5
|
+
def self.included(thor)
|
6
|
+
thor.desc(:firehose, 'Firehose subcommands')
|
7
|
+
thor.subcommand(:firehose, Cmd::Firehose)
|
8
|
+
|
9
|
+
def stack_firehoses
|
10
|
+
Aws::Cfn.resources_by_type(stack_name, 'AWS::KinesisFirehose::DeliveryStream')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Cmd
|
16
|
+
class Firehose < SubCommand
|
17
|
+
|
18
|
+
COLORS = {
|
19
|
+
ACTIVE: :green,
|
20
|
+
CREATING: :yellow,
|
21
|
+
DELETING: :red,
|
22
|
+
}
|
23
|
+
|
24
|
+
desc 'ls', 'list stack firehoses'
|
25
|
+
def ls
|
26
|
+
print_table my.stack_firehoses.map { |r|
|
27
|
+
f = Aws::Firehose.describe(r.physical_resource_id)
|
28
|
+
[f.delivery_stream_name, color(f.delivery_stream_status, COLORS), f.create_timestamp, f.delivery_stream_type]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/stax/mixin/lambda.rb
CHANGED
@@ -18,6 +18,21 @@ module Stax
|
|
18
18
|
def stack_lambdas
|
19
19
|
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::Lambda::Function')
|
20
20
|
end
|
21
|
+
|
22
|
+
## return zip file contents, make it if necessary
|
23
|
+
def zip_thing(thing)
|
24
|
+
if File.directory?(thing)
|
25
|
+
Dir.chdir(thing) do
|
26
|
+
%x[zip -q -r - .] # zip dir contents
|
27
|
+
end
|
28
|
+
elsif thing.match(/\.zip$/i)
|
29
|
+
File.read(thing) # raw zipfile contents
|
30
|
+
elsif File.file?(thing)
|
31
|
+
%x[zip -q -j - #{thing}] # zip a single file
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
21
36
|
end
|
22
37
|
|
23
38
|
desc 'ls', 'list lambdas for stack'
|
@@ -52,6 +67,18 @@ module Stax
|
|
52
67
|
end
|
53
68
|
end
|
54
69
|
|
70
|
+
desc 'update ID FILE', 'update code for lambda function with ID'
|
71
|
+
method_option :publish, aliases: '-p', type: :boolean, default: false, desc: 'publish as a new version'
|
72
|
+
def update(id, file)
|
73
|
+
Aws::Lambda.update_code(
|
74
|
+
function_name: my.resource(id),
|
75
|
+
publish: options[:publish],
|
76
|
+
zip_file: zip_thing(file),
|
77
|
+
)&.version.tap do |v|
|
78
|
+
puts "version: #{v}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
55
82
|
desc 'test ID', 'run lambda with ID'
|
56
83
|
method_option :type, type: :string, default: nil, desc: 'invocation type: RequestResponse, Event'
|
57
84
|
method_option :tail, type: :boolean, default: false, desc: 'tail log for RequestResponse'
|
data/lib/stax/mixin/logs.rb
CHANGED
@@ -89,6 +89,18 @@ module Stax
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
## lambdas create their own log groups, and when we delete stack they are left behind;
|
93
|
+
## this task looks up their names by stack prefix, and deletes them
|
94
|
+
desc 'cleanup', 'cleanup lambda log groups named for stack'
|
95
|
+
method_option :test, aliases: '-t', type: :boolean, default: false, desc: 'show group names without deleting'
|
96
|
+
def cleanup
|
97
|
+
debug("Cleaning up log groups for stack #{my.stack_name}")
|
98
|
+
Aws::Logs.groups("/aws/lambda/#{my.stack_name}").map(&:log_group_name).each do |name|
|
99
|
+
puts name
|
100
|
+
Aws::Logs.delete_group(name) unless options[:test]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
92
104
|
end
|
93
105
|
end
|
94
106
|
end
|
data/lib/stax/stack/crud.rb
CHANGED
@@ -75,6 +75,11 @@ module Stax
|
|
75
75
|
cfer_tail
|
76
76
|
end
|
77
77
|
|
78
|
+
desc 'generate', 'generate cloudformation template'
|
79
|
+
def generate
|
80
|
+
cfer_generate
|
81
|
+
end
|
82
|
+
|
78
83
|
desc 'protection', 'show/set termination protection for stack'
|
79
84
|
method_option :enable, aliases: '-e', type: :boolean, default: nil, desc: 'enable termination protection'
|
80
85
|
method_option :disable, aliases: '-d', type: :boolean, default: nil, desc: 'disable termination protection'
|
data/lib/stax/staxfile.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
module Stax
|
2
|
+
@@_stack_list = []
|
3
|
+
|
4
|
+
def self.stack_list
|
5
|
+
@@_stack_list
|
6
|
+
end
|
2
7
|
|
3
8
|
## search up the dir tree for nearest Staxfile
|
4
9
|
def self.load_staxfile
|
@@ -14,6 +19,7 @@ module Stax
|
|
14
19
|
|
15
20
|
## add a stack by name, creates class as needed
|
16
21
|
def self.add_stack(name, opt = {})
|
22
|
+
@@_stack_list << name
|
17
23
|
c = name.capitalize
|
18
24
|
|
19
25
|
## create the class if it does not exist yet
|
data/lib/stax/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stax
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Lister
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- lib/stax.rb
|
132
132
|
- lib/stax/asg.rb
|
133
133
|
- lib/stax/aws/alb.rb
|
134
|
+
- lib/stax/aws/apigw.rb
|
134
135
|
- lib/stax/aws/asg.rb
|
135
136
|
- lib/stax/aws/cfn.rb
|
136
137
|
- lib/stax/aws/dynamodb.rb
|
@@ -139,6 +140,7 @@ files:
|
|
139
140
|
- lib/stax/aws/ecs.rb
|
140
141
|
- lib/stax/aws/elb.rb
|
141
142
|
- lib/stax/aws/emr.rb
|
143
|
+
- lib/stax/aws/firehose.rb
|
142
144
|
- lib/stax/aws/iam.rb
|
143
145
|
- lib/stax/aws/keypair.rb
|
144
146
|
- lib/stax/aws/kms.rb
|
@@ -159,13 +161,18 @@ files:
|
|
159
161
|
- lib/stax/github.rb
|
160
162
|
- lib/stax/iam.rb
|
161
163
|
- lib/stax/keypair.rb
|
164
|
+
- lib/stax/meta.rb
|
162
165
|
- lib/stax/mixin/alb.rb
|
166
|
+
- lib/stax/mixin/apigw.rb
|
163
167
|
- lib/stax/mixin/asg.rb
|
164
168
|
- lib/stax/mixin/dynamodb.rb
|
169
|
+
- lib/stax/mixin/dynamodb/backup.rb
|
170
|
+
- lib/stax/mixin/dynamodb/throughput.rb
|
165
171
|
- lib/stax/mixin/ec2.rb
|
166
172
|
- lib/stax/mixin/ecs.rb
|
167
173
|
- lib/stax/mixin/elb.rb
|
168
174
|
- lib/stax/mixin/emr.rb
|
175
|
+
- lib/stax/mixin/firehose.rb
|
169
176
|
- lib/stax/mixin/keypair.rb
|
170
177
|
- lib/stax/mixin/kms.rb
|
171
178
|
- lib/stax/mixin/lambda.rb
|