stax 0.1.7 → 0.1.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2aca89588152ddee959060e8fc3c1dc1bef1c2abb3a57839ab0a35de9aa208
4
- data.tar.gz: b9bcf76bba2aa8a472c07291596142b6d4338a566abd42636fb9cf8b05b6b7be
3
+ metadata.gz: e17c465590b04d1db946eb5cf12e660fb01a63c348aefcfadca2b5d98e568e52
4
+ data.tar.gz: d585924ea4d53397cc817cda8a617f7a14c430d84befa9f485ee5440a64653af
5
5
  SHA512:
6
- metadata.gz: b4f6c035f4c10239132e339ef964be95ac86069a47b6bf0f7f1cf7f25a5b1661d3b0d57a518cde8d4ef018d8dd333cc5bfc6b6db6f71e64846b77839bf037958
7
- data.tar.gz: 651280004b99781f0d209b9c91179560b75b238dd0faa1431a6f418f94dc1283e9c06e0a6bb9c9636b847898812f9fa8648f052f47aefbe8230d3cc2c3a33074
6
+ metadata.gz: 4d71a59e526634d2c27d768db9e979569482e53bfeb43ae3907326dd851ba983c3498d5b04ec11727461abcdee5e1dfcf7f634a0c1b6338901718b9092652fb0
7
+ data.tar.gz: eb8469a0fd373332b3d51171324e9ec9857c77e8a75cdf50c1f6414304af84bcfde1628087ea1866acf6a63b29f4ca5fa9cc13364ec7d8de59e2d330cd20555a
data/bin/stax CHANGED
@@ -5,6 +5,8 @@ require 'stax'
5
5
  Stax.load_staxfile
6
6
  begin
7
7
  Stax::Cli.start(ARGV)
8
- rescue Aws::CloudFormation::Errors::ExpiredToken => e
8
+ rescue Aws::CloudFormation::Errors::ExpiredToken,
9
+ Aws::CloudFormation::Errors::ValidationError,
10
+ Aws::Errors::MissingCredentialsError => e
9
11
  abort(e.message)
10
12
  end
data/lib/stax/aws/cfn.rb CHANGED
@@ -7,6 +7,7 @@ module Stax
7
7
  CREATE_IN_PROGRESS CREATE_FAILED CREATE_COMPLETE
8
8
  ROLLBACK_IN_PROGRESS ROLLBACK_FAILED ROLLBACK_COMPLETE
9
9
  DELETE_IN_PROGRESS DELETE_FAILED
10
+ IMPORT_IN_PROGRESS IMPORT_COMPLETE IMPORT_ROLLBACK_IN_PROGRESS IMPORT_ROLLBACK_FAILED IMPORT_ROLLBACK_COMPLETE
10
11
  UPDATE_IN_PROGRESS UPDATE_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_COMPLETE
11
12
  UPDATE_ROLLBACK_IN_PROGRESS UPDATE_ROLLBACK_FAILED UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_ROLLBACK_COMPLETE
12
13
  REVIEW_IN_PROGRESS
@@ -17,6 +18,7 @@ module Stax
17
18
  CREATE_COMPLETE: :green,
18
19
  DELETE_COMPLETE: :green,
19
20
  UPDATE_COMPLETE: :green,
21
+ IMPORT_COMPLETE: :green,
20
22
  CREATE_FAILED: :red,
21
23
  DELETE_FAILED: :red,
22
24
  UPDATE_FAILED: :red,
@@ -24,6 +26,7 @@ module Stax
24
26
  ROLLBACK_COMPLETE: :red,
25
27
  ## resource action
26
28
  Add: :green,
29
+ Import: :green,
27
30
  Modify: :yellow,
28
31
  Remove: :red,
29
32
  }
@@ -170,4 +173,4 @@ module Stax
170
173
 
171
174
  end
172
175
  end
173
- end
176
+ end
data/lib/stax/base.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'thor'
2
2
  require 'stax/aws/sts'
3
3
 
4
+ ## clean exit on ctrl-c for all methods
5
+ trap('SIGINT', 'EXIT')
6
+
4
7
  module Stax
5
8
  class Base < Thor
6
9
 
@@ -68,7 +71,9 @@ module Stax
68
71
  end
69
72
  end
70
73
 
71
- def color(string, hash)
74
+ ## color a string according to current class COLORS hash
75
+ def color(string, hash = nil)
76
+ hash ||= self.class::COLORS
72
77
  set_color(string, hash.fetch(string.to_sym, :yellow))
73
78
  end
74
79
 
@@ -118,6 +123,7 @@ module Stax
118
123
 
119
124
  ## convert a diff in seconds to d h m s
120
125
  def human_time_diff(t, n = 5)
126
+ t = t.round # handle fractional seconds
121
127
  mm, ss = t.divmod(60)
122
128
  hh, mm = mm.divmod(60)
123
129
  dd, hh = hh.divmod(24)
data/lib/stax/cli/ls.rb CHANGED
@@ -2,37 +2,43 @@ module Stax
2
2
  class Cli < Base
3
3
 
4
4
  no_commands do
5
+ ## fields to show in output
6
+ def ls_stack_fields(s)
7
+ [ s.stack_name, s.creation_time, color(s.stack_status, Aws::Cfn::COLORS) ]
8
+ end
9
+
10
+ ## list stacks from Staxfile in given order
5
11
  def ls_staxfile_stacks
6
- stacks = Aws::Cfn.stacks.each_with_object({}) { |s, h| h[s.stack_name] = s }
7
12
  print_table Stax.stack_list.map { |id|
8
13
  name = stack(id).stack_name
9
- if (s = stacks[name])
10
- [s.stack_name, s.creation_time, color(s.stack_status, Aws::Cfn::COLORS), s.template_description]
14
+ if (s = Aws::Cfn.describe(name))
15
+ ls_stack_fields(s)
11
16
  else
12
- options[:existing] ? nil : [name, '-']
17
+ [ name, '-' ]
13
18
  end
14
- }.compact
19
+ }
15
20
  end
16
21
 
22
+ ## list all extant stacks we think match our prefix
17
23
  def ls_stacks_with_prefix(prefix)
18
24
  print_table Aws::Cfn.stacks.select { |s|
19
25
  s.stack_name.start_with?(prefix || stack_prefix)
20
26
  }.map { |s|
21
- [s.stack_name, s.creation_time, color(s.stack_status, Aws::Cfn::COLORS), s.template_description]
27
+ ls_stack_fields(s)
22
28
  }.sort
23
29
  end
24
30
 
31
+ ## list all stacks in account
25
32
  def ls_account_stacks
26
33
  print_table Aws::Cfn.stacks.map { |s|
27
- [s.stack_name, s.creation_time, color(s.stack_status, Aws::Cfn::COLORS), s.template_description]
34
+ ls_stack_fields(s)
28
35
  }.sort
29
36
  end
30
37
  end
31
38
 
32
39
  desc 'ls [PREFIX]', 'list stacks'
33
- method_option :existing, aliases: '-e', type: :boolean, default: false, desc: 'list just existing stacks'
34
- method_option :all, aliases: '-a', type: :boolean, default: false, desc: 'list all running stacks with our prefix'
35
- method_option :account, aliases: '-A', type: :boolean, default: false, desc: 'list all running stacks in account'
40
+ method_option :all, aliases: '-a', type: :boolean, default: false, desc: 'list all running stacks with our prefix'
41
+ method_option :account, aliases: '-A', type: :boolean, default: false, desc: 'list all running stacks in account'
36
42
  def ls(prefix = nil)
37
43
  if options[:account]
38
44
  ls_account_stacks
@@ -44,4 +50,4 @@ module Stax
44
50
  end
45
51
 
46
52
  end
47
- end
53
+ end
data/lib/stax/cli.rb CHANGED
@@ -9,6 +9,7 @@ module Stax
9
9
  class Cli < Base
10
10
  class_option :branch, type: :string, default: Git.branch, desc: 'git branch to use'
11
11
  class_option :app, type: :string, default: File.basename(Git.toplevel), desc: 'application name'
12
+
12
13
  ## silence deprecation warning
13
14
  ## https://github.com/erikhuda/thor/blob/fb625b223465692a9d8a88cc2a483e126f1a8978/CHANGELOG.md#100
14
15
  def self.exit_on_failure?
@@ -14,6 +14,7 @@ module Stax
14
14
 
15
15
  COLORS = {
16
16
  available: :green,
17
+ 'in-sync': :green,
17
18
  Complete: :green,
18
19
  Active: :green,
19
20
  }
@@ -40,13 +41,36 @@ module Stax
40
41
  def stack_db_subnet_groups
41
42
  Aws::Cfn.resources_by_type(my.stack_name, 'AWS::RDS::DBSubnetGroup')
42
43
  end
44
+
45
+ def print_rds_events(opt)
46
+ Aws::Rds.client.describe_events(opt).map(&:events).flatten.map do |e|
47
+ [ e.date, e.message ]
48
+ end.tap(&method(:print_table))
49
+ end
50
+
51
+ end
52
+
53
+ desc 'ls', 'list clusters with members'
54
+ def ls
55
+ debug("RDS databases for #{my.stack_name}")
56
+ stack_rds_clusters.map do |c|
57
+ cluster = [ c.db_cluster_identifier, 'cluster', color(c.status), c.engine ]
58
+ instances = c.db_cluster_members.map do |m|
59
+ role = m.is_cluster_writer ? 'writer' : 'reader'
60
+ i = Aws::Rds.instances(filters: [ { name: 'db-instance-id', values: [ m.db_instance_identifier ] } ]).first
61
+ [ '- ' + i.db_instance_identifier, role, color(i.db_instance_status), i.engine, i.availability_zone, i.db_instance_class ]
62
+ end
63
+ [ cluster ] + instances
64
+ end.flatten(1).tap do |list|
65
+ print_table list
66
+ end
43
67
  end
44
68
 
45
69
  desc 'clusters', 'list db clusters for stack'
46
70
  def clusters
47
71
  debug("RDS DB clusters for #{my.stack_name}")
48
72
  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]
73
+ [c.db_cluster_identifier, c.engine, c.engine_version, color(c.status), c.cluster_create_time]
50
74
  }
51
75
  end
52
76
 
@@ -65,7 +89,7 @@ module Stax
65
89
  def instances
66
90
  debug("RDS DB instances for #{my.stack_name}")
67
91
  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]
92
+ [i.db_instance_identifier, i.engine, i.engine_version, color(i.db_instance_status), i.db_instance_class, i.db_subnet_group&.vpc_id, i.availability_zone]
69
93
  }
70
94
  end
71
95
 
@@ -92,11 +116,67 @@ module Stax
92
116
  end.flatten.each do |g|
93
117
  debug("Subnets for group #{g.db_subnet_group_name}")
94
118
  print_table g.subnets.map { |s|
95
- [s&.subnet_availability_zone&.name, s&.subnet_identifier, color(s&.subnet_status, COLORS)]
119
+ [s&.subnet_availability_zone&.name, s&.subnet_identifier, color(s&.subnet_status)]
96
120
  }
97
121
  end
98
122
  end
99
123
 
124
+ desc 'failover', 'failover clusters'
125
+ method_option :target, type: :string, default: nil, desc: 'id of instance to promote'
126
+ def failover
127
+ stack_rds_clusters.each do |c|
128
+ if yes?("Failover #{c.db_cluster_identifier}?", :yellow)
129
+ resp = Aws::Rds.client.failover_db_cluster(db_cluster_identifier: c.db_cluster_identifier, target_db_instance_identifier: options[:target])
130
+ puts "failing over #{resp.db_cluster.db_cluster_identifier}"
131
+ end
132
+ end
133
+ end
134
+
135
+ desc 'snapshots', 'list snapshots for stack clusters'
136
+ def snapshots
137
+ stack_db_clusters.map(&:physical_resource_id).each do |id|
138
+ debug("Snapshots for cluster #{id}")
139
+ Aws::Rds.client.describe_db_cluster_snapshots(db_cluster_identifier: id).map(&:db_cluster_snapshots).flatten.map do |s|
140
+ [ s.db_cluster_snapshot_identifier, s.snapshot_create_time, "#{s.allocated_storage}G", color(s.status), s.snapshot_type ]
141
+ end.tap do |list|
142
+ print_table list
143
+ end
144
+ end
145
+ end
146
+
147
+ desc 'events', 'list rds events for this stack'
148
+ option :duration, aliases: '-d', type: :numeric, default: 60*24, desc: 'duration in mins to show'
149
+ def events
150
+ stack_db_clusters.map(&:physical_resource_id).each do |id|
151
+ debug("Events for cluster #{id}")
152
+ print_rds_events(duration: options[:duration], source_type: 'db-cluster', source_identifier: id)
153
+ end
154
+
155
+ stack_db_instances.map(&:physical_resource_id).each do |id|
156
+ debug("Events for instance #{id}")
157
+ print_rds_events(duration: options[:duration], source_type: 'db-instance', source_identifier: id)
158
+ end
159
+ end
160
+
161
+ desc 'write-forwarding', 'control write-forwarding'
162
+ method_option :disable, aliases: '-d', type: :boolean, desc: 'disable write-forwarding'
163
+ method_option :enable, aliases: '-e', type: :boolean, desc: 'enable write-forwarding'
164
+ def write_forwarding
165
+ stack_db_clusters.map(&:physical_resource_id).each do |cluster|
166
+ if options[:enable]
167
+ puts "#{cluster} enabling write-forwarding"
168
+ Aws::Rds.client.modify_db_cluster(db_cluster_identifier: cluster, enable_global_write_forwarding: true)
169
+ elsif options[:disable]
170
+ puts "#{cluster} disabling write-forwarding"
171
+ Aws::Rds.client.modify_db_cluster(db_cluster_identifier: cluster, enable_global_write_forwarding: false)
172
+ else
173
+ print_table Aws::Rds.client.describe_db_clusters(db_cluster_identifier: cluster).db_clusters.map { |c|
174
+ [ c.db_cluster_identifier, c.global_write_forwarding_status || '-' ]
175
+ }
176
+ end
177
+ end
178
+ end
179
+
100
180
  end
101
181
  end
102
- end
182
+ end
@@ -30,6 +30,23 @@ module Stax
30
30
  fail_task(e.message)
31
31
  end
32
32
 
33
+ ## create change set to import a resource
34
+ def change_set_import(resource)
35
+ Aws::Cfn.changeset(
36
+ change_set_type: :IMPORT,
37
+ resources_to_import: [ resource ],
38
+ stack_name: stack_name,
39
+ template_body: cfn_template_body,
40
+ template_url: cfn_template_url,
41
+ parameters: cfn_parameters_update,
42
+ capabilities: cfn_capabilities,
43
+ notification_arns: cfn_notification_arns,
44
+ change_set_name: change_set_name,
45
+ ).id
46
+ rescue ::Aws::CloudFormation::Errors::ValidationError => e
47
+ fail_task(e.message)
48
+ end
49
+
33
50
  ## wait and return true if changeset ready for execute
34
51
  def change_set_complete?(id)
35
52
  begin
@@ -94,5 +111,32 @@ module Stax
94
111
  change_set_lock
95
112
  end
96
113
 
114
+ desc 'import', 'create and execute changeset to import a resource'
115
+ option :type, aliases: '-t', type: :string, default: nil, desc: 'cfn resource (e.g. AWS::DynamoDB::Table)'
116
+ option :id, aliases: '-i', type: :string, default: nil, desc: 'logical id (e.g. OrdersTable)'
117
+ option :key, aliases: '-k', type: :string, default: nil, desc: 'resource key (e.g. TableName)'
118
+ option :value, aliases: '-v', type: :string, default: nil, desc: 'resource value (e.g. orders)'
119
+ def import
120
+ ## prompt for missing options
121
+ %i[type id key value].each do |opt|
122
+ options[opt] ||= ask("Resource #{opt}?", :yellow)
123
+ end
124
+
125
+ ## create import changeset
126
+ debug("Creating import change set for #{stack_name}")
127
+ id = change_set_import(
128
+ resource_type: options[:type],
129
+ logical_resource_id: options[:id],
130
+ resource_identifier: {
131
+ options[:key] => options[:value]
132
+ }
133
+ )
134
+
135
+ ## wait for changeset, prompt for changes, and execute
136
+ change_set_complete?(id) || fail_task(change_set_reason(id))
137
+ change_set_changes(id)
138
+ change_set_execute(id) && tail && update_warn_imports
139
+ end
140
+
97
141
  end
98
142
  end
@@ -21,8 +21,8 @@ module Stax
21
21
  end
22
22
  end
23
23
 
24
- desc 'imports', 'list imports from this stack'
25
- def imports
24
+ desc 'exports', 'list exports from this stack, and stacks that import them'
25
+ def exports
26
26
  debug("Stacks that import from #{stack_name}")
27
27
  print_table Aws::Cfn.exports(stack_name).map { |e|
28
28
  imports = (i = Aws::Cfn.imports(e.export_name)).empty? ? '-' : i.join(' ')
@@ -30,5 +30,11 @@ module Stax
30
30
  }.sort
31
31
  end
32
32
 
33
+ desc 'imports', 'deprecated: use exports'
34
+ def imports
35
+ warn("deprecated method: please use 'exports' instead")
36
+ exports
37
+ end
38
+
33
39
  end
34
- end
40
+ end
@@ -21,4 +21,4 @@ module Stax
21
21
  end
22
22
 
23
23
  end
24
- end
24
+ end
data/lib/stax/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Stax
2
- VERSION = '0.1.7'
2
+ VERSION = '0.1.10'
3
3
  end
data/lib/stax.rb CHANGED
@@ -16,7 +16,7 @@ require 'stax/stack/crud'
16
16
  require 'stax/stack/changeset'
17
17
  require 'stax/stack/parameters'
18
18
  require 'stax/stack/outputs'
19
- require 'stax/stack/imports'
19
+ require 'stax/stack/exports'
20
20
  require 'stax/stack/resources'
21
21
  require 'stax/stack/drift'
22
22
 
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.1.7
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Lister
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-29 00:00:00.000000000 Z
11
+ date: 2022-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -582,7 +582,7 @@ files:
582
582
  - lib/stax/stack/changeset.rb
583
583
  - lib/stax/stack/crud.rb
584
584
  - lib/stax/stack/drift.rb
585
- - lib/stax/stack/imports.rb
585
+ - lib/stax/stack/exports.rb
586
586
  - lib/stax/stack/outputs.rb
587
587
  - lib/stax/stack/parameters.rb
588
588
  - lib/stax/stack/resources.rb
@@ -610,7 +610,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
610
610
  - !ruby/object:Gem::Version
611
611
  version: '0'
612
612
  requirements: []
613
- rubygems_version: 3.1.4
613
+ rubygems_version: 3.1.6
614
614
  signing_key:
615
615
  specification_version: 4
616
616
  summary: Control Cloudformation stack and other stuff.