stax 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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