stax 0.1.7 → 0.1.10

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: 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.