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.
- checksums.yaml +4 -4
- data/bin/stax +1 -4
- data/lib/stax.rb +33 -3
- data/lib/stax/asg.rb +140 -0
- data/lib/stax/aws/alb.rb +28 -0
- data/lib/stax/aws/asg.rb +34 -0
- data/lib/stax/aws/cfn.rb +102 -0
- data/lib/stax/aws/dynamodb.rb +27 -0
- data/lib/stax/aws/ec2.rb +22 -0
- data/lib/stax/aws/ecr.rb +25 -0
- data/lib/stax/aws/ecs.rb +54 -0
- data/lib/stax/aws/elb.rb +30 -0
- data/lib/stax/aws/emr.rb +28 -0
- data/lib/stax/aws/iam.rb +19 -0
- data/lib/stax/aws/keypair.rb +26 -0
- data/lib/stax/aws/kms.rb +19 -0
- data/lib/stax/aws/lambda.rb +33 -0
- data/lib/stax/aws/logs.rb +25 -0
- data/lib/stax/aws/s3.rb +41 -0
- data/lib/stax/aws/sdk.rb +21 -0
- data/lib/stax/aws/sg.rb +42 -0
- data/lib/stax/aws/sqs.rb +30 -0
- data/lib/stax/aws/ssm.rb +49 -0
- data/lib/stax/aws/sts.rb +31 -0
- data/lib/stax/base.rb +92 -4
- data/lib/stax/cfer.rb +59 -0
- data/lib/stax/cli.rb +13 -4
- data/lib/stax/docker.rb +106 -0
- data/lib/stax/dsl.rb +15 -0
- data/lib/stax/git.rb +61 -0
- data/lib/stax/github.rb +25 -0
- data/lib/stax/iam.rb +18 -0
- data/lib/stax/keypair.rb +75 -0
- data/lib/stax/mixin/alb.rb +45 -0
- data/lib/stax/mixin/asg.rb +115 -0
- data/lib/stax/mixin/dynamodb.rb +62 -0
- data/lib/stax/mixin/ec2.rb +37 -0
- data/lib/stax/mixin/ecs.rb +114 -0
- data/lib/stax/mixin/elb.rb +42 -0
- data/lib/stax/mixin/emr.rb +69 -0
- data/lib/stax/mixin/keypair.rb +45 -0
- data/lib/stax/mixin/kms.rb +30 -0
- data/lib/stax/mixin/lambda.rb +76 -0
- data/lib/stax/mixin/logs.rb +94 -0
- data/lib/stax/mixin/s3.rb +76 -0
- data/lib/stax/mixin/sg.rb +95 -0
- data/lib/stax/mixin/sqs.rb +49 -0
- data/lib/stax/mixin/ssh.rb +52 -0
- data/lib/stax/mixin/ssm.rb +137 -0
- data/lib/stax/stack.rb +19 -35
- data/lib/stax/stack/cfn.rb +24 -0
- data/lib/stax/stack/crud.rb +92 -0
- data/lib/stax/stack/outputs.rb +24 -0
- data/lib/stax/stack/parameters.rb +24 -0
- data/lib/stax/stack/resources.rb +35 -0
- data/lib/stax/staxfile.rb +41 -0
- data/lib/stax/subcommand.rb +12 -0
- data/lib/stax/version.rb +1 -1
- data/stax.gemspec +6 -13
- metadata +105 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9678c52393c842e801f8e3c306eb6976049e7d9
|
4
|
+
data.tar.gz: 6732bf5a03a47223ac78300ff338d71df796047c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97d814ccc5329e44a534213a6100a8f485e4c2c59eac762cc6aa1b5ccabd79c2cb0707f4d82383fcd5d29fb914836cdbfd0f2af9ad5ddbd900a68824af6022e5
|
7
|
+
data.tar.gz: de492742715aaf17fbcd5a57cd4a626469264a6a1af95d7bc5808b58007d28aa02ee9861643affefd33b7d5fe89547c9fc55e08b3b14cf9be6767599230b99b1
|
data/bin/stax
CHANGED
data/lib/stax.rb
CHANGED
@@ -1,5 +1,35 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
require 'stax/aws/sdk'
|
4
|
+
require 'stax/aws/cfn'
|
5
|
+
|
6
|
+
require 'stax/dsl'
|
7
|
+
require 'stax/staxfile'
|
3
8
|
require 'stax/base'
|
9
|
+
require 'stax/git'
|
4
10
|
require 'stax/cli'
|
5
|
-
require 'stax/
|
11
|
+
require 'stax/subcommand'
|
12
|
+
require 'stax/cfer'
|
13
|
+
require 'stax/stack'
|
14
|
+
require 'stax/stack/cfn'
|
15
|
+
require 'stax/stack/crud'
|
16
|
+
require 'stax/stack/parameters'
|
17
|
+
require 'stax/stack/outputs'
|
18
|
+
require 'stax/stack/resources'
|
19
|
+
|
20
|
+
require 'stax/mixin/ec2'
|
21
|
+
require 'stax/mixin/alb'
|
22
|
+
require 'stax/mixin/elb'
|
23
|
+
require 'stax/mixin/sg'
|
24
|
+
require 'stax/mixin/s3'
|
25
|
+
require 'stax/mixin/asg'
|
26
|
+
require 'stax/mixin/ecs'
|
27
|
+
require 'stax/mixin/sqs'
|
28
|
+
require 'stax/mixin/kms'
|
29
|
+
require 'stax/mixin/ssm'
|
30
|
+
require 'stax/mixin/keypair'
|
31
|
+
require 'stax/mixin/emr'
|
32
|
+
require 'stax/mixin/ssh'
|
33
|
+
require 'stax/mixin/lambda'
|
34
|
+
require 'stax/mixin/dynamodb'
|
35
|
+
require 'stax/mixin/logs'
|
data/lib/stax/asg.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'awful/auto_scaling'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module Asg
|
5
|
+
def self.included(thor) # magic to make mixins work in Thor
|
6
|
+
thor.class_eval do # ... so magical
|
7
|
+
|
8
|
+
class_option :groups, aliases: '-g', type: :array, default: nil, desc: 'limit ASGs returned by id'
|
9
|
+
|
10
|
+
no_commands do
|
11
|
+
def auto_scaling_groups
|
12
|
+
cf(:resources, [stack_name], type: ['AWS::AutoScaling::AutoScalingGroup'], quiet: true).tap do |asgs|
|
13
|
+
if options[:groups]
|
14
|
+
ids = options[:groups].map { |group| prepend(:asg, group) }
|
15
|
+
asgs.select! { |g| ids.include?(g.logical_resource_id) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def auto_scaling_instances
|
21
|
+
asg(:instances, auto_scaling_groups.map(&:physical_resource_id), describe: true, quiet: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
## get instance details from ec2
|
25
|
+
def auto_scaling_describe_instances
|
26
|
+
asgs = auto_scaling_groups.map(&:physical_resource_id)
|
27
|
+
fail_task("No matching autoscaling groups") if asgs.empty?
|
28
|
+
asg(:ips, auto_scaling_groups.map(&:physical_resource_id), quiet: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
def asg_status
|
32
|
+
auto_scaling_groups.each do |asg|
|
33
|
+
debug("ASG status for #{asg.physical_resource_id}")
|
34
|
+
asg(:instances, [asg.physical_resource_id], long: true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def asg_enter_standby(asg, *instances)
|
39
|
+
debug("Taking #{instances.join(',')} out of ELB for #{asg}")
|
40
|
+
instances.each do |instance| # one at a time so we can rescue each one
|
41
|
+
begin
|
42
|
+
asg(:enter_standby, [asg, instance])
|
43
|
+
rescue Aws::AutoScaling::Errors::ValidationError => e
|
44
|
+
warn(e.message)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def asg_exit_standby(asg, *instances)
|
50
|
+
debug("Putting #{instances.join(',')} back into ELB")
|
51
|
+
instances.each do |instance| # one at a time so we can rescue each one
|
52
|
+
begin
|
53
|
+
asg(:exit_standby, [asg, instance])
|
54
|
+
rescue Aws::AutoScaling::Errors::ValidationError => e
|
55
|
+
warn(e.message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
## defaults, override in subclass
|
61
|
+
def ssh_options
|
62
|
+
{
|
63
|
+
User: 'core',
|
64
|
+
StrictHostKeyChecking: 'no',
|
65
|
+
UserKnownHostsFile: '/dev/null'
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
## return num instances, filter by ids if non-nil
|
70
|
+
def ssh_instances(num, ids)
|
71
|
+
instances = auto_scaling_describe_instances
|
72
|
+
instances.select!{ |i| ids.include?(i.instance_id) } if ids
|
73
|
+
num ? instances.last(num) : instances
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
desc 'scale', 'scale number of instances in ASGs for stack'
|
78
|
+
method_option :desired_capacity, aliases: '-d', type: :numeric, default: nil, desc: 'desired instance count for each ASG'
|
79
|
+
method_option :min_size, aliases: '-m', type: :numeric, default: nil, desc: 'set minimum capacity'
|
80
|
+
method_option :max_size, aliases: '-M', type: :numeric, default: nil, desc: 'set maximum capacity'
|
81
|
+
def scale
|
82
|
+
opt = options.slice(:desired_capacity, :min_size, :max_size)
|
83
|
+
fail_task('No change requested') if opt.empty?
|
84
|
+
|
85
|
+
auto_scaling_groups.tap do |asgs|
|
86
|
+
warn('No matching auto-scaling groups') if asgs.empty?
|
87
|
+
end.each do |asg|
|
88
|
+
id = asg.physical_resource_id
|
89
|
+
debug("Scaling to #{opt} for #{id}")
|
90
|
+
asg(:update, [id], opt)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'old', 'list or terminate old instances from ASGs'
|
95
|
+
method_option :terminate, aliases: '-t', type: :boolean, default: false, desc: 'terminate old instances'
|
96
|
+
def old
|
97
|
+
verb = options[:terminate] ? 'Terminating' : 'Listing'
|
98
|
+
debug("#{verb} out-of-date instances in autoscaling groups")
|
99
|
+
asgs = auto_scaling_groups.map(&:physical_resource_id)
|
100
|
+
asg(:old_instances, asgs, terminate: options[:terminate])
|
101
|
+
end
|
102
|
+
|
103
|
+
desc 'standby', 'enter (or exit) standby for ASGs'
|
104
|
+
method_option :exit, aliases: '-x', type: :boolean, default: false, desc: 'exit standby instead of enter'
|
105
|
+
def standby
|
106
|
+
auto_scaling_instances.each_with_object(Hash.new {|h,k| h[k]=[]}) do |i, h|
|
107
|
+
h[i.auto_scaling_group_name] << i.instance_id
|
108
|
+
end.each do |asg, ins|
|
109
|
+
options[:exit] ? asg_exit_standby(asg, *ins) : asg_enter_standby(asg, *ins)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
desc 'ssh [CMD]', 'ssh to ASG instances'
|
114
|
+
method_option :number, aliases: '-n', type: :numeric, default: nil, desc: 'number of instances to ssh'
|
115
|
+
method_option :verbose, aliases: '-v', type: :boolean, default: false, desc: 'verbose ssh client logging'
|
116
|
+
method_option :instances, aliases: '-i', type: :array, default: nil, desc: 'match on these instance IDs'
|
117
|
+
def ssh(*cmd)
|
118
|
+
keyfile = try(:key_pair_get) # get private key from param store
|
119
|
+
try(:let_me_in_allow) # open security group
|
120
|
+
|
121
|
+
## build ssh -o options
|
122
|
+
opts = ssh_options.merge(
|
123
|
+
IdentityFile: keyfile.try(:path),
|
124
|
+
LogLevel: (options[:verbose] ? 'DEBUG' : nil)
|
125
|
+
).reject{ |_,v| v.nil? }.map{ |k,v| "-o #{k}=#{v}" }.join(' ')
|
126
|
+
|
127
|
+
## loop instances
|
128
|
+
ssh_instances(options[:number], options[:instances]).each do |i|
|
129
|
+
debug("SSH to #{i.instance_id} #{i.public_ip_address}")
|
130
|
+
system "ssh #{opts} #{i.public_ip_address} #{cmd.join(' ')}"
|
131
|
+
end
|
132
|
+
ensure
|
133
|
+
keyfile.try(:unlink) # remove private key
|
134
|
+
try(:let_me_in_revoke) # close security group
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/stax/aws/alb.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Alb < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::ElasticLoadBalancingV2::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def describe(alb_arns)
|
12
|
+
client.describe_load_balancers(load_balancer_arns: alb_arns).load_balancers
|
13
|
+
end
|
14
|
+
|
15
|
+
def target_groups(alb_arn)
|
16
|
+
paginate(:target_groups) do |marker|
|
17
|
+
client.describe_target_groups(load_balancer_arn: alb_arn, marker: marker)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def target_health(tg_arn)
|
22
|
+
client.describe_target_health(target_group_arn: tg_arn).target_health_descriptions.flatten(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/stax/aws/asg.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Asg < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::AutoScaling::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def describe(names)
|
12
|
+
paginate(:auto_scaling_groups) do |token|
|
13
|
+
client.describe_auto_scaling_groups(auto_scaling_group_names: Array(names), next_token: token)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def instances(names)
|
18
|
+
ids = describe(names).map(&:instances).flatten.map(&:instance_id)
|
19
|
+
paginate(:auto_scaling_instances) do |token|
|
20
|
+
client.describe_auto_scaling_instances(instance_ids: ids, next_token: token)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(name, opt = {})
|
25
|
+
client.update_auto_scaling_group(opt.merge(auto_scaling_group_name: name))
|
26
|
+
end
|
27
|
+
|
28
|
+
def terminate(id, decrement = false)
|
29
|
+
client.terminate_instance_in_auto_scaling_group(instance_id: id, should_decrement_desired_capacity: decrement)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/stax/aws/cfn.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Cfn < Sdk
|
4
|
+
|
5
|
+
## stack statuses that are not DELETE_COMPLETE
|
6
|
+
STATUSES = %i[
|
7
|
+
CREATE_IN_PROGRESS CREATE_FAILED CREATE_COMPLETE
|
8
|
+
ROLLBACK_IN_PROGRESS ROLLBACK_FAILED ROLLBACK_COMPLETE
|
9
|
+
DELETE_IN_PROGRESS DELETE_FAILED
|
10
|
+
UPDATE_IN_PROGRESS UPDATE_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_COMPLETE
|
11
|
+
UPDATE_ROLLBACK_IN_PROGRESS UPDATE_ROLLBACK_FAILED UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_ROLLBACK_COMPLETE
|
12
|
+
REVIEW_IN_PROGRESS
|
13
|
+
]
|
14
|
+
|
15
|
+
COLORS = {
|
16
|
+
CREATE_COMPLETE: :green,
|
17
|
+
DELETE_COMPLETE: :green,
|
18
|
+
UPDATE_COMPLETE: :green,
|
19
|
+
CREATE_FAILED: :red,
|
20
|
+
DELETE_FAILED: :red,
|
21
|
+
UPDATE_FAILED: :red,
|
22
|
+
ROLLBACK_IN_PROGRESS: :red,
|
23
|
+
ROLLBACK_COMPLETE: :red,
|
24
|
+
}
|
25
|
+
|
26
|
+
class << self
|
27
|
+
|
28
|
+
def client
|
29
|
+
@_client ||= ::Aws::CloudFormation::Client.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def stacks
|
33
|
+
paginate(:stack_summaries) do |token|
|
34
|
+
client.list_stacks(stack_status_filter: STATUSES, next_token: token)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def template(name)
|
39
|
+
client.get_template(stack_name: name).template_body
|
40
|
+
end
|
41
|
+
|
42
|
+
def resources(name)
|
43
|
+
paginate(:stack_resource_summaries) do |token|
|
44
|
+
client.list_stack_resources(stack_name: name, next_token: token)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def resources_by_type(name, type)
|
49
|
+
resources(name).select do |r|
|
50
|
+
r.resource_type == type
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def events(name)
|
55
|
+
paginate(:stack_events) do |token|
|
56
|
+
client.describe_stack_events(stack_name: name, next_token: token)
|
57
|
+
end
|
58
|
+
rescue ::Aws::CloudFormation::Errors::ValidationError => e
|
59
|
+
puts e.message
|
60
|
+
end
|
61
|
+
|
62
|
+
def id(name, id)
|
63
|
+
client.describe_stack_resource(stack_name: name, logical_resource_id: id).stack_resource_detail.physical_resource_id
|
64
|
+
end
|
65
|
+
|
66
|
+
def parameters(name)
|
67
|
+
client.describe_stacks(stack_name: name).stacks.first.parameters
|
68
|
+
end
|
69
|
+
|
70
|
+
def describe(name)
|
71
|
+
client.describe_stacks(stack_name: name).stacks.first
|
72
|
+
end
|
73
|
+
|
74
|
+
def exists?(name)
|
75
|
+
Cfn.describe(name) && true
|
76
|
+
rescue ::Aws::CloudFormation::Errors::ValidationError
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
def outputs(name)
|
81
|
+
describe(name).outputs.each_with_object(HashWithIndifferentAccess.new) do |o, h|
|
82
|
+
h[o.output_key] = o.output_value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def output(name, key)
|
87
|
+
outputs(name)[key]
|
88
|
+
end
|
89
|
+
|
90
|
+
def delete(name)
|
91
|
+
client.delete_stack(stack_name: name)
|
92
|
+
end
|
93
|
+
|
94
|
+
def protection(name, enable)
|
95
|
+
client.update_termination_protection(stack_name: name, enable_termination_protection: enable)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class DynamoDB < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::DynamoDB::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def table(name)
|
12
|
+
client.describe_table(table_name: name).table
|
13
|
+
end
|
14
|
+
|
15
|
+
def gsi(name)
|
16
|
+
client.describe_table(table_name: name).table.global_secondary_indexes || []
|
17
|
+
end
|
18
|
+
|
19
|
+
def lsi(name)
|
20
|
+
client.describe_table(table_name: name).table.local_secondary_indexes || []
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/stax/aws/ec2.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Stax
|
2
|
+
module Aws
|
3
|
+
class Ec2 < Sdk
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def client
|
8
|
+
@_client ||= ::Aws::EC2::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
## return instances tagged by stack with name
|
12
|
+
def instances(name)
|
13
|
+
filter = {name: 'tag:aws:cloudformation:stack-name', values: [name]}
|
14
|
+
paginate(:reservations) do |token|
|
15
|
+
client.describe_instances(filters: [filter], next_token: token)
|
16
|
+
end.map(&:instances).flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/stax/aws/ecr.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Stax
|
4
|
+
module Aws
|
5
|
+
class Ecr < Sdk
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def client
|
10
|
+
@_client ||= ::Aws::ECR::Client.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def auth
|
14
|
+
client.get_authorization_token.authorization_data
|
15
|
+
end
|
16
|
+
|
17
|
+
def exists?(repo, tag)
|
18
|
+
!client.batch_get_image(repository_name: repo, image_ids: [{image_tag: tag}]).images.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|