stax 0.0.8 → 0.0.9
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/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.
|