stax 0.0.1 → 0.0.2

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/bin/stax +1 -4
  3. data/lib/stax.rb +33 -3
  4. data/lib/stax/asg.rb +140 -0
  5. data/lib/stax/aws/alb.rb +28 -0
  6. data/lib/stax/aws/asg.rb +34 -0
  7. data/lib/stax/aws/cfn.rb +102 -0
  8. data/lib/stax/aws/dynamodb.rb +27 -0
  9. data/lib/stax/aws/ec2.rb +22 -0
  10. data/lib/stax/aws/ecr.rb +25 -0
  11. data/lib/stax/aws/ecs.rb +54 -0
  12. data/lib/stax/aws/elb.rb +30 -0
  13. data/lib/stax/aws/emr.rb +28 -0
  14. data/lib/stax/aws/iam.rb +19 -0
  15. data/lib/stax/aws/keypair.rb +26 -0
  16. data/lib/stax/aws/kms.rb +19 -0
  17. data/lib/stax/aws/lambda.rb +33 -0
  18. data/lib/stax/aws/logs.rb +25 -0
  19. data/lib/stax/aws/s3.rb +41 -0
  20. data/lib/stax/aws/sdk.rb +21 -0
  21. data/lib/stax/aws/sg.rb +42 -0
  22. data/lib/stax/aws/sqs.rb +30 -0
  23. data/lib/stax/aws/ssm.rb +49 -0
  24. data/lib/stax/aws/sts.rb +31 -0
  25. data/lib/stax/base.rb +92 -4
  26. data/lib/stax/cfer.rb +59 -0
  27. data/lib/stax/cli.rb +13 -4
  28. data/lib/stax/docker.rb +106 -0
  29. data/lib/stax/dsl.rb +15 -0
  30. data/lib/stax/git.rb +61 -0
  31. data/lib/stax/github.rb +25 -0
  32. data/lib/stax/iam.rb +18 -0
  33. data/lib/stax/keypair.rb +75 -0
  34. data/lib/stax/mixin/alb.rb +45 -0
  35. data/lib/stax/mixin/asg.rb +115 -0
  36. data/lib/stax/mixin/dynamodb.rb +62 -0
  37. data/lib/stax/mixin/ec2.rb +37 -0
  38. data/lib/stax/mixin/ecs.rb +114 -0
  39. data/lib/stax/mixin/elb.rb +42 -0
  40. data/lib/stax/mixin/emr.rb +69 -0
  41. data/lib/stax/mixin/keypair.rb +45 -0
  42. data/lib/stax/mixin/kms.rb +30 -0
  43. data/lib/stax/mixin/lambda.rb +76 -0
  44. data/lib/stax/mixin/logs.rb +94 -0
  45. data/lib/stax/mixin/s3.rb +76 -0
  46. data/lib/stax/mixin/sg.rb +95 -0
  47. data/lib/stax/mixin/sqs.rb +49 -0
  48. data/lib/stax/mixin/ssh.rb +52 -0
  49. data/lib/stax/mixin/ssm.rb +137 -0
  50. data/lib/stax/stack.rb +19 -35
  51. data/lib/stax/stack/cfn.rb +24 -0
  52. data/lib/stax/stack/crud.rb +92 -0
  53. data/lib/stax/stack/outputs.rb +24 -0
  54. data/lib/stax/stack/parameters.rb +24 -0
  55. data/lib/stax/stack/resources.rb +35 -0
  56. data/lib/stax/staxfile.rb +41 -0
  57. data/lib/stax/subcommand.rb +12 -0
  58. data/lib/stax/version.rb +1 -1
  59. data/stax.gemspec +6 -13
  60. metadata +105 -9
@@ -0,0 +1,45 @@
1
+ require 'stax/aws/alb'
2
+
3
+ module Stax
4
+ module Alb
5
+ def self.included(thor)
6
+ thor.desc(:alb, 'ALB subcommands')
7
+ thor.subcommand(:alb, Cmd::Alb)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class Alb < SubCommand
13
+
14
+ COLORS = {
15
+ healthy: :green,
16
+ unhealthy: :red,
17
+ unavailable: :red,
18
+ }
19
+
20
+ no_commands do
21
+ def stack_albs
22
+ Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ElasticLoadBalancingV2::LoadBalancer')
23
+ end
24
+ end
25
+
26
+ desc 'dns', 'ALB DNS names'
27
+ def dns
28
+ puts Aws::Alb.describe(stack_albs.map(&:physical_resource_id)).map(&:dns_name)
29
+ end
30
+
31
+ desc 'status', 'ALB instance status'
32
+ def status
33
+ stack_albs.each do |alb|
34
+ Aws::Alb.target_groups(alb.physical_resource_id).each do |t|
35
+ debug("ALB status for #{alb.logical_resource_id} #{t.protocol}:#{t.port} #{t.target_group_name}")
36
+ print_table Aws::Alb.target_health(t.target_group_arn).map { |h|
37
+ [h.target.id, h.target.port, color(h.target_health.state, COLORS), h.target_health.reason, h.target_health.description]
38
+ }
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,115 @@
1
+ require 'stax/aws/asg'
2
+
3
+ module Stax
4
+ module Asg
5
+ def self.included(thor)
6
+ thor.desc(:asg, 'ASG subcommands')
7
+ thor.subcommand(:asg, Cmd::Asg)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class Asg < SubCommand
13
+ COLORS = {
14
+ ## lifecycle states
15
+ Pending: :yellow, InService: :green, Terminating: :red,
16
+ ## health statuses
17
+ Healthy: :green, Unhealthy: :red,
18
+ ## same for asg instances describe
19
+ HEALTHY: :green, UNHEALTHY: :red,
20
+ ## activity status
21
+ Successful: :green, Failed: :red, Cancelled: :red,
22
+ ## instance state
23
+ running: :green, stopped: :yellow, terminated: :red,
24
+ }
25
+
26
+ class_option :groups, aliases: '-g', type: :array, default: nil, desc: 'limit ASGs returned by id'
27
+
28
+ no_commands do
29
+ def stack_asgs
30
+ a = Aws::Cfn.resources_by_type(my.stack_name, 'AWS::AutoScaling::AutoScalingGroup')
31
+ filter_asgs(a, options[:groups])
32
+ end
33
+
34
+ def filter_asgs(asgs, groups)
35
+ return asgs unless groups
36
+ ids = groups.map { |g| prepend(:asg, g) }
37
+ asgs.select { |g| ids.include?(g.logical_resource_id) }
38
+ end
39
+ end
40
+
41
+ desc 'ls', 'list ASGs for stack'
42
+ def ls
43
+ print_table Aws::Asg.describe(stack_asgs.map(&:physical_resource_id)).map { |a|
44
+ [
45
+ a.auto_scaling_group_name[0,40],
46
+ a.launch_configuration_name[0,40],
47
+ "#{a.instances.length}/#{a.desired_capacity}",
48
+ "#{a.min_size}-#{a.max_size}",
49
+ a.availability_zones.map{ |az| az[-1,1] }.sort.join(','),
50
+ a.created_time
51
+ ]
52
+ }
53
+ end
54
+
55
+ desc 'status', 'status of instances by ASG'
56
+ def status
57
+ stack_asgs.each do |asg|
58
+ debug("ASG status for #{asg.physical_resource_id}")
59
+ print_table Aws::Asg.instances(asg.physical_resource_id).map { |i|
60
+ [
61
+ i.instance_id,
62
+ i.availability_zone,
63
+ color(i.lifecycle_state, COLORS),
64
+ color(i.health_status, COLORS),
65
+ i.launch_configuration_name,
66
+ ]
67
+ }
68
+ end
69
+ end
70
+
71
+ desc 'terminate [ID_REGEXES]', 'terminate matching instances'
72
+ method_option :decrement, aliases: '-d', type: :boolean, default: false, desc: 'decrement desired count after terminate'
73
+ def terminate(*ids)
74
+ instances = Aws::Asg.instances(stack_asgs.map(&:physical_resource_id))
75
+ instances.select do |i|
76
+ ids.any? { |id| i.instance_id.match(id) }
77
+ end.each do |i|
78
+ yes?("Terminate #{i.instance_id}?", :yellow) && Aws::Asg.terminate(i.instance_id, options[:decrement])
79
+ end
80
+ end
81
+
82
+ desc 'old', 'ASG instances with outdated launch config'
83
+ method_option :terminate, aliases: '-t', type: :boolean, default: false, desc: 'terminate outdated instances'
84
+ method_option :decrement, aliases: '-d', type: :boolean, default: false, desc: 'decrement desired count after terminate'
85
+ def old
86
+ Aws::Asg.describe(stack_asgs.map(&:physical_resource_id)).map do |a|
87
+ Aws::Asg.instances(a.auto_scaling_group_name).select do |i|
88
+ i.launch_configuration_name != a.launch_configuration_name
89
+ end
90
+ end.flatten.tap do |list|
91
+ print_table list.map { |i| [i.instance_id, i.auto_scaling_group_name, i.launch_configuration_name] }
92
+ if options[:terminate]
93
+ list.each do |i|
94
+ yes?("Terminate #{i.instance_id}?", :yellow) && Aws::Asg.terminate(i.instance_id, options[:decrement])
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ desc 'scale', 'ASG scale instance count'
101
+ method_option :desired_capacity, aliases: '-d', type: :numeric, default: nil, desc: 'set desired instance count'
102
+ method_option :min_size, aliases: '-m', type: :numeric, default: nil, desc: 'set minimum capacity'
103
+ method_option :max_size, aliases: '-M', type: :numeric, default: nil, desc: 'set maximum capacity'
104
+ def scale
105
+ opt = options.slice(:desired_capacity, :min_size, :max_size)
106
+ fail_task('No change requested') if opt.empty?
107
+ stack_asgs.each do |a|
108
+ debug("Scaling to #{opt} for #{a.logical_resource_id} #{a.physical_resource_id}")
109
+ Aws::Asg.update(a.physical_resource_id, opt)
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,62 @@
1
+ require 'stax/aws/dynamodb'
2
+
3
+ module Stax
4
+ module DynamoDB
5
+ def self.included(thor)
6
+ thor.desc(:dynamodb, 'Dynamo subcommands')
7
+ thor.subcommand(:dynamodb, Cmd::DynamoDB)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class DynamoDB < SubCommand
13
+
14
+ COLORS = {
15
+ CREATING: :yellow,
16
+ UPDATING: :yellow,
17
+ DELETING: :red,
18
+ ACTIVE: :green,
19
+ }
20
+
21
+ no_commands do
22
+ def stack_tables
23
+ Aws::Cfn.resources_by_type(my.stack_name, 'AWS::DynamoDB::Table')
24
+ end
25
+ end
26
+
27
+ desc 'tables', 'list tables for stack'
28
+ def tables
29
+ print_table stack_tables.map { |r|
30
+ t = Aws::DynamoDB.table(r.physical_resource_id)
31
+ [ t.table_name, color(t.table_status, COLORS), t.item_count, t.table_size_bytes, t.creation_date_time ]
32
+ }
33
+ end
34
+
35
+ desc 'gsi ID', 'list global secondary indexes for table with ID'
36
+ def gsi(id)
37
+ print_table Aws::DynamoDB.gsi(my.resource(id)).map { |i|
38
+ hash = i.key_schema.find{ |k| k.key_type == 'HASH' }&.attribute_name
39
+ range = i.key_schema.find{ |k| k.key_type == 'RANGE' }&.attribute_name
40
+ [i.index_name, hash, range, i.projection.projection_type, i.index_size_bytes, i.item_count]
41
+ }.sort
42
+ end
43
+
44
+ desc 'lsi ID', 'list local secondary indexes for table with ID'
45
+ def lsi(id)
46
+ print_table Aws::DynamoDB.lsi(my.resource(id)).map { |i|
47
+ hash = i.key_schema.find{ |k| k.key_type == 'HASH' }&.attribute_name
48
+ range = i.key_schema.find{ |k| k.key_type == 'RANGE' }&.attribute_name
49
+ [i.index_name, hash, range, i.projection.projection_type, i.index_size_bytes, i.item_count]
50
+ }.sort
51
+ end
52
+
53
+ desc 'keys ID', 'get hash and range keys of table with ID'
54
+ 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
+ }
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,37 @@
1
+ require 'stax/aws/ec2'
2
+
3
+ module Stax
4
+ module Ec2
5
+ def self.included(thor)
6
+ thor.desc(:ec2, 'EC2 subcommands')
7
+ thor.subcommand(:ec2, Cmd::Ec2)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class Ec2 < SubCommand
13
+ COLORS = {
14
+ running: :green,
15
+ stopped: :yellow,
16
+ terminated: :red,
17
+ }
18
+
19
+ desc 'ls', 'list instances for stack'
20
+ def ls
21
+ print_table Aws::Ec2.instances(my.stack_name).map { |i|
22
+ name = i.tags.find { |t| t.key == 'Name' }&.value
23
+ [
24
+ name,
25
+ i.instance_id,
26
+ i.instance_type,
27
+ i.placement.availability_zone,
28
+ color(i.state.name, COLORS),
29
+ i.private_ip_address,
30
+ i.public_ip_address
31
+ ]
32
+ }
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,114 @@
1
+ require 'stax/aws/ecs'
2
+
3
+ module Stax
4
+ module Ecs
5
+ def self.included(thor)
6
+ thor.desc(:ecs, 'ECS subcommands')
7
+ thor.subcommand(:ecs, Cmd::Ecs)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class Ecs < SubCommand
13
+ COLORS = {
14
+ ACTIVE: :green,
15
+ INACTIVE: :red,
16
+ RUNNING: :green,
17
+ STOPPED: :red,
18
+ }
19
+
20
+ 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
+ def ecs_task_definition(id)
30
+ Aws::Cfn.id(my.stack_name, id)
31
+ end
32
+
33
+ def ecs_services
34
+ @_ecs_services ||= Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ECS::Service')
35
+ end
36
+ end
37
+
38
+ desc 'clusters', 'ECS cluster for stack'
39
+ def clusters
40
+ print_table Aws::Ecs.clusters(ecs_cluster_name).map { |c|
41
+ [
42
+ c.cluster_name,
43
+ color(c.status, COLORS),
44
+ "instances:#{c.registered_container_instances_count}",
45
+ "pending:#{c.pending_tasks_count}",
46
+ "running:#{c.running_tasks_count}",
47
+ ]
48
+ }
49
+ end
50
+
51
+ desc 'services', 'ECS services for stack'
52
+ def services
53
+ print_table Aws::Ecs.services(ecs_cluster_name, ecs_services.map(&:physical_resource_id)).map { |s|
54
+ [s.service_name, color(s.status, COLORS), s.task_definition.split('/').last, "#{s.running_count}/#{s.desired_count}"]
55
+ }
56
+ end
57
+
58
+ desc 'definitions', 'ECS task definitions for stack'
59
+ def definitions
60
+ print_table ecs_task_definitions.map { |r|
61
+ t = Aws::Ecs.task_definition(r.physical_resource_id)
62
+ [r.logical_resource_id, t.family, t.revision, color(t.status, COLORS)]
63
+ }
64
+ end
65
+
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
110
+
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,42 @@
1
+ require 'stax/aws/elb'
2
+
3
+ module Stax
4
+ module Elb
5
+ def self.included(thor)
6
+ thor.desc(:elb, 'ELB subcommands')
7
+ thor.subcommand(:elb, Cmd::Elb)
8
+ end
9
+ end
10
+
11
+ module Cmd
12
+ class Elb < SubCommand
13
+
14
+ COLORS = {
15
+ InService: :green,
16
+ OutOfService: :red,
17
+ }
18
+
19
+ no_commands do
20
+ def stack_elbs
21
+ Aws::Cfn.resources_by_type(my.stack_name, 'AWS::ElasticLoadBalancing::LoadBalancer')
22
+ end
23
+ end
24
+
25
+ desc 'dns', 'ALB DNS names'
26
+ def dns
27
+ puts Aws::Elb.describe(stack_elbs.map(&:physical_resource_id)).map(&:dns_name)
28
+ end
29
+
30
+ desc 'status', 'ELB instance status'
31
+ def status
32
+ stack_elbs.each do |elb|
33
+ debug("ELB status for #{elb.logical_resource_id} #{elb.physical_resource_id}")
34
+ print_table Aws::Elb.instance_health(elb.physical_resource_id).map { |i|
35
+ [i.instance_id, color(i.state, COLORS), i.reason_code, i.description]
36
+ }
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,69 @@
1
+ require 'stax/aws/emr'
2
+ require 'yaml'
3
+
4
+ module Stax
5
+ module Emr
6
+ def self.included(thor)
7
+ thor.desc(:emr, 'Emr subcommands')
8
+ thor.subcommand(:emr, Cmd::Emr)
9
+ end
10
+ end
11
+
12
+ module Cmd
13
+ class Emr < SubCommand
14
+
15
+ COLORS = {
16
+ RUNNING: :green,
17
+ WAITING: :green,
18
+ TERMINATING: :red,
19
+ TERMINATED: :red,
20
+ TERMINATED_WITH_ERRORS: :red,
21
+ }
22
+
23
+ no_commands do
24
+ def stack_emr_clusters
25
+ Aws::Cfn.resources_by_type(my.stack_name, 'AWS::EMR::Cluster')
26
+ end
27
+ end
28
+
29
+ desc 'status', 'EMR cluster state'
30
+ def status
31
+ print_table stack_emr_clusters.map { |r|
32
+ c = Aws::Emr.describe(r.physical_resource_id)
33
+ [color(c.status.state, COLORS), c.status.state_change_reason.message]
34
+ }
35
+ end
36
+
37
+ desc 'describe', 'describe EMR clusters'
38
+ def describe
39
+ stack_emr_clusters.each do |r|
40
+ Aws::Emr.describe(r.physical_resource_id).tap do |c|
41
+ puts YAML.dump(stringify_keys(c.to_hash))
42
+ end
43
+ end
44
+ end
45
+
46
+ desc 'groups', 'EMR instance groups'
47
+ def groups
48
+ stack_emr_clusters.each do |r|
49
+ debug("Instance groups for #{r.logical_resource_id} #{r.physical_resource_id}")
50
+ print_table Aws::Emr.groups(r.physical_resource_id).map { |g|
51
+ [g.id, color(g.status.state, COLORS), g.name, g.instance_type, g.running_instance_count, g.market]
52
+ }
53
+ end
54
+ end
55
+
56
+ desc 'instances', 'EMR instances'
57
+ def instances
58
+ stack_emr_clusters.each do |r|
59
+ debug("Instances for #{r.logical_resource_id} #{r.physical_resource_id}")
60
+ group_names = Aws::Emr.groups(r.physical_resource_id).each_with_object({}) { |g,h| h[g.id] = g.name }
61
+ print_table Aws::Emr.instances(r.physical_resource_id).map { |i|
62
+ [i.id, i.ec2_instance_id, group_names[i.instance_group_id], i.instance_type, color(i.status.state, COLORS), i.public_ip_address]
63
+ }
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+ end