stax 0.0.2 → 0.0.3

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