stax 0.0.2 → 0.0.3

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
  SHA1:
3
- metadata.gz: d9678c52393c842e801f8e3c306eb6976049e7d9
4
- data.tar.gz: 6732bf5a03a47223ac78300ff338d71df796047c
3
+ metadata.gz: 0444c2ae4f050d73ba7cfd720eca476667102bf2
4
+ data.tar.gz: 764f80db2e413eade8840ef8f04c33d93003e190
5
5
  SHA512:
6
- metadata.gz: 97d814ccc5329e44a534213a6100a8f485e4c2c59eac762cc6aa1b5ccabd79c2cb0707f4d82383fcd5d29fb914836cdbfd0f2af9ad5ddbd900a68824af6022e5
7
- data.tar.gz: de492742715aaf17fbcd5a57cd4a626469264a6a1af95d7bc5808b58007d28aa02ee9861643affefd33b7d5fe89547c9fc55e08b3b14cf9be6767599230b99b1
6
+ metadata.gz: bb84fb5b04019ef607f8f4a6658414fdda2b3dbf641a31bbedadfb785ab20f1e76c8d039529c71a52d5f18419ead4c5594297bdfc7303efef8ccb4f8d0ef0191
7
+ data.tar.gz: f65676b1870954bcc39c5d53a0ecd7b238d5b2fafd7bd2ea5761fc993dece0e33c7b8c2205ebf6a61021b148ba01f84f8856a523fd4d4c2fa6ff3bbfcea6fb41
@@ -8,6 +8,7 @@ require 'stax/staxfile'
8
8
  require 'stax/base'
9
9
  require 'stax/git'
10
10
  require 'stax/cli'
11
+ require 'stax/meta'
11
12
  require 'stax/subcommand'
12
13
  require 'stax/cfer'
13
14
  require 'stax/stack'
@@ -32,4 +33,6 @@ require 'stax/mixin/emr'
32
33
  require 'stax/mixin/ssh'
33
34
  require 'stax/mixin/lambda'
34
35
  require 'stax/mixin/dynamodb'
35
- require 'stax/mixin/logs'
36
+ require 'stax/mixin/logs'
37
+ require 'stax/mixin/apigw'
38
+ require 'stax/mixin/firehose'
@@ -0,0 +1,24 @@
1
+
2
+ module Stax
3
+ module Aws
4
+ class APIGateway < Sdk
5
+
6
+ class << self
7
+
8
+ def client
9
+ @_client ||= ::Aws::APIGateway::Client.new
10
+ end
11
+
12
+ def api(id)
13
+ client.get_rest_api(rest_api_id: id)
14
+ end
15
+
16
+ def stages(id, deployment = nil)
17
+ client.get_stages(rest_api_id: id, deployment_id: deployment).item
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module Stax
2
4
  module Aws
3
5
  class DynamoDB < Sdk
@@ -20,6 +22,81 @@ module Stax
20
22
  client.describe_table(table_name: name).table.local_secondary_indexes || []
21
23
  end
22
24
 
25
+ def key_schema(name)
26
+ client.describe_table(table_name: name).table.key_schema
27
+ end
28
+
29
+ ## key schema as a hash
30
+ def keys(name)
31
+ key_schema(name).each_with_object({}) do |s, h|
32
+ h[s.key_type.downcase.to_sym] = s.attribute_name
33
+ end
34
+ end
35
+
36
+ def do_scan(opt)
37
+ exclusive_start_key = nil
38
+ loop do
39
+ r = client.scan(opt.merge(exclusive_start_key: exclusive_start_key))
40
+ yield r
41
+ exclusive_start_key = r.last_evaluated_key
42
+ break unless exclusive_start_key
43
+ end
44
+ end
45
+
46
+ def scan(opt)
47
+ do_scan(opt) do |r|
48
+ r.items.each do |item|
49
+ puts JSON.generate(item)
50
+ end
51
+ end
52
+ end
53
+
54
+ def count(opt)
55
+ total = 0
56
+ do_scan(opt.merge(select: 'COUNT')) do |r|
57
+ total += r.count
58
+ end
59
+ return total
60
+ end
61
+
62
+ def query(opt)
63
+ exclusive_start_key = nil
64
+ loop do
65
+ r = client.query(opt.merge(exclusive_start_key: exclusive_start_key))
66
+ r.items.each do |item|
67
+ puts JSON.generate(item)
68
+ end
69
+ exclusive_start_key = r.last_evaluated_key
70
+ break unless exclusive_start_key
71
+ end
72
+ end
73
+
74
+ def list_backups(opt = {})
75
+ last_arn = nil
76
+ backups = []
77
+ loop do
78
+ r = client.list_backups(opt.merge(exclusive_start_backup_arn: last_arn))
79
+ backups += r.backup_summaries
80
+ last_arn = r.last_evaluated_backup_arn
81
+ break unless last_arn
82
+ end
83
+ backups
84
+ end
85
+
86
+ def create_backup(table_name, backup_name)
87
+ client.create_backup(
88
+ table_name: table_name,
89
+ backup_name: backup_name,
90
+ ).backup_details
91
+ end
92
+
93
+ def restore_backup(table_name, backup_arn)
94
+ client.restore_table_from_backup(
95
+ target_table_name: table_name,
96
+ backup_arn: backup_arn,
97
+ ).table_description
98
+ end
99
+
23
100
  end
24
101
 
25
102
  end
@@ -16,6 +16,10 @@ module Stax
16
16
  client.describe_services(cluster: cluster, services: services).services
17
17
  end
18
18
 
19
+ def update_service(opt)
20
+ client.update_service(opt).service
21
+ end
22
+
19
23
  def task_definition(name)
20
24
  client.describe_task_definition(task_definition: name).task_definition
21
25
  end
@@ -27,7 +31,12 @@ module Stax
27
31
  end
28
32
 
29
33
  def tasks(cluster, status = :RUNNING)
30
- client.describe_tasks(cluster: cluster, tasks: list_tasks(cluster, status)).tasks
34
+ tasks = list_tasks(cluster, status)
35
+ if tasks.empty?
36
+ []
37
+ else
38
+ client.describe_tasks(cluster: cluster, tasks: tasks).tasks
39
+ end
31
40
  end
32
41
 
33
42
  def list_instances(cluster)
@@ -0,0 +1,19 @@
1
+ module Stax
2
+ module Aws
3
+ class Firehose < Sdk
4
+
5
+ class << self
6
+
7
+ def client
8
+ @_client ||= ::Aws::Firehose::Client.new
9
+ end
10
+
11
+ def describe(name)
12
+ client.describe_delivery_stream(delivery_stream_name: name).delivery_stream_description
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -26,6 +26,10 @@ module Stax
26
26
  client.invoke(opt)
27
27
  end
28
28
 
29
+ def update_code(opt)
30
+ client.update_function_code(opt)
31
+ end
32
+
29
33
  end
30
34
 
31
35
  end
@@ -18,6 +18,10 @@ module Stax
18
18
  client.describe_log_streams(opt).log_streams
19
19
  end
20
20
 
21
+ def delete_group(name)
22
+ client.delete_log_group(log_group_name: name)
23
+ end
24
+
21
25
  end
22
26
 
23
27
  end
@@ -5,6 +5,7 @@ module Stax
5
5
  class_option :use_previous_value, aliases: '-u', type: :array, default: [], desc: 'params to use previous value'
6
6
 
7
7
  no_commands do
8
+
8
9
  def cfer_parameters
9
10
  {}
10
11
  end
@@ -48,11 +49,28 @@ module Stax
48
49
  Cfer.generate!(cfer_template, opts)
49
50
  end
50
51
 
52
+ ## generate method does puts, so steal stdout into a string
53
+ def cfer_generate_string
54
+ capture_stdout do
55
+ cfer_generate
56
+ end
57
+ end
58
+
51
59
  def cfer_tail
52
60
  Cfer.tail!(stack_name, follow: true, number: 1)
53
61
  rescue ::Aws::CloudFormation::Errors::ValidationError => e
54
62
  puts e.message
55
63
  end
64
+
65
+ ## temporarily grab stdout to a string
66
+ def capture_stdout
67
+ stdout, $stdout = $stdout, StringIO.new
68
+ yield
69
+ $stdout.string
70
+ ensure
71
+ $stdout = stdout
72
+ end
73
+
56
74
  end
57
75
 
58
76
  end
@@ -0,0 +1,39 @@
1
+ module Stax
2
+ class Cli < Base
3
+
4
+ no_commands do
5
+ ## create order: default to stacks from Staxfile, override if needed
6
+ ## delete will be this, in reverse order
7
+ def stack_order
8
+ Stax.stack_list
9
+ end
10
+
11
+ def stack_objects
12
+ stack_order.map(&method(:stack))
13
+ end
14
+ end
15
+
16
+ desc 'create', 'meta create task'
17
+ def create
18
+ stack_objects.each do |s|
19
+ if s.exists?
20
+ say("Skipping: #{s.stack_name} exists", :yellow)
21
+ elsif y_or_n?("Create #{s.stack_name}?", :yellow)
22
+ s.create
23
+ end
24
+ end
25
+ end
26
+
27
+ desc 'delete', 'meta delete task'
28
+ def delete
29
+ stack_objects.reverse.each do |s|
30
+ if s.exists?
31
+ s.delete
32
+ else
33
+ say("#{s.stack_name} does not exist", :green)
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ require 'stax/aws/apigw'
2
+
3
+ module Stax
4
+ module Apigw
5
+ def self.included(thor)
6
+ thor.desc(:apigw, 'API Gateway subcommands')
7
+ thor.subcommand(:apigw, Cmd::Apigw)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class Apigw < SubCommand
13
+
14
+ no_commands do
15
+ def stack_apis
16
+ Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ApiGateway::RestApi')
17
+ end
18
+ end
19
+
20
+ desc 'ls', 'list APIS'
21
+ def ls
22
+ print_table stack_apis.map { |r|
23
+ a = Aws::APIGateway.api(r.physical_resource_id)
24
+ [a.name, a.id, a.endpoint_configuration.types.join(','), a.created_date, a.description]
25
+ }
26
+ end
27
+
28
+ desc 'stages', 'list API stages'
29
+ def stages
30
+ stack_apis.each do |r|
31
+ api = Aws::APIGateway.api(r.physical_resource_id)
32
+ debug("Stages for API #{api.name} #{api.id}")
33
+ print_table Aws::APIGateway.stages(api.id).map { |s|
34
+ [s.stage_name, s.deployment_id, s.created_date, s.last_updated_date, s.description]
35
+ }
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -1,4 +1,7 @@
1
+ require 'yaml'
1
2
  require 'stax/aws/dynamodb'
3
+ require_relative 'dynamodb/throughput'
4
+ require_relative 'dynamodb/backup'
2
5
 
3
6
  module Stax
4
7
  module DynamoDB
@@ -12,16 +15,25 @@ module Stax
12
15
  class DynamoDB < SubCommand
13
16
 
14
17
  COLORS = {
15
- CREATING: :yellow,
16
- UPDATING: :yellow,
17
- DELETING: :red,
18
- ACTIVE: :green,
18
+ CREATING: :yellow,
19
+ UPDATING: :yellow,
20
+ DELETING: :red,
21
+ ACTIVE: :green,
22
+ DELETED: :red,
23
+ AVAILABLE: :green,
19
24
  }
20
25
 
21
26
  no_commands do
22
27
  def stack_tables
23
28
  Aws::Cfn.resources_by_type(my.stack_name, 'AWS::DynamoDB::Table')
24
29
  end
30
+
31
+ ## get table names from logical IDs, return all tables if nil
32
+ def stack_table_names(logical_ids)
33
+ stack_tables.tap do |tables|
34
+ tables.select! { |t| logical_ids.include?(t.logical_resource_id) } if logical_ids
35
+ end.map(&:physical_resource_id)
36
+ end
25
37
  end
26
38
 
27
39
  desc 'tables', 'list tables for stack'
@@ -52,9 +64,34 @@ module Stax
52
64
 
53
65
  desc 'keys ID', 'get hash and range keys of table with ID'
54
66
  def keys(id)
55
- print_table Aws::DynamoDB.table(my.resource(id)).key_schema.each_with_object({}) { |schema, h|
56
- h[schema.key_type.downcase.to_sym] = schema.attribute_name
57
- }
67
+ print_table Aws::DynamoDB.keys(my.resource(id))
68
+ end
69
+
70
+ desc 'scan ID', 'scan table with given logical id from this stack'
71
+ def scan(id)
72
+ Aws::DynamoDB.scan(table_name: my.resource(id))
73
+ end
74
+
75
+ desc 'count ID', 'count items in table with given id'
76
+ def count(id)
77
+ puts Aws::DynamoDB.count(table_name: my.resource(id))
78
+ end
79
+
80
+ desc 'query ID HASH_VALUE [RANGE_VALUE]', 'query table with id'
81
+ def query(id, hash_value, range_value = nil)
82
+ name = my.resource(id)
83
+ k = Aws::DynamoDB.keys(name)
84
+ Aws::DynamoDB.query(
85
+ table_name: name,
86
+ expression_attribute_values: {
87
+ ':h' => hash_value,
88
+ ':r' => range_value,
89
+ }.compact,
90
+ key_condition_expression: [
91
+ "#{k[:hash]} = :h",
92
+ range_value ? "#{k[:range]} = :r" : nil,
93
+ ].compact.join(' and '),
94
+ )
58
95
  end
59
96
 
60
97
  end
@@ -0,0 +1,41 @@
1
+ module Stax
2
+ module Cmd
3
+ class DynamoDB < SubCommand
4
+
5
+ no_commands do
6
+ def list_backups(name)
7
+ debug("Backups for #{name}")
8
+ print_table Aws::DynamoDB.list_backups(table_name: name).map { |b|
9
+ [b.backup_name, color(b.backup_status, COLORS), b.table_name, b.backup_creation_date_time, human_bytes(b.backup_size_bytes)]
10
+ }
11
+ end
12
+
13
+ def create_backup(table_name, backup_name)
14
+ backup_name = Time.now.utc.strftime("#{table_name}-%Y%m%d%H%M%S") if backup_name == 'create' # thor option empty
15
+ debug("Creating backup #{backup_name} from #{table_name}")
16
+ Aws::DynamoDB.create_backup(table_name, backup_name).tap do |b|
17
+ puts YAML.dump(stringify_keys(b.to_hash))
18
+ end
19
+ end
20
+ end
21
+
22
+ desc 'backup ID', 'table backups'
23
+ method_option :create, aliases: '-c', type: :string, default: nil, desc: 'create new backup from table'
24
+ def backup(id = nil)
25
+ name = my.resource(id)
26
+ if options[:create]
27
+ create_backup(name, options[:create])
28
+ else
29
+ list_backups(name)
30
+ end
31
+ end
32
+
33
+ desc 'restore ARN TABLE', 'restore backup to a new table'
34
+ def restore(arn, table)
35
+ debug("Creating table #{table} from backup #{arn}")
36
+ Aws::DynamoDB.restore_backup(table, arn)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,67 @@
1
+ module Stax
2
+ module Cmd
3
+ class DynamoDB < SubCommand
4
+
5
+ no_commands do
6
+ def print_throughput(ids)
7
+ output = [['NAME', 'INDEX NAME', 'READ CAPACITY', 'WRITE CAPACITY', 'DECREASES TODAY']]
8
+ stack_table_names(ids).each do |name|
9
+ table = Aws::DynamoDB.table(name)
10
+ t = table.provisioned_throughput
11
+ output << [table.table_name, nil, t.read_capacity_units, t.write_capacity_units, t.number_of_decreases_today]
12
+ (table.global_secondary_indexes || []).each do |gsi|
13
+ t = gsi.provisioned_throughput
14
+ output << [table.table_name, gsi.index_name, t.read_capacity_units, t.write_capacity_units, t.number_of_decreases_today]
15
+ end
16
+ end
17
+ print_table(output)
18
+ end
19
+
20
+ def update_throughput(ids, read, write)
21
+ debug("Updating throughput on #{ids ? ids.count : 'all'} tables")
22
+ stack_table_names(ids).each do |name|
23
+ puts name
24
+ table = Aws::DynamoDB.table(name)
25
+ begin
26
+ Aws::DynamoDB.client.update_table(
27
+ table_name: name,
28
+ provisioned_throughput: {
29
+ read_capacity_units: read || table.provisioned_throughput.read_capacity_units,
30
+ write_capacity_units: write || table.provisioned_throughput.write_capacity_units,
31
+ },
32
+ global_secondary_index_updates: table.global_secondary_indexes&.map do |gsi|
33
+ {
34
+ update: {
35
+ index_name: gsi.index_name,
36
+ provisioned_throughput: {
37
+ read_capacity_units: read || gsi.provisioned_throughput.read_capacity_units,
38
+ write_capacity_units: write || gsi.provisioned_throughput.write_capacity_units,
39
+ }
40
+ }
41
+ }
42
+ end
43
+ )
44
+ rescue ::Aws::DynamoDB::Errors::ValidationException
45
+ puts 'no change'
46
+ rescue ::Aws::DynamoDB::Errors::ResourceInUseException => e
47
+ warn(e.message)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ desc 'throughput ID', 'throughput'
54
+ method_option :tables, aliases: '-t', type: :array, default: nil, desc: 'limit to given table IDs'
55
+ method_option :read, aliases: '-r', type: :numeric, default: nil, desc: 'set read capacity units'
56
+ method_option :write, aliases: '-w', type: :numeric, default: nil, desc: 'set write capacity units'
57
+ def throughput
58
+ if options[:write] || options[:read]
59
+ update_throughput(options[:tables], options[:read], options[:write])
60
+ else
61
+ print_throughput(options[:tables])
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -6,6 +6,42 @@ module Stax
6
6
  thor.desc(:ecs, 'ECS subcommands')
7
7
  thor.subcommand(:ecs, Cmd::Ecs)
8
8
  end
9
+
10
+ def ecs_cluster_name
11
+ 'default'
12
+ end
13
+
14
+ def ecs_services
15
+ @_ecs_services ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::ECS::Service')
16
+ end
17
+
18
+ def ecs_task_definitions
19
+ @_ecs_task_definitions ||= Aws::Cfn.resources_by_type(stack_name, 'AWS::ECS::TaskDefinition')
20
+ end
21
+
22
+ def ecs_service_objects
23
+ Aws::Ecs.services(ecs_cluster_name, ecs_services.map(&:physical_resource_id))
24
+ end
25
+
26
+ ## register a new revision of existing task definition
27
+ def ecs_update_taskdef(id)
28
+ taskdef = Aws::Ecs.task_definition(resource(id))
29
+ debug("Registering new revision of #{taskdef.family}")
30
+ args = %i[family cpu memory requires_compatibilities task_role_arn execution_role_arn network_mode container_definitions volumes placement_constraints]
31
+ Aws::Ecs.client.register_task_definition(taskdef.to_hash.slice(*args)).task_definition.tap do |t|
32
+ puts t.task_definition_arn
33
+ end
34
+ end
35
+
36
+ ## update service to use a new task definition
37
+ def ecs_update_service(id, taskdef)
38
+ service_name = resource(id).split('/').last
39
+ taskdef_name = taskdef.task_definition_arn.split('/').last
40
+ debug("Updating #{service_name} to #{taskdef_name}")
41
+ Aws::Ecs.update_service(service: service_name, task_definition: taskdef_name).tap do |s|
42
+ puts s.task_definition
43
+ end
44
+ end
9
45
  end
10
46
 
11
47
  module Cmd
@@ -18,26 +54,14 @@ module Stax
18
54
  }
19
55
 
20
56
  no_commands do
21
- def ecs_cluster_name
22
- @_ecs_cluster_name ||= my.stack_name
23
- end
24
-
25
- def ecs_task_definitions
26
- @_ecs_task_definitions ||= Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ECS::TaskDefinition' )
27
- end
28
-
29
57
  def ecs_task_definition(id)
30
58
  Aws::Cfn.id(my.stack_name, id)
31
59
  end
32
-
33
- def ecs_services
34
- @_ecs_services ||= Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ECS::Service')
35
- end
36
60
  end
37
61
 
38
62
  desc 'clusters', 'ECS cluster for stack'
39
63
  def clusters
40
- print_table Aws::Ecs.clusters(ecs_cluster_name).map { |c|
64
+ print_table Aws::Ecs.clusters(my.ecs_cluster_name).map { |c|
41
65
  [
42
66
  c.cluster_name,
43
67
  color(c.status, COLORS),
@@ -50,63 +74,119 @@ module Stax
50
74
 
51
75
  desc 'services', 'ECS services for stack'
52
76
  def services
53
- print_table Aws::Ecs.services(ecs_cluster_name, ecs_services.map(&:physical_resource_id)).map { |s|
77
+ print_table my.ecs_service_objects.map { |s|
54
78
  [s.service_name, color(s.status, COLORS), s.task_definition.split('/').last, "#{s.running_count}/#{s.desired_count}"]
55
79
  }
56
80
  end
57
81
 
82
+ desc 'events', 'show service events'
83
+ method_option :number, aliases: '-n', type: :numeric, default: 10, desc: 'number of events to show'
84
+ def events
85
+ my.ecs_service_objects.each do |s|
86
+ debug("Events for #{s.service_name}")
87
+ print_table s.events.first(options[:number]).map { |e|
88
+ [set_color(e.created_at, :green), e.message]
89
+ }.reverse
90
+ end
91
+ end
92
+
93
+ desc 'deployments', 'show service deployments'
94
+ def deployments
95
+ my.ecs_service_objects.each do |s|
96
+ debug("Deployments for #{s.service_name}")
97
+ print_table s.deployments.map { |d|
98
+ count = "#{d.running_count}/#{d.desired_count} (#{d.pending_count})"
99
+ [d.id, d.status, count, d.created_at, d.updated_at, d.task_definition.split('/').last]
100
+ }
101
+ end
102
+ end
103
+
58
104
  desc 'definitions', 'ECS task definitions for stack'
59
105
  def definitions
60
- print_table ecs_task_definitions.map { |r|
106
+ print_table my.ecs_task_definitions.map { |r|
61
107
  t = Aws::Ecs.task_definition(r.physical_resource_id)
62
108
  [r.logical_resource_id, t.family, t.revision, color(t.status, COLORS)]
63
109
  }
64
110
  end
65
111
 
66
- desc 'tasks' , 'ECS tasks for stack'
67
- method_option :status, aliases: '-s', type: :string, default: 'RUNNING', desc: 'status to list'
68
- def tasks
69
- print_table Aws::Ecs.tasks(ecs_cluster_name, options[:status].upcase).map { |t|
70
- [
71
- t.task_arn.split('/').last,
72
- t.task_definition_arn.split('/').last,
73
- t.container_instance_arn.split('/').last,
74
- color(t.last_status, COLORS),
75
- "(#{t.desired_status})",
76
- t.started_by,
77
- ]
78
- }
79
- end
80
-
81
- desc 'instances', 'ECS instances'
82
- def instances
83
- print_table Aws::Ecs.instances(ecs_cluster_name).map { |i|
84
- [
85
- i.container_instance_arn.split('/').last,
86
- i.ec2_instance_id,
87
- i.agent_connected,
88
- color(i.status, COLORS),
89
- i.running_tasks_count,
90
- "(#{i.pending_tasks_count})",
91
- "agent #{i.version_info.agent_version}",
92
- i.version_info.docker_version,
93
- ]
94
- }
95
- end
96
-
97
- desc 'run_task [ID]', 'run task by id'
98
- def run_task(id)
99
- Aws::Ecs.run(ecs_cluster_name, Aws::Cfn.id(my.stack_name, id)).tap do |tasks|
100
- puts tasks.map(&:container_instance_arn)
101
- end
102
- end
103
-
104
- desc 'stop_task [TASK]', 'stop task'
105
- def stop_task(task)
106
- Aws::Ecs.stop(ecs_cluster_name, task).tap do |task|
107
- puts task.container_instance_arn
108
- end
109
- end
112
+ desc 'tasks', 'ECS tasks for stack'
113
+ method_option :status, aliases: '-s', type: :string, default: 'RUNNING', desc: 'status to list'
114
+ def tasks
115
+ print_table Aws::Ecs.tasks(my.ecs_cluster_name, options[:status].upcase).map { |t|
116
+ [
117
+ t.task_arn.split('/').last,
118
+ t.task_definition_arn.split('/').last,
119
+ t.container_instance_arn&.split('/')&.last || '--',
120
+ color(t.last_status, COLORS),
121
+ "(#{t.desired_status})",
122
+ t.started_by,
123
+ ]
124
+ }
125
+ end
126
+
127
+ desc 'containers', 'containers for running tasks'
128
+ method_option :status, aliases: '-s', type: :string, default: 'RUNNING', desc: 'status to list'
129
+ def containers
130
+ debug("Containers for cluster #{my.ecs_cluster_name}")
131
+ print_table Aws::Ecs.tasks(my.ecs_cluster_name, options[:status].upcase).map { |task|
132
+ task_defn = task.task_definition_arn.split('/').last
133
+ task.containers.map { |c|
134
+ [
135
+ c.container_arn.split('/').last,
136
+ c.name,
137
+ color(c.last_status, COLORS),
138
+ c.network_interfaces.map(&:private_ipv_4_address).join(','),
139
+ task_defn,
140
+ c.exit_code,
141
+ c.reason,
142
+ ]
143
+ }
144
+ }.flatten(1)
145
+ end
146
+
147
+ desc 'instances', 'ECS instances'
148
+ def instances
149
+ print_table Aws::Ecs.instances(my.ecs_cluster_name).map { |i|
150
+ [
151
+ i.container_instance_arn.split('/').last,
152
+ i.ec2_instance_id,
153
+ i.agent_connected,
154
+ color(i.status, COLORS),
155
+ i.running_tasks_count,
156
+ "(#{i.pending_tasks_count})",
157
+ "agent #{i.version_info.agent_version}",
158
+ i.version_info.docker_version,
159
+ ]
160
+ }
161
+ end
162
+
163
+ desc 'run_task [ID]', 'run task by id'
164
+ def run_task(id)
165
+ Aws::Ecs.run(my.ecs_cluster_name, Aws::Cfn.id(my.stack_name, id)).tap do |tasks|
166
+ puts tasks.map(&:container_instance_arn)
167
+ end
168
+ end
169
+
170
+ desc 'stop_task [TASK]', 'stop task'
171
+ def stop_task(task)
172
+ Aws::Ecs.stop(my.ecs_cluster_name, task).tap do |task|
173
+ puts task.container_instance_arn
174
+ end
175
+ end
176
+
177
+ desc 'scale', 'scale containers for service'
178
+ method_option :desired, aliases: '-d', type: :numeric, default: nil, desc: 'desired container count'
179
+ def scale
180
+ my.ecs_services.each do |s|
181
+ debug("Scaling service #{s.logical_resource_id}")
182
+ Aws::Ecs.update_service(
183
+ service: s.physical_resource_id,
184
+ desired_count: options[:desired],
185
+ ).tap do |s|
186
+ puts "desired: #{s.desired_count}"
187
+ end
188
+ end
189
+ end
110
190
 
111
191
  end
112
192
 
@@ -0,0 +1,34 @@
1
+ require 'stax/aws/firehose'
2
+
3
+ module Stax
4
+ module Firehose
5
+ def self.included(thor)
6
+ thor.desc(:firehose, 'Firehose subcommands')
7
+ thor.subcommand(:firehose, Cmd::Firehose)
8
+
9
+ def stack_firehoses
10
+ Aws::Cfn.resources_by_type(stack_name, 'AWS::KinesisFirehose::DeliveryStream')
11
+ end
12
+ end
13
+ end
14
+
15
+ module Cmd
16
+ class Firehose < SubCommand
17
+
18
+ COLORS = {
19
+ ACTIVE: :green,
20
+ CREATING: :yellow,
21
+ DELETING: :red,
22
+ }
23
+
24
+ desc 'ls', 'list stack firehoses'
25
+ def ls
26
+ print_table my.stack_firehoses.map { |r|
27
+ f = Aws::Firehose.describe(r.physical_resource_id)
28
+ [f.delivery_stream_name, color(f.delivery_stream_status, COLORS), f.create_timestamp, f.delivery_stream_type]
29
+ }
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -18,6 +18,21 @@ module Stax
18
18
  def stack_lambdas
19
19
  Aws::Cfn.resources_by_type(my.stack_name, 'AWS::Lambda::Function')
20
20
  end
21
+
22
+ ## return zip file contents, make it if necessary
23
+ def zip_thing(thing)
24
+ if File.directory?(thing)
25
+ Dir.chdir(thing) do
26
+ %x[zip -q -r - .] # zip dir contents
27
+ end
28
+ elsif thing.match(/\.zip$/i)
29
+ File.read(thing) # raw zipfile contents
30
+ elsif File.file?(thing)
31
+ %x[zip -q -j - #{thing}] # zip a single file
32
+ else
33
+ nil
34
+ end
35
+ end
21
36
  end
22
37
 
23
38
  desc 'ls', 'list lambdas for stack'
@@ -52,6 +67,18 @@ module Stax
52
67
  end
53
68
  end
54
69
 
70
+ desc 'update ID FILE', 'update code for lambda function with ID'
71
+ method_option :publish, aliases: '-p', type: :boolean, default: false, desc: 'publish as a new version'
72
+ def update(id, file)
73
+ Aws::Lambda.update_code(
74
+ function_name: my.resource(id),
75
+ publish: options[:publish],
76
+ zip_file: zip_thing(file),
77
+ )&.version.tap do |v|
78
+ puts "version: #{v}"
79
+ end
80
+ end
81
+
55
82
  desc 'test ID', 'run lambda with ID'
56
83
  method_option :type, type: :string, default: nil, desc: 'invocation type: RequestResponse, Event'
57
84
  method_option :tail, type: :boolean, default: false, desc: 'tail log for RequestResponse'
@@ -89,6 +89,18 @@ module Stax
89
89
  end
90
90
  end
91
91
 
92
+ ## lambdas create their own log groups, and when we delete stack they are left behind;
93
+ ## this task looks up their names by stack prefix, and deletes them
94
+ desc 'cleanup', 'cleanup lambda log groups named for stack'
95
+ method_option :test, aliases: '-t', type: :boolean, default: false, desc: 'show group names without deleting'
96
+ def cleanup
97
+ debug("Cleaning up log groups for stack #{my.stack_name}")
98
+ Aws::Logs.groups("/aws/lambda/#{my.stack_name}").map(&:log_group_name).each do |name|
99
+ puts name
100
+ Aws::Logs.delete_group(name) unless options[:test]
101
+ end
102
+ end
103
+
92
104
  end
93
105
  end
94
106
  end
@@ -75,6 +75,11 @@ module Stax
75
75
  cfer_tail
76
76
  end
77
77
 
78
+ desc 'generate', 'generate cloudformation template'
79
+ def generate
80
+ cfer_generate
81
+ end
82
+
78
83
  desc 'protection', 'show/set termination protection for stack'
79
84
  method_option :enable, aliases: '-e', type: :boolean, default: nil, desc: 'enable termination protection'
80
85
  method_option :disable, aliases: '-d', type: :boolean, default: nil, desc: 'disable termination protection'
@@ -1,4 +1,9 @@
1
1
  module Stax
2
+ @@_stack_list = []
3
+
4
+ def self.stack_list
5
+ @@_stack_list
6
+ end
2
7
 
3
8
  ## search up the dir tree for nearest Staxfile
4
9
  def self.load_staxfile
@@ -14,6 +19,7 @@ module Stax
14
19
 
15
20
  ## add a stack by name, creates class as needed
16
21
  def self.add_stack(name, opt = {})
22
+ @@_stack_list << name
17
23
  c = name.capitalize
18
24
 
19
25
  ## create the class if it does not exist yet
@@ -1,3 +1,3 @@
1
1
  module Stax
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
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.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Lister
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-21 00:00:00.000000000 Z
11
+ date: 2018-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -131,6 +131,7 @@ files:
131
131
  - lib/stax.rb
132
132
  - lib/stax/asg.rb
133
133
  - lib/stax/aws/alb.rb
134
+ - lib/stax/aws/apigw.rb
134
135
  - lib/stax/aws/asg.rb
135
136
  - lib/stax/aws/cfn.rb
136
137
  - lib/stax/aws/dynamodb.rb
@@ -139,6 +140,7 @@ files:
139
140
  - lib/stax/aws/ecs.rb
140
141
  - lib/stax/aws/elb.rb
141
142
  - lib/stax/aws/emr.rb
143
+ - lib/stax/aws/firehose.rb
142
144
  - lib/stax/aws/iam.rb
143
145
  - lib/stax/aws/keypair.rb
144
146
  - lib/stax/aws/kms.rb
@@ -159,13 +161,18 @@ files:
159
161
  - lib/stax/github.rb
160
162
  - lib/stax/iam.rb
161
163
  - lib/stax/keypair.rb
164
+ - lib/stax/meta.rb
162
165
  - lib/stax/mixin/alb.rb
166
+ - lib/stax/mixin/apigw.rb
163
167
  - lib/stax/mixin/asg.rb
164
168
  - lib/stax/mixin/dynamodb.rb
169
+ - lib/stax/mixin/dynamodb/backup.rb
170
+ - lib/stax/mixin/dynamodb/throughput.rb
165
171
  - lib/stax/mixin/ec2.rb
166
172
  - lib/stax/mixin/ecs.rb
167
173
  - lib/stax/mixin/elb.rb
168
174
  - lib/stax/mixin/emr.rb
175
+ - lib/stax/mixin/firehose.rb
169
176
  - lib/stax/mixin/keypair.rb
170
177
  - lib/stax/mixin/kms.rb
171
178
  - lib/stax/mixin/lambda.rb