stax 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/stax.rb +2 -1
- data/lib/stax/aws/cfn.rb +5 -3
- data/lib/stax/aws/dms.rb +39 -0
- data/lib/stax/aws/rds.rb +27 -0
- data/lib/stax/aws/s3.rb +4 -0
- data/lib/stax/aws/secrets_manager.rb +22 -0
- data/lib/stax/aws/sg.rb +31 -0
- data/lib/stax/cfer.rb +1 -1
- data/lib/stax/mixin/dms.rb +129 -0
- data/lib/stax/mixin/dynamodb/local.rb +34 -9
- data/lib/stax/mixin/ec2.rb +3 -0
- data/lib/stax/mixin/rds.rb +102 -0
- data/lib/stax/mixin/s3.rb +38 -1
- data/lib/stax/mixin/secrets_manager.rb +37 -0
- data/lib/stax/mixin/ssh.rb +1 -1
- data/lib/stax/stack/changeset.rb +6 -1
- data/lib/stax/stack/crud.rb +17 -5
- data/lib/stax/stack/imports.rb +2 -2
- data/lib/stax/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1637c27b601383577e3b2f002e93de68a1d030d13a083fafaf0ae29d325d4cc3
|
4
|
+
data.tar.gz: 9d3d833071c1ef2c0ce92491e64aecbc39858dd44ffe225dc61d0e0adf487219
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e560c98cafefb0228b31e0613372f722ed2b129e804a1010eaa6557e35de3ed070f93eba1e3e15b8fe8b76d78fbf5e2d9ca6b1492925caed3721198030af3c5a
|
7
|
+
data.tar.gz: c173d44f14e732458c6f677a24972b0eb88c8d8dc2f6189a397b598c8c46c3d09c0cd9bb68627bbab25c4f9888ef9c3587961a3c6cb1ee9bc9563c4d56a508d5
|
data/README.md
CHANGED
@@ -176,7 +176,7 @@ Commands:
|
|
176
176
|
## Cloudformation templates
|
177
177
|
|
178
178
|
Stax will load template files from the path relative to its `Staxfile`
|
179
|
-
as `cf/$stack.rb`, e.g. `cf/vpc.rb`. Modify this using the method `Stax::Stack::
|
179
|
+
as `cf/$stack.rb`, e.g. `cf/vpc.rb`. Modify this using the method `Stax::Stack::cfn_template_dir`.
|
180
180
|
See `examples` for a typical setup.
|
181
181
|
|
182
182
|
Simply control stacks using the relevant subcommands:
|
data/lib/stax.rb
CHANGED
data/lib/stax/aws/cfn.rb
CHANGED
@@ -99,11 +99,13 @@ module Stax
|
|
99
99
|
|
100
100
|
## list of stacks that import from this one
|
101
101
|
def imports(name)
|
102
|
-
|
103
|
-
client.list_imports(export_name: name, next_token: next_token)
|
104
|
-
end
|
102
|
+
client.list_imports(export_name: name).map(&:imports)
|
105
103
|
rescue ::Aws::CloudFormation::Errors::ValidationError
|
106
104
|
[]
|
105
|
+
rescue ::Aws::CloudFormation::Errors::Throttling => e # this call rate-limits aggressively
|
106
|
+
warn(e.message)
|
107
|
+
sleep 1
|
108
|
+
retry
|
107
109
|
end
|
108
110
|
|
109
111
|
def validate(opt)
|
data/lib/stax/aws/dms.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Dms < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::DatabaseMigrationService::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def endpoints(opt)
|
12
|
+
client.describe_endpoints(opt).map(&:endpoints).flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
def instances(opt)
|
16
|
+
client.describe_replication_instances(opt).map(&:replication_instances).flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
def tasks(opt)
|
20
|
+
client.describe_replication_tasks(opt).map(&:replication_tasks).flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
def test(opt)
|
24
|
+
client.test_connection(opt).connection
|
25
|
+
end
|
26
|
+
|
27
|
+
def connections(opt)
|
28
|
+
client.describe_connections(opt).map(&:connections).flatten
|
29
|
+
end
|
30
|
+
|
31
|
+
def start(opt)
|
32
|
+
client.start_replication_task(opt).replication_task
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/stax/aws/rds.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Rds < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::RDS::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def clusters(opt)
|
12
|
+
client.describe_db_clusters(opt)&.db_clusters
|
13
|
+
end
|
14
|
+
|
15
|
+
def instances(opt)
|
16
|
+
client.describe_db_instances(opt).map(&:db_instances).flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
def subnet_groups(opt)
|
20
|
+
client.describe_db_subnet_groups(opt).map(&:db_subnet_groups).flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/stax/aws/s3.rb
CHANGED
@@ -14,6 +14,10 @@ module Stax
|
|
14
14
|
|
15
15
|
def bucket_tags(bucket)
|
16
16
|
client.get_bucket_tagging(bucket: bucket).tag_set
|
17
|
+
rescue ::Aws::Errors::NoSuchEndpointError
|
18
|
+
warn("socket error for #{bucket}, retrying")
|
19
|
+
sleep 1
|
20
|
+
retry
|
17
21
|
rescue ::Aws::S3::Errors::NoSuchTagSet
|
18
22
|
[]
|
19
23
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class SecretsManager < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::SecretsManager::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def list
|
12
|
+
client.list_secrets.map(&:secret_list).flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(id, version = :AWSCURRENT)
|
16
|
+
client.get_secret_value(secret_id: id, version_stage: version)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/stax/aws/sg.rb
CHANGED
@@ -24,6 +24,22 @@ module Stax
|
|
24
24
|
warn(e.message)
|
25
25
|
end
|
26
26
|
|
27
|
+
def authorize_sg(id, sg, port)
|
28
|
+
client.authorize_security_group_ingress(
|
29
|
+
group_id: id,
|
30
|
+
ip_permissions: [
|
31
|
+
{
|
32
|
+
ip_protocol: :tcp,
|
33
|
+
from_port: port,
|
34
|
+
to_port: port,
|
35
|
+
user_id_group_pairs: [ { group_id: sg } ],
|
36
|
+
}
|
37
|
+
]
|
38
|
+
)
|
39
|
+
rescue ::Aws::EC2::Errors::InvalidPermissionDuplicate => e
|
40
|
+
warn(e.message)
|
41
|
+
end
|
42
|
+
|
27
43
|
def revoke(id, cidr, port = 22)
|
28
44
|
client.revoke_security_group_ingress(
|
29
45
|
group_id: id,
|
@@ -36,6 +52,21 @@ module Stax
|
|
36
52
|
warn(e.message)
|
37
53
|
end
|
38
54
|
|
55
|
+
def revoke_sg(id, sg, port)
|
56
|
+
client.revoke_security_group_ingress(
|
57
|
+
group_id: id,
|
58
|
+
ip_permissions: [
|
59
|
+
{
|
60
|
+
ip_protocol: :tcp,
|
61
|
+
from_port: port,
|
62
|
+
to_port: port,
|
63
|
+
user_id_group_pairs: [ { group_id: sg } ],
|
64
|
+
}
|
65
|
+
]
|
66
|
+
)
|
67
|
+
rescue ::Aws::EC2::Errors::InvalidPermissionNotFound => e
|
68
|
+
warn(e.message)
|
69
|
+
end
|
39
70
|
end
|
40
71
|
end
|
41
72
|
end
|
data/lib/stax/cfer.rb
CHANGED
@@ -33,7 +33,7 @@ end
|
|
33
33
|
|
34
34
|
module Stax
|
35
35
|
class Stack < Base
|
36
|
-
class_option :use_previous_value, aliases: '-u', type: :array, default:
|
36
|
+
class_option :use_previous_value, aliases: '-u', type: :array, default: nil, desc: 'params to use previous value'
|
37
37
|
|
38
38
|
no_commands do
|
39
39
|
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'stax/aws/dms'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module Dms
|
5
|
+
def self.included(thor)
|
6
|
+
thor.desc('dms COMMAND', 'DMS subcommands')
|
7
|
+
thor.subcommand(:dms, Cmd::Dms)
|
8
|
+
end
|
9
|
+
|
10
|
+
def stack_dms_endpoints
|
11
|
+
@_stack_dms_endpoints ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::DMS::Endpoint')
|
12
|
+
end
|
13
|
+
|
14
|
+
def stack_dms_replication_instances
|
15
|
+
@_stack_dms_replication_instances ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::DMS::ReplicationInstance')
|
16
|
+
end
|
17
|
+
|
18
|
+
def stack_dms_replication_tasks
|
19
|
+
@_stack_dms_replication_tasks ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::DMS::ReplicationTask')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Cmd
|
24
|
+
class Dms < SubCommand
|
25
|
+
stax_info :endpoints, :instances, :tasks
|
26
|
+
|
27
|
+
COLORS = {
|
28
|
+
active: :green,
|
29
|
+
available: :green,
|
30
|
+
successful: :green,
|
31
|
+
failed: :red,
|
32
|
+
stopped: :red,
|
33
|
+
ready: :green,
|
34
|
+
}
|
35
|
+
|
36
|
+
no_commands do
|
37
|
+
def dms_endpoint_arns
|
38
|
+
my.stack_dms_endpoints.map(&:physical_resource_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def dms_instance_arns
|
42
|
+
my.stack_dms_replication_instances.map(&:physical_resource_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def dms_task_arns
|
46
|
+
my.stack_dms_replication_tasks.map(&:physical_resource_id)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'endpoints', 'list endpoints'
|
51
|
+
def endpoints
|
52
|
+
debug("DMS endpoints for #{my.stack_name}")
|
53
|
+
print_table Aws::Dms.endpoints(filters: [{name: 'endpoint-arn', values: dms_endpoint_arns}]).map { |e|
|
54
|
+
[e.endpoint_identifier, e.endpoint_type, color(e.status, COLORS), e.engine_name, e.server_name]
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'instances', 'list replication instances'
|
59
|
+
def instances
|
60
|
+
debug("DMS replication instances for #{my.stack_name}")
|
61
|
+
print_table Aws::Dms.instances(filters: [{name: 'replication-instance-arn', values: dms_instance_arns}]).map { |i|
|
62
|
+
[
|
63
|
+
i.replication_instance_identifier, color(i.replication_instance_status, COLORS),
|
64
|
+
i.replication_subnet_group&.vpc_id, i.replication_instance_class, i.engine_version,
|
65
|
+
i.availability_zone, i.replication_instance_private_ip_address,
|
66
|
+
]
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'tasks', 'list replication tasks'
|
71
|
+
def tasks
|
72
|
+
debug("DMS replication tasks for #{my.stack_name}")
|
73
|
+
print_table Aws::Dms.tasks(filters: [{name: 'replication-task-arn', values: dms_task_arns}]).map { |t|
|
74
|
+
[
|
75
|
+
t.replication_task_identifier, color(t.status, COLORS), t.migration_type,
|
76
|
+
"#{t.replication_task_stats&.full_load_progress_percent}%", "#{(t.replication_task_stats&.elapsed_time_millis/1000).to_i}s",
|
77
|
+
"#{t.replication_task_stats&.tables_loaded} loaded", "#{t.replication_task_stats&.tables_errored} errors",
|
78
|
+
]
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
desc 'test', 'test endpoint connections'
|
83
|
+
def test
|
84
|
+
instance = dms_instance_arns.first # FIXME: handle multiple instances
|
85
|
+
dms_endpoint_arns.each do |endpoint|
|
86
|
+
debug("Testing connection for #{endpoint}")
|
87
|
+
conn = Aws::Dms.test(replication_instance_arn: instance, endpoint_arn: endpoint)
|
88
|
+
loop do
|
89
|
+
sleep 3
|
90
|
+
c = Aws::Dms.connections(
|
91
|
+
filters: [
|
92
|
+
{ name: 'endpoint-arn', values: [conn.endpoint_arn] },
|
93
|
+
{ name: 'replication-instance-arn', values: [conn.replication_instance_arn] },
|
94
|
+
]
|
95
|
+
).first
|
96
|
+
puts [c.endpoint_identifier, c.replication_instance_identifier, color(c.status, COLORS), c.last_failure_message].join(' ')
|
97
|
+
break unless c.status == 'testing'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'connections', 'list endpoint test connections'
|
103
|
+
def connections
|
104
|
+
debug("Test connection results for #{my.stack_name}")
|
105
|
+
print_table Aws::Dms.connections(filters: [{name: 'endpoint-arn', values: dms_endpoint_arns}]).map { |c|
|
106
|
+
[c.endpoint_identifier, c.replication_instance_identifier, color(c.status, COLORS), c.last_failure_message]
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
desc 'start', 'start replication task'
|
111
|
+
method_option :tasks, type: :array, default: nil, description: 'task arns to start'
|
112
|
+
method_option :resume, type: :boolean, default: false, description: 'resume processing'
|
113
|
+
method_option :reload, type: :boolean, default: false, description: 'reload target'
|
114
|
+
def start(*tasks)
|
115
|
+
type = (options[:resume] && 'resume-processing') || (options[:reload] && 'reload-target') || 'start-replication'
|
116
|
+
options.fetch(:tasks, dms_task_arns).each do |task|
|
117
|
+
Aws::Dms.start(replication_task_arn: task, start_replication_task_type: type).tap do |r|
|
118
|
+
puts [r.replication_task_identifier, r.status, r.replication_task_start_date].join(' ')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
rescue ::Aws::DatabaseMigrationService::Errors::InvalidParameterCombinationException => e
|
122
|
+
fail_task(e.message)
|
123
|
+
rescue ::Aws::DatabaseMigrationService::Errors::InvalidResourceStateFault => e
|
124
|
+
fail_task(e.message)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -1,14 +1,25 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
3
|
module Stax
|
4
|
+
module DynamoDB
|
5
|
+
|
6
|
+
## monkey-patch this method to apply any app-specific changes to payload
|
7
|
+
## args: logical_id, payload hash
|
8
|
+
## returns: new payload
|
9
|
+
def dynamo_local_payload_hacks(id, payload)
|
10
|
+
payload
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
4
15
|
module Cmd
|
5
16
|
class DynamoDB < SubCommand
|
6
17
|
|
7
18
|
no_commands do
|
8
19
|
|
9
|
-
## client for dynamodb-local endpoint
|
10
|
-
def client
|
11
|
-
@_client ||= ::Aws::DynamoDB::Client.new(endpoint:
|
20
|
+
## client for dynamodb-local endpoint on given port
|
21
|
+
def client(port)
|
22
|
+
@_client ||= ::Aws::DynamoDB::Client.new(endpoint: "http://localhost:#{port}")
|
12
23
|
end
|
13
24
|
|
14
25
|
## get CFN template and return hash of table configs
|
@@ -37,8 +48,8 @@ module Stax
|
|
37
48
|
end
|
38
49
|
|
39
50
|
## create table
|
40
|
-
def dynamo_local_create(payload)
|
41
|
-
client.create_table(dynamo_ruby_payload(payload))
|
51
|
+
def dynamo_local_create(payload, port)
|
52
|
+
client(port).create_table(dynamo_ruby_payload(payload))
|
42
53
|
rescue ::Aws::DynamoDB::Errors::ResourceInUseException => e
|
43
54
|
warn(e.message) # table exists
|
44
55
|
rescue Seahorse::Client::NetworkingError => e
|
@@ -46,24 +57,38 @@ module Stax
|
|
46
57
|
end
|
47
58
|
end
|
48
59
|
|
49
|
-
desc 'local', 'create local tables from template'
|
60
|
+
desc 'local-create', 'create local tables from template'
|
50
61
|
method_option :tables, aliases: '-t', type: :array, default: nil, desc: 'filter table ids'
|
51
62
|
method_option :payload, aliases: '-p', type: :boolean, default: false, desc: 'just output payload'
|
52
|
-
|
63
|
+
method_option :port, aliases: '-P', type: :numeric, default: 8000, desc: 'local dynamo port'
|
64
|
+
def local_create
|
53
65
|
tables = dynamo_local_tables
|
54
66
|
tables.slice!(*options[:tables]) if options[:tables]
|
55
67
|
|
56
68
|
tables.each do |id, value|
|
57
69
|
payload = dynamo_payload_from_template(id, value)
|
70
|
+
payload = my.dynamo_local_payload_hacks(id, payload) # apply user-supplied hacks
|
58
71
|
if options[:payload]
|
59
72
|
puts JSON.pretty_generate(payload)
|
60
73
|
else
|
61
74
|
puts "create table #{id}"
|
62
|
-
dynamo_local_create(payload)
|
75
|
+
dynamo_local_create(payload, options[:port])
|
63
76
|
end
|
64
77
|
end
|
65
78
|
end
|
66
79
|
|
80
|
+
desc 'local-delete', 'delete local tables from template'
|
81
|
+
method_option :tables, aliases: '-t', type: :array, default: nil, desc: 'filter table ids'
|
82
|
+
method_option :port, aliases: '-P', type: :numeric, default: 8000, desc: 'local dynamo port'
|
83
|
+
def local_delete
|
84
|
+
tables = dynamo_local_tables
|
85
|
+
tables.slice!(*options[:tables]) if options[:tables]
|
86
|
+
|
87
|
+
tables.each do |id,_value|
|
88
|
+
puts "deleting table #{id}"
|
89
|
+
client(options[:port]).delete_table(table_name: id)
|
90
|
+
end
|
91
|
+
end
|
67
92
|
end
|
68
93
|
end
|
69
|
-
end
|
94
|
+
end
|
data/lib/stax/mixin/ec2.rb
CHANGED
@@ -10,6 +10,8 @@ module Stax
|
|
10
10
|
|
11
11
|
module Cmd
|
12
12
|
class Ec2 < SubCommand
|
13
|
+
stax_info :ls
|
14
|
+
|
13
15
|
COLORS = {
|
14
16
|
## instances
|
15
17
|
running: :green,
|
@@ -23,6 +25,7 @@ module Stax
|
|
23
25
|
|
24
26
|
desc 'ls', 'list instances for stack'
|
25
27
|
def ls
|
28
|
+
debug("EC2 instances for #{my.stack_name}")
|
26
29
|
print_table Aws::Ec2.instances(my.stack_name).map { |i|
|
27
30
|
name = i.tags.find { |t| t.key == 'Name' }&.value
|
28
31
|
[
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'stax/aws/rds'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module Rds
|
5
|
+
def self.included(thor)
|
6
|
+
thor.desc('rds COMMAND', 'RDS subcommands')
|
7
|
+
thor.subcommand(:rds, Cmd::Rds)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Cmd
|
12
|
+
class Rds < SubCommand
|
13
|
+
stax_info :clusters, :instances, :endpoints
|
14
|
+
|
15
|
+
COLORS = {
|
16
|
+
available: :green,
|
17
|
+
Complete: :green,
|
18
|
+
Active: :green,
|
19
|
+
}
|
20
|
+
|
21
|
+
no_commands do
|
22
|
+
def stack_db_clusters
|
23
|
+
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::RDS::DBCluster')
|
24
|
+
end
|
25
|
+
|
26
|
+
def stack_db_instances
|
27
|
+
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::RDS::DBInstance')
|
28
|
+
end
|
29
|
+
|
30
|
+
def stack_rds_clusters
|
31
|
+
filter = { name: 'db-cluster-id', values: stack_db_clusters.map(&:physical_resource_id) }
|
32
|
+
Aws::Rds.clusters(filters: [filter])
|
33
|
+
end
|
34
|
+
|
35
|
+
def stack_rds_instances
|
36
|
+
filter = { name: 'db-instance-id', values: stack_db_instances.map(&:physical_resource_id) }
|
37
|
+
Aws::Rds.instances(filters: [filter])
|
38
|
+
end
|
39
|
+
|
40
|
+
def stack_db_subnet_groups
|
41
|
+
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::RDS::DBSubnetGroup')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'clusters', 'list db clusters for stack'
|
46
|
+
def clusters
|
47
|
+
debug("RDS DB clusters for #{my.stack_name}")
|
48
|
+
print_table stack_rds_clusters.map { |c|
|
49
|
+
[c.db_cluster_identifier, c.engine, c.engine_version, color(c.status, COLORS), c.cluster_create_time]
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'members', 'list db cluster members for stack'
|
54
|
+
def members
|
55
|
+
stack_rds_clusters.each do |c|
|
56
|
+
debug("RDS DB members for cluster #{c.db_cluster_identifier}")
|
57
|
+
print_table c.db_cluster_members.map { |m|
|
58
|
+
role = m.is_cluster_writer ? 'writer' : 'reader'
|
59
|
+
[m.db_instance_identifier, role, m.db_cluster_parameter_group_status]
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'instances', 'list db instances for stack'
|
65
|
+
def instances
|
66
|
+
debug("RDS DB instances for #{my.stack_name}")
|
67
|
+
print_table stack_rds_instances.map { |i|
|
68
|
+
[i.db_instance_identifier, i.engine, i.engine_version, color(i.db_instance_status, COLORS), i.db_instance_class, i.db_subnet_group&.vpc_id, i.availability_zone]
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'endpoints', 'list db instance endpoints'
|
73
|
+
def endpoints
|
74
|
+
stack_rds_clusters.each do |c|
|
75
|
+
debug("RDS DB endpoints for cluster #{c.db_cluster_identifier}")
|
76
|
+
print_table [
|
77
|
+
['writer', c.endpoint, c.port, c.hosted_zone_id],
|
78
|
+
['reader', c.reader_endpoint, c.port, c.hosted_zone_id],
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
debug("RDS DB instance endpoints for #{my.stack_name}")
|
83
|
+
print_table stack_rds_instances.map { |i|
|
84
|
+
[i.db_instance_identifier, i.endpoint&.address, i.endpoint&.port, i.endpoint&.hosted_zone_id]
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
desc 'subnets', 'list db subnet groups'
|
89
|
+
def subnets
|
90
|
+
stack_db_subnet_groups.map do |r|
|
91
|
+
Aws::Rds.subnet_groups(db_subnet_group_name: r.physical_resource_id)
|
92
|
+
end.flatten.each do |g|
|
93
|
+
debug("Subnets for group #{g.db_subnet_group_name}")
|
94
|
+
print_table g.subnets.map { |s|
|
95
|
+
[s&.subnet_availability_zone&.name, s&.subnet_identifier, color(s&.subnet_status, COLORS)]
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/stax/mixin/s3.rb
CHANGED
@@ -15,6 +15,10 @@ module Stax
|
|
15
15
|
Aws::Cfn.resources_by_type(my.stack_name, 'AWS::S3::Bucket')
|
16
16
|
end
|
17
17
|
|
18
|
+
def stack_s3_bucket_names
|
19
|
+
stack_s3_buckets.map(&:physical_resource_id).compact
|
20
|
+
end
|
21
|
+
|
18
22
|
def stack_tagged_buckets
|
19
23
|
Aws::S3.list_buckets.select do |bucket|
|
20
24
|
region = Aws::S3.bucket_region(bucket.name)
|
@@ -27,16 +31,27 @@ module Stax
|
|
27
31
|
|
28
32
|
desc 'buckets', 'S3 buckets for this stack'
|
29
33
|
def buckets
|
30
|
-
puts
|
34
|
+
puts stack_s3_bucket_names
|
31
35
|
end
|
32
36
|
|
33
37
|
desc 'tagged', 'S3 buckets that were tagged by this stack'
|
34
38
|
def tagged
|
39
|
+
debug("Buckets tagged by stack #{my.stack_name}")
|
35
40
|
print_table stack_tagged_buckets.map { |b|
|
36
41
|
[b.name, b.creation_date]
|
37
42
|
}
|
38
43
|
end
|
39
44
|
|
45
|
+
desc 'reap', 'delete buckets tagged by this stack'
|
46
|
+
def reap
|
47
|
+
debug("Deleting buckets tagged by #{my.stack_name}")
|
48
|
+
stack_tagged_buckets.each do |b|
|
49
|
+
if yes?("Delete bucket and contents #{b.name}?", :yellow)
|
50
|
+
::Aws::S3::Bucket.new(b.name).delete!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
40
55
|
desc 'lifecycle', 'show/set lifecycle for tagged buckets'
|
41
56
|
def lifecycle
|
42
57
|
debug("Lifecycle for buckets tagged by #{my.stack_name}")
|
@@ -71,6 +86,28 @@ module Stax
|
|
71
86
|
end
|
72
87
|
end
|
73
88
|
|
89
|
+
desc 'clear', 'clear objects from buckets'
|
90
|
+
method_option :names, aliases: '-n', type: :array, default: nil, desc: 'names of buckets to clear'
|
91
|
+
def clear
|
92
|
+
debug("Clearing buckets for #{my.stack_name}")
|
93
|
+
(options[:names] || stack_s3_bucket_names).each do |b|
|
94
|
+
if yes?("Clear contents of bucket #{b}?", :yellow)
|
95
|
+
::Aws::S3::Bucket.new(b).clear!
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'delete', 'delete buckets and objects'
|
101
|
+
method_option :names, aliases: '-n', type: :array, default: nil, desc: 'names of buckets to delete'
|
102
|
+
def delete
|
103
|
+
debug("Deleting buckets for #{my.stack_name}")
|
104
|
+
(options[:names] || stack_s3_bucket_names).each do |b|
|
105
|
+
if yes?("Delete bucket and contents #{b}?", :yellow)
|
106
|
+
::Aws::S3::Bucket.new(b).delete!
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
74
111
|
end
|
75
112
|
end
|
76
113
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'stax/aws/secrets_manager'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module SecretsManager
|
5
|
+
def self.included(thor)
|
6
|
+
thor.desc('sm COMMAND', 'SecretsManager subcommands')
|
7
|
+
thor.subcommand(:sm, Cmd::SecretsManager)
|
8
|
+
end
|
9
|
+
|
10
|
+
## monkey-patch in your application as needed
|
11
|
+
def secrets_manager_prefix
|
12
|
+
@_secrets_manager_prefix ||= "#{app_name}/#{branch_name}/"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Cmd
|
17
|
+
class SecretsManager < SubCommand
|
18
|
+
|
19
|
+
desc 'ls', 'list secrets'
|
20
|
+
def ls
|
21
|
+
debug("Secrets for #{my.stack_name}")
|
22
|
+
print_table Aws::SecretsManager.list.select { |s|
|
23
|
+
s.name.start_with?(my.secrets_manager_prefix)
|
24
|
+
}.map { |s|
|
25
|
+
[s.name, s.description, s.last_accessed_date]
|
26
|
+
}.sort
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'get ID', 'get secret'
|
30
|
+
def get(id)
|
31
|
+
id = my.secrets_manager_prefix + id unless id.include?('/') # allow absolute or relative path
|
32
|
+
puts Aws::SecretsManager.get(id).secret_string
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/stax/mixin/ssh.rb
CHANGED
data/lib/stax/stack/changeset.rb
CHANGED
@@ -57,6 +57,11 @@ module Stax
|
|
57
57
|
}
|
58
58
|
end
|
59
59
|
|
60
|
+
## get status reason, used for a failure message
|
61
|
+
def change_set_reason(id)
|
62
|
+
Aws::Cfn.client.describe_change_set(stack_name: stack_name, change_set_name: id).status_reason
|
63
|
+
end
|
64
|
+
|
60
65
|
## confirm and execute the change set
|
61
66
|
def change_set_execute(id)
|
62
67
|
if yes?("Apply these changes to stack #{stack_name}?", :yellow)
|
@@ -76,7 +81,7 @@ module Stax
|
|
76
81
|
desc 'change', 'create and execute a changeset'
|
77
82
|
def change
|
78
83
|
id = change_set_update
|
79
|
-
change_set_complete?(id) || fail_task(
|
84
|
+
change_set_complete?(id) || fail_task(change_set_reason(id))
|
80
85
|
change_set_changes(id)
|
81
86
|
change_set_unlock
|
82
87
|
change_set_execute(id) && tail && update_warn_imports
|
data/lib/stax/stack/crud.rb
CHANGED
@@ -45,20 +45,32 @@ module Stax
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
## stack should monkey-patch with list of params to keep on update
|
49
|
+
def use_previous_value
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
|
53
|
+
## return option or method
|
54
|
+
def _use_previous_value
|
55
|
+
@_use_previous_value ||= (options[:use_previous_value] || use_previous_value.map(&:to_s))
|
56
|
+
end
|
57
|
+
|
58
|
+
## get array of params for stack create
|
48
59
|
def cfn_parameters_create
|
49
|
-
cfn_parameters.map
|
60
|
+
@_cfn_parameters_create ||= cfn_parameters.map { |k,v|
|
50
61
|
{ parameter_key: k, parameter_value: v }
|
51
|
-
|
62
|
+
}
|
52
63
|
end
|
53
64
|
|
65
|
+
## get array of params for stack update, use previous where requested
|
54
66
|
def cfn_parameters_update
|
55
|
-
cfn_parameters.map
|
56
|
-
if
|
67
|
+
@_cfn_parameters_update ||= cfn_parameters.map { |k,v|
|
68
|
+
if _use_previous_value.include?(k.to_s)
|
57
69
|
{ parameter_key: k, use_previous_value: true }
|
58
70
|
else
|
59
71
|
{ parameter_key: k, parameter_value: v }
|
60
72
|
end
|
61
|
-
|
73
|
+
}
|
62
74
|
end
|
63
75
|
|
64
76
|
## location of templates relative to Staxfile
|
data/lib/stax/stack/imports.rb
CHANGED
@@ -25,9 +25,9 @@ module Stax
|
|
25
25
|
def imports
|
26
26
|
debug("Stacks that import from #{stack_name}")
|
27
27
|
print_table Aws::Cfn.exports(stack_name).map { |e|
|
28
|
-
imports = (i = Aws::Cfn.imports(e.export_name)).empty? ? '-' : i.join('
|
28
|
+
imports = (i = Aws::Cfn.imports(e.export_name)).empty? ? '-' : i.join(' ')
|
29
29
|
[e.output_key, imports]
|
30
|
-
}
|
30
|
+
}.sort
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Lister
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- lib/stax/aws/cfn.rb
|
164
164
|
- lib/stax/aws/codebuild.rb
|
165
165
|
- lib/stax/aws/codepipeline.rb
|
166
|
+
- lib/stax/aws/dms.rb
|
166
167
|
- lib/stax/aws/dynamodb.rb
|
167
168
|
- lib/stax/aws/ec2.rb
|
168
169
|
- lib/stax/aws/ecr.rb
|
@@ -175,9 +176,11 @@ files:
|
|
175
176
|
- lib/stax/aws/kms.rb
|
176
177
|
- lib/stax/aws/lambda.rb
|
177
178
|
- lib/stax/aws/logs.rb
|
179
|
+
- lib/stax/aws/rds.rb
|
178
180
|
- lib/stax/aws/route53.rb
|
179
181
|
- lib/stax/aws/s3.rb
|
180
182
|
- lib/stax/aws/sdk.rb
|
183
|
+
- lib/stax/aws/secrets_manager.rb
|
181
184
|
- lib/stax/aws/sg.rb
|
182
185
|
- lib/stax/aws/sqs.rb
|
183
186
|
- lib/stax/aws/ssm.rb
|
@@ -211,6 +214,7 @@ files:
|
|
211
214
|
- lib/stax/mixin/asg.rb
|
212
215
|
- lib/stax/mixin/codebuild.rb
|
213
216
|
- lib/stax/mixin/codepipeline.rb
|
217
|
+
- lib/stax/mixin/dms.rb
|
214
218
|
- lib/stax/mixin/dynamodb.rb
|
215
219
|
- lib/stax/mixin/dynamodb/backup.rb
|
216
220
|
- lib/stax/mixin/dynamodb/local.rb
|
@@ -226,7 +230,9 @@ files:
|
|
226
230
|
- lib/stax/mixin/kms.rb
|
227
231
|
- lib/stax/mixin/lambda.rb
|
228
232
|
- lib/stax/mixin/logs.rb
|
233
|
+
- lib/stax/mixin/rds.rb
|
229
234
|
- lib/stax/mixin/s3.rb
|
235
|
+
- lib/stax/mixin/secrets_manager.rb
|
230
236
|
- lib/stax/mixin/sg.rb
|
231
237
|
- lib/stax/mixin/sqs.rb
|
232
238
|
- lib/stax/mixin/ssh.rb
|
@@ -263,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
269
|
version: '0'
|
264
270
|
requirements: []
|
265
271
|
rubyforge_project:
|
266
|
-
rubygems_version: 2.7.
|
272
|
+
rubygems_version: 2.7.6
|
267
273
|
signing_key:
|
268
274
|
specification_version: 4
|
269
275
|
summary: Control Cloudformation stack and other stuff.
|