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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee309ab8e6e330d1d4a048112e92ef4baa447a476d424aa603ef6ab8ba5d8373
4
- data.tar.gz: b961c4bd5be7fc88e23b07a36fd69ab9f3d308c490e32ddac51d4ff1aace36f0
3
+ metadata.gz: 1637c27b601383577e3b2f002e93de68a1d030d13a083fafaf0ae29d325d4cc3
4
+ data.tar.gz: 9d3d833071c1ef2c0ce92491e64aecbc39858dd44ffe225dc61d0e0adf487219
5
5
  SHA512:
6
- metadata.gz: e6190e8b3ebbac9249c4e4a506f940250dac24c9601453d7d956ef177ac71c62c885a7f784f971bdf611378719bd98c0b074f2470172c77963e9f7227941a2f0
7
- data.tar.gz: 1cf0747c5ed623d09612a3e19a55e6324afd7dc7bce69eb7f976464ec027cfa80d029baaa330dad80abe0d1ff8c2f4b900efed9b1b7034fe7b1ef6c9eb1206f0
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::cfer_template`.
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:
@@ -38,4 +38,5 @@ require 'stax/mixin/logs'
38
38
  require 'stax/mixin/apigw'
39
39
  require 'stax/mixin/firehose'
40
40
  require 'stax/mixin/codebuild'
41
- require 'stax/mixin/codepipeline'
41
+ require 'stax/mixin/codepipeline'
42
+ require 'stax/mixin/rds'
@@ -99,11 +99,13 @@ module Stax
99
99
 
100
100
  ## list of stacks that import from this one
101
101
  def imports(name)
102
- paginate(:imports) do |next_token|
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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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: [], desc: 'params to use previous value'
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: 'http://localhost:8000')
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
- def local
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
@@ -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
@@ -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 stack_s3_buckets.map(&:physical_resource_id)
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
@@ -16,7 +16,7 @@ module Stax
16
16
 
17
17
  ## IP address to ssh
18
18
  def ssh_instances
19
- Aws::Ec2.instances(stack_name).map(&:public_ip_address)
19
+ Aws::Ec2.instances(stack_name).map(&:public_ip_address).reject(&:nil?)
20
20
  end
21
21
 
22
22
  def ssh_options_format(opt)
@@ -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('No changes')
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
@@ -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 do |k,v|
60
+ @_cfn_parameters_create ||= cfn_parameters.map { |k,v|
50
61
  { parameter_key: k, parameter_value: v }
51
- end
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 do |k,v|
56
- if options[:use_previous_value].include?(k.to_s)
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
- end
73
+ }
62
74
  end
63
75
 
64
76
  ## location of templates relative to Staxfile
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Stax
2
- VERSION = '0.0.8'
2
+ VERSION = '0.0.9'
3
3
  end
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.8
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-06-09 00:00:00.000000000 Z
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.3
272
+ rubygems_version: 2.7.6
267
273
  signing_key:
268
274
  specification_version: 4
269
275
  summary: Control Cloudformation stack and other stuff.