kontena-plugin-aws 0.2.7 → 0.3.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kontena/machine/aws/keypair_provisioner.rb +38 -0
- data/lib/kontena/machine/aws/master_destroyer.rb +46 -0
- data/lib/kontena/machine/aws/master_provisioner.rb +3 -5
- data/lib/kontena/machine/aws/node_destroyer.rb +1 -1
- data/lib/kontena/machine/aws/node_provisioner.rb +4 -5
- data/lib/kontena/machine/aws.rb +2 -0
- data/lib/kontena/plugin/aws/master/create_command.rb +19 -43
- data/lib/kontena/plugin/aws/master/terminate_command.rb +40 -0
- data/lib/kontena/plugin/aws/master_command.rb +2 -0
- data/lib/kontena/plugin/aws/nodes/create_command.rb +20 -47
- data/lib/kontena/plugin/aws/nodes/restart_command.rb +7 -14
- data/lib/kontena/plugin/aws/nodes/terminate_command.rb +7 -14
- data/lib/kontena/plugin/aws/prompts.rb +138 -106
- data/lib/kontena/plugin/aws.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 944cc0456a0dbe057a86a2662000e2ff622ec8c3
|
4
|
+
data.tar.gz: cc12f182b670334d33db3855c8769d9167064850
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c211fc7de64b11db19d949a1e78750a7fcffbc1f6e27c67c2fef9f807172f1eb94fa7cc17656d6edd55f8c72d3f9e5f4b9f5f114df577d569554ba7eace21417
|
7
|
+
data.tar.gz: 45d56e46fdc4374e06a02a25af2c43b84ba647183283aa1be37f8256933eb288a855e51714dc095cc181e513fadc5600bf14975c0619d0c44cedc0df1ad519e4
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Kontena::Machine::Aws
|
4
|
+
class KeypairProvisioner
|
5
|
+
|
6
|
+
attr_reader :ec2, :region, :public_key, :keypair_name
|
7
|
+
|
8
|
+
# @param [String] access_key_id aws_access_key_id
|
9
|
+
# @param [String] secret_key aws_secret_access_key
|
10
|
+
# @param [String] region
|
11
|
+
def initialize(access_key_id, secret_key, region)
|
12
|
+
@ec2 = ::Aws::EC2::Resource.new(
|
13
|
+
region: region, credentials: ::Aws::Credentials.new(access_key_id, secret_key)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_opts!(opts)
|
18
|
+
if opts[:public_key]
|
19
|
+
@public_key = opts[:public_key]
|
20
|
+
else
|
21
|
+
raise "Missing public key"
|
22
|
+
end
|
23
|
+
|
24
|
+
@keypair_name = opts[:keypair_name] || "kontena-#{SecureRandom.hex(4)}-#{Time.now.strftime '%Y-%m-%d'}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [Hash] opts
|
28
|
+
def run!(opts)
|
29
|
+
validate_opts!(opts)
|
30
|
+
ec2.import_key_pair(
|
31
|
+
key_name: keypair_name,
|
32
|
+
public_key_material: public_key,
|
33
|
+
dry_run: false
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Kontena
|
2
|
+
module Machine
|
3
|
+
module Aws
|
4
|
+
class MasterDestroyer
|
5
|
+
|
6
|
+
include Kontena::Cli::ShellSpinner
|
7
|
+
|
8
|
+
attr_reader :ec2, :api_client
|
9
|
+
|
10
|
+
# @param [Kontena::Client] api_client Kontena api client
|
11
|
+
# @param [String] access_key_id aws_access_key_id
|
12
|
+
# @param [String] secret_key aws_secret_access_key
|
13
|
+
# @param [String] region
|
14
|
+
def initialize(api_client, access_key_id, secret_key, region = 'eu-west-1')
|
15
|
+
@api_client = api_client
|
16
|
+
@ec2 = ::Aws::EC2::Resource.new(
|
17
|
+
region: region,
|
18
|
+
credentials: ::Aws::Credentials.new(access_key_id, secret_key)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def run!(name)
|
23
|
+
instances = ec2.instances({
|
24
|
+
filters: [
|
25
|
+
{name: 'tag:Name', values: [name]}
|
26
|
+
]
|
27
|
+
})
|
28
|
+
abort("Cannot find AWS instance #{name}") if instances.to_a.size == 0
|
29
|
+
abort("There are multiple instances with name #{name}") if instances.to_a.size > 1
|
30
|
+
instance = instances.first
|
31
|
+
if instance
|
32
|
+
spinner "Terminating AWS instance #{name.colorize(:cyan)} " do
|
33
|
+
instance.terminate
|
34
|
+
until instance.reload.state.name.to_s == 'terminated'
|
35
|
+
sleep 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
abort "Cannot find instance #{name.colorize(:cyan)} in AWS"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -39,11 +39,9 @@ module Kontena::Machine::Aws
|
|
39
39
|
ami = resolve_ami(region)
|
40
40
|
abort('No valid AMI found for region') unless ami
|
41
41
|
opts[:vpc] = default_vpc.vpc_id unless opts[:vpc]
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
subnet = ec2.subnet(opts[:subnet])
|
46
|
-
end
|
42
|
+
|
43
|
+
raise "Missing :subnet option" if opts[:subnet].nil?
|
44
|
+
subnet = ec2.subnet(opts[:subnet])
|
47
45
|
abort('Failed to find subnet!') unless subnet
|
48
46
|
|
49
47
|
name = opts[:name] || generate_name
|
@@ -41,7 +41,7 @@ module Kontena
|
|
41
41
|
node = api_client.get("grids/#{grid['id']}/nodes")['nodes'].find{|n| n['name'] == name}
|
42
42
|
if node
|
43
43
|
spinner "Removing node #{name.colorize(:cyan)} from grid #{grid['name'].colorize(:cyan)} " do
|
44
|
-
api_client.delete("
|
44
|
+
api_client.delete("nodes/#{grid['id']}/#{name}")
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -33,11 +33,10 @@ module Kontena::Machine::Aws
|
|
33
33
|
resolve_security_groups_to_ids(opts[:security_groups], opts[:vpc]) :
|
34
34
|
ensure_security_group(opts[:grid], opts[:vpc])
|
35
35
|
|
36
|
-
if opts[:subnet].nil?
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
36
|
+
raise "Missing :subnet option" if opts[:subnet].nil?
|
37
|
+
subnet = ec2.subnet(opts[:subnet])
|
38
|
+
abort('Failed to find subnet!') unless subnet
|
39
|
+
|
41
40
|
dns_server = aws_dns_supported?(opts[:vpc]) ? '169.254.169.253' : '8.8.8.8'
|
42
41
|
instances = []
|
43
42
|
opts[:count].to_i.times do |i|
|
data/lib/kontena/machine/aws.rb
CHANGED
@@ -18,7 +18,9 @@ end
|
|
18
18
|
require 'aws-sdk'
|
19
19
|
require 'kontena/machine/random_name'
|
20
20
|
require 'kontena/machine/cert_helper'
|
21
|
+
require_relative 'aws/keypair_provisioner'
|
21
22
|
require_relative 'aws/master_provisioner'
|
23
|
+
require_relative 'aws/master_destroyer'
|
22
24
|
require_relative 'aws/node_provisioner'
|
23
25
|
require_relative 'aws/node_restarter'
|
24
26
|
require_relative 'aws/node_destroyer'
|
@@ -7,64 +7,40 @@ module Kontena::Plugin::Aws::Master
|
|
7
7
|
include Kontena::Plugin::Aws::Prompts
|
8
8
|
|
9
9
|
option "--name", "[NAME]", "Set Master name"
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
|
11
|
+
include Kontena::Plugin::Aws::Prompts::Common
|
12
|
+
|
13
13
|
option "--ssl-cert", "SSL CERT", "SSL certificate file (default: generate self-signed cert)"
|
14
|
-
option "--region", "REGION", "EC2 Region", environment_variable: "AWS_REGION"
|
15
|
-
option "--zone", "ZONE", "EC2 Availability Zone (a,b,c,d,e)"
|
16
|
-
option "--vpc-id", "VPC ID", "Virtual Private Cloud (VPC) ID"
|
17
|
-
option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into (default: first subnet in vpc/az)"
|
18
|
-
option "--type", "SIZE", "Instance type"
|
19
|
-
option "--storage", "STORAGE", "Storage size (GiB)"
|
20
14
|
option "--vault-secret", "VAULT_SECRET", "Secret key for Vault (default: generate random secret)"
|
21
15
|
option "--vault-iv", "VAULT_IV", "Initialization vector for Vault (default: generate random iv)"
|
22
16
|
option "--mongodb-uri", "URI", "External MongoDB uri (optional)"
|
23
|
-
option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
|
24
|
-
option "--associate-public-ip-address", :flag, "Whether to associated public IP in case the VPC defaults to not doing it", default: true, attribute_name: :associate_public_ip
|
25
|
-
option "--security-groups", "SECURITY_GROUPS", "Comma separated list of security groups (names) where the new instance will be attached (default: create 'kontena_master' group if not already existing)"
|
26
|
-
option "--aws-bundled-cert", :flag, "Use CA certificate bundled in AWS SDK", default: false
|
27
17
|
|
28
18
|
def execute
|
29
19
|
require_relative '../../../machine/aws'
|
30
20
|
|
31
|
-
Aws.use_bundled_cert! if aws_bundled_cert?
|
32
|
-
|
33
|
-
aws_access_key = ask_aws_access_key
|
34
|
-
aws_secret_key = ask_aws_secret_key
|
35
|
-
aws_region = ask_aws_region(aws_access_key, aws_secret_key)
|
36
|
-
aws_zone = ask_aws_az(aws_access_key, aws_secret_key, aws_region)
|
37
|
-
aws_vpc_id = ask_aws_vpc(aws_access_key, aws_secret_key, aws_region)
|
38
|
-
|
39
|
-
exit_with_error("Could not find any Virtual Private Cloud (VPC). Please create one in the AWS console first.") unless aws_vpc_id
|
40
|
-
aws_subnet_id = ask_aws_subnet(aws_access_key, aws_secret_key, aws_region, aws_zone, aws_vpc_id)
|
41
|
-
aws_key_pair = ask_aws_key_pair(aws_access_key, aws_secret_key, aws_region)
|
42
|
-
aws_type = ask_aws_instance_type
|
43
|
-
aws_storage = ask_aws_storage
|
44
|
-
provisioner = provisioner(aws_access_key, aws_secret_key, aws_region)
|
45
21
|
provisioner.run!(
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
22
|
+
name: name,
|
23
|
+
type: type,
|
24
|
+
vpc: vpc_id,
|
25
|
+
zone: zone,
|
26
|
+
subnet: subnet_id,
|
27
|
+
ssl_cert: ssl_cert,
|
28
|
+
storage: storage,
|
29
|
+
version: version,
|
30
|
+
key_pair: key_pair,
|
31
|
+
vault_secret: vault_secret || SecureRandom.hex(24),
|
32
|
+
vault_iv: vault_iv || SecureRandom.hex(24),
|
33
|
+
mongodb_uri: mongodb_uri,
|
34
|
+
associate_public_ip: associate_public_ip?,
|
35
|
+
security_groups: security_groups,
|
36
|
+
initial_admin_code: SecureRandom.hex(16)
|
61
37
|
)
|
62
38
|
rescue Seahorse::Client::NetworkingError => ex
|
63
39
|
raise ex unless ex.message.match(/certificate verify failed/)
|
64
40
|
exit_with_error Kontena::Machine::Aws.ssl_fail_message(aws_bundled_cert?)
|
65
41
|
end
|
66
42
|
|
67
|
-
def provisioner
|
43
|
+
def provisioner
|
68
44
|
Kontena::Machine::Aws::MasterProvisioner.new(access_key, secret_key, region)
|
69
45
|
end
|
70
46
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Kontena::Plugin::Aws::Master
|
2
|
+
class TerminateCommand < Kontena::Command
|
3
|
+
include Kontena::Cli::Common
|
4
|
+
include Kontena::Cli::GridOptions
|
5
|
+
include Kontena::Plugin::Aws::Prompts
|
6
|
+
prepend Kontena::Plugin::Aws::Prompts::Common::Defaults
|
7
|
+
|
8
|
+
option "--access-key", "ACCESS_KEY", "AWS access key ID", environment_variable: "AWS_ACCESS_KEY_ID"
|
9
|
+
option "--secret-key", "SECRET_KEY", "AWS secret access key", environment_variable: "AWS_SECRET_ACCESS_KEY"
|
10
|
+
option "--region", "REGION", "EC2 Region (default: node's region)", environment_variable: "AWS_REGION", attribute_name: :aws_region
|
11
|
+
option "--force", :flag, "Force remove", default: false, attribute_name: :forced
|
12
|
+
option "--aws-bundled-cert", :flag, "Use CA certificate bundled in AWS SDK", default: false
|
13
|
+
|
14
|
+
requires_current_master
|
15
|
+
|
16
|
+
def execute
|
17
|
+
require_current_grid
|
18
|
+
|
19
|
+
node_name = config.current_master.name
|
20
|
+
confirm_command(node_name) unless forced?
|
21
|
+
|
22
|
+
require_relative '../../../machine/aws'
|
23
|
+
Aws.use_bundled_cert! if aws_bundled_cert?
|
24
|
+
|
25
|
+
destroyer.run!(node_name)
|
26
|
+
rescue Seahorse::Client::NetworkingError => ex
|
27
|
+
raise ex unless ex.message.match(/certificate verify failed/)
|
28
|
+
exit_with_error Kontena::Machine::Aws.ssl_fail_message(aws_bundled_cert?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def destroyer
|
32
|
+
Kontena::Machine::Aws::MasterDestroyer.new(client, access_key, secret_key, aws_region)
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_aws_region
|
36
|
+
resolve_region(@node) || default_region
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require_relative 'master/create_command'
|
2
|
+
require_relative 'master/terminate_command'
|
2
3
|
|
3
4
|
class Kontena::Plugin::Aws::MasterCommand < Kontena::Command
|
4
5
|
|
5
6
|
subcommand "create", "Create a new master to AWS", Kontena::Plugin::Aws::Master::CreateCommand
|
7
|
+
subcommand "terminate", "Destroy current master from AWS", Kontena::Plugin::Aws::Master::TerminateCommand
|
6
8
|
|
7
9
|
def execute
|
8
10
|
end
|
@@ -5,56 +5,33 @@ module Kontena::Plugin::Aws::Nodes
|
|
5
5
|
include Kontena::Plugin::Aws::Prompts
|
6
6
|
|
7
7
|
parameter "[NAME]", "Node name"
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
option "--region", "REGION", "EC2 Region", environment_variable: "AWS_REGION"
|
12
|
-
option "--zone", "ZONE", "EC2 Availability Zone (a,b,c,d,e)"
|
13
|
-
option "--vpc-id", "VPC ID", "Virtual Private Cloud (VPC) ID (default: default vpc)"
|
14
|
-
option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into (default: first subnet in vpc/az)"
|
15
|
-
option "--type", "SIZE", "Instance type"
|
16
|
-
option "--storage", "STORAGE", "Storage size (GiB)"
|
8
|
+
|
9
|
+
include Kontena::Plugin::Aws::Prompts::Common
|
10
|
+
|
17
11
|
option "--count", "COUNT", "How many instances to create"
|
18
|
-
option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
|
19
|
-
option "--[no-]associate-public-ip-address", :flag, "Whether to associated public IP in case the VPC defaults to not doing it", default: true, attribute_name: :associate_public_ip
|
20
|
-
option "--security-groups", "SECURITY GROUPS", "Comma separated list of security groups (names) where the new instance will be attached (default: create grid specific group if not already existing)"
|
21
|
-
option "--aws-bundled-cert", :flag, "Use CA certificate bundled in AWS SDK", default: false
|
22
12
|
|
23
13
|
requires_current_master
|
24
14
|
|
25
15
|
def execute
|
26
16
|
require_current_grid
|
27
17
|
require_relative '../../../machine/aws'
|
28
|
-
Aws.use_bundled_cert! if aws_bundled_cert?
|
29
18
|
|
30
19
|
grid = fetch_grid(current_grid)
|
31
|
-
aws_access_key = ask_aws_access_key
|
32
|
-
aws_secret_key = ask_aws_secret_key
|
33
|
-
aws_region = ask_aws_region(aws_access_key, aws_secret_key)
|
34
|
-
aws_zone = ask_aws_az(aws_access_key, aws_secret_key, aws_region)
|
35
|
-
aws_vpc_id = ask_aws_vpc(aws_access_key, aws_secret_key, aws_region)
|
36
|
-
exit_with_error("Could not find any Virtual Private Cloud (VPC). Please create one in the AWS console first.") unless aws_vpc_id
|
37
|
-
aws_subnet_id = ask_aws_subnet(aws_access_key, aws_secret_key, aws_region, aws_zone, aws_vpc_id)
|
38
|
-
aws_key_pair = ask_aws_key_pair(aws_access_key, aws_secret_key, aws_region)
|
39
|
-
aws_type = ask_aws_instance_type
|
40
|
-
aws_storage = ask_aws_storage
|
41
|
-
aws_count = ask_instance_count
|
42
|
-
provisioner = provisioner(client, aws_access_key, aws_secret_key, aws_region)
|
43
20
|
provisioner.run!(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
21
|
+
master_uri: api_url,
|
22
|
+
grid_token: grid['token'],
|
23
|
+
grid: current_grid,
|
24
|
+
name: name,
|
25
|
+
type: type,
|
26
|
+
vpc: vpc_id,
|
27
|
+
zone: zone,
|
28
|
+
subnet: subnet_id,
|
29
|
+
storage: storage,
|
30
|
+
version: version,
|
31
|
+
key_pair: key_pair,
|
32
|
+
count: count,
|
33
|
+
associate_public_ip: associate_public_ip?,
|
34
|
+
security_groups: security_groups
|
58
35
|
)
|
59
36
|
rescue Seahorse::Client::NetworkingError => ex
|
60
37
|
raise ex unless ex.message.match(/certificate verify failed/)
|
@@ -72,16 +49,12 @@ module Kontena::Plugin::Aws::Nodes
|
|
72
49
|
# @param [String] secret_key
|
73
50
|
# @param [String] region
|
74
51
|
# @return [Kontena::Machine::Aws::NodeProvisioner]
|
75
|
-
def provisioner
|
52
|
+
def provisioner
|
76
53
|
Kontena::Machine::Aws::NodeProvisioner.new(client, access_key, secret_key, region)
|
77
54
|
end
|
78
55
|
|
79
|
-
def
|
80
|
-
|
81
|
-
prompt.ask('How many instances?: ', default: 1)
|
82
|
-
else
|
83
|
-
self.count
|
84
|
-
end
|
56
|
+
def default_count
|
57
|
+
prompt.ask('How many instances?: ', default: 1)
|
85
58
|
end
|
86
59
|
end
|
87
60
|
end
|
@@ -3,11 +3,12 @@ module Kontena::Plugin::Aws::Nodes
|
|
3
3
|
include Kontena::Cli::Common
|
4
4
|
include Kontena::Cli::GridOptions
|
5
5
|
include Kontena::Plugin::Aws::Prompts
|
6
|
+
prepend Kontena::Plugin::Aws::Prompts::Common::Defaults
|
6
7
|
|
7
8
|
parameter "[NAME]", "Node name"
|
8
9
|
option "--access-key", "ACCESS_KEY", "AWS access key ID", environment_variable: "AWS_ACCESS_KEY_ID"
|
9
10
|
option "--secret-key", "SECRET_KEY", "AWS secret access key", environment_variable: "AWS_SECRET_ACCESS_KEY"
|
10
|
-
option "--region", "REGION", "EC2 Region", environment_variable: "AWS_REGION"
|
11
|
+
option "--region", "REGION", "EC2 Region (default: node's region)", environment_variable: "AWS_REGION", attribute_name: :aws_region
|
11
12
|
option "--aws-bundled-cert", :flag, "Use CA certificate bundled in AWS SDK", default: false
|
12
13
|
|
13
14
|
requires_current_master
|
@@ -15,31 +16,23 @@ module Kontena::Plugin::Aws::Nodes
|
|
15
16
|
def execute
|
16
17
|
require_current_grid
|
17
18
|
node_name = self.name || ask_node
|
18
|
-
node = client.get("nodes/#{current_grid}/#{node_name}")
|
19
|
+
@node = client.get("nodes/#{current_grid}/#{node_name}")
|
19
20
|
|
20
|
-
aws_access_key = ask_aws_access_key
|
21
|
-
aws_secret_key = ask_aws_secret_key
|
22
|
-
aws_region = self.region || resolve_or_ask_region(node, aws_access_key, aws_secret_key)
|
23
21
|
require_relative '../../../machine/aws'
|
24
22
|
Aws.use_bundled_cert! if aws_bundled_cert?
|
25
23
|
|
26
|
-
restarter = restarter(aws_access_key, aws_secret_key, aws_region)
|
27
24
|
restarter.run!(node_name)
|
28
25
|
rescue Seahorse::Client::NetworkingError => ex
|
29
26
|
raise ex unless ex.message.match(/certificate verify failed/)
|
30
27
|
exit_with_error Kontena::Machine::Aws.ssl_fail_message(aws_bundled_cert?)
|
31
28
|
end
|
32
29
|
|
33
|
-
def restarter
|
34
|
-
Kontena::Machine::Aws::NodeRestarter.new(access_key, secret_key,
|
30
|
+
def restarter
|
31
|
+
Kontena::Machine::Aws::NodeRestarter.new(access_key, secret_key, aws_region)
|
35
32
|
end
|
36
33
|
|
37
|
-
def
|
38
|
-
|
39
|
-
region = region_label.split('=').last
|
40
|
-
end
|
41
|
-
region = ask_aws_region(access_key, secret_key) unless region
|
42
|
-
region
|
34
|
+
def default_aws_region
|
35
|
+
resolve_region(@node) || default_region
|
43
36
|
end
|
44
37
|
end
|
45
38
|
end
|
@@ -3,11 +3,12 @@ module Kontena::Plugin::Aws::Nodes
|
|
3
3
|
include Kontena::Cli::Common
|
4
4
|
include Kontena::Cli::GridOptions
|
5
5
|
include Kontena::Plugin::Aws::Prompts
|
6
|
+
prepend Kontena::Plugin::Aws::Prompts::Common::Defaults
|
6
7
|
|
7
8
|
parameter "[NAME]", "Node name"
|
8
9
|
option "--access-key", "ACCESS_KEY", "AWS access key ID", environment_variable: "AWS_ACCESS_KEY_ID"
|
9
10
|
option "--secret-key", "SECRET_KEY", "AWS secret access key", environment_variable: "AWS_SECRET_ACCESS_KEY"
|
10
|
-
option "--region", "REGION", "EC2 Region (default: node's region)", environment_variable: "AWS_REGION"
|
11
|
+
option "--region", "REGION", "EC2 Region (default: node's region)", environment_variable: "AWS_REGION", attribute_name: :aws_region
|
11
12
|
option "--force", :flag, "Force remove", default: false, attribute_name: :forced
|
12
13
|
option "--aws-bundled-cert", :flag, "Use CA certificate bundled in AWS SDK", default: false
|
13
14
|
|
@@ -17,33 +18,25 @@ module Kontena::Plugin::Aws::Nodes
|
|
17
18
|
require_current_grid
|
18
19
|
|
19
20
|
node_name = self.name || ask_node
|
20
|
-
node = client.get("nodes/#{current_grid}/#{node_name}")
|
21
|
-
aws_access_key = ask_aws_access_key
|
22
|
-
aws_secret_key = ask_aws_secret_key
|
23
|
-
aws_region = self.region || resolve_or_ask_region(node, aws_access_key, aws_secret_key)
|
21
|
+
@node = client.get("nodes/#{current_grid}/#{node_name}")
|
24
22
|
|
25
23
|
confirm_command(node_name) unless forced?
|
26
24
|
require_relative '../../../machine/aws'
|
27
25
|
Aws.use_bundled_cert! if aws_bundled_cert?
|
28
26
|
|
29
27
|
grid = client.get("grids/#{current_grid}")
|
30
|
-
destroyer = destroyer(aws_access_key, aws_secret_key, aws_region)
|
31
28
|
destroyer.run!(grid, node_name)
|
32
29
|
rescue Seahorse::Client::NetworkingError => ex
|
33
30
|
raise ex unless ex.message.match(/certificate verify failed/)
|
34
31
|
exit_with_error Kontena::Machine::Aws.ssl_fail_message(aws_bundled_cert?)
|
35
32
|
end
|
36
33
|
|
37
|
-
def destroyer
|
38
|
-
Kontena::Machine::Aws::NodeDestroyer.new(client, access_key, secret_key,
|
34
|
+
def destroyer
|
35
|
+
Kontena::Machine::Aws::NodeDestroyer.new(client, access_key, secret_key, aws_region)
|
39
36
|
end
|
40
37
|
|
41
|
-
def
|
42
|
-
|
43
|
-
region = region_label.split('=').last
|
44
|
-
end
|
45
|
-
region = ask_aws_region(access_key, secret_key) unless region
|
46
|
-
region
|
38
|
+
def default_aws_region
|
39
|
+
resolve_region(@node) || default_region
|
47
40
|
end
|
48
41
|
end
|
49
42
|
end
|
@@ -1,130 +1,153 @@
|
|
1
1
|
require 'aws-sdk'
|
2
2
|
module Kontena::Plugin::Aws::Prompts
|
3
3
|
|
4
|
-
def
|
5
|
-
|
6
|
-
prompt.ask('AWS access key: ', echo: false)
|
7
|
-
else
|
8
|
-
self.access_key
|
9
|
-
end
|
4
|
+
def aws_client
|
5
|
+
@aws_client ||= Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: region)
|
10
6
|
end
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
module Common
|
9
|
+
def self.included(base)
|
10
|
+
base.prepend Defaults
|
11
|
+
base.option "--access-key", "ACCESS_KEY", "AWS access key ID", environment_variable: "AWS_ACCESS_KEY_ID"
|
12
|
+
base.option "--secret-key", "SECRET_KEY", "AWS secret access key", environment_variable: "AWS_SECRET_ACCESS_KEY"
|
13
|
+
base.option "--key-pair", "KEY_PAIR", "EC2 key pair name"
|
14
|
+
base.option "--region", "REGION", "EC2 Region", environment_variable: "AWS_REGION"
|
15
|
+
base.option "--ssh-public-key", "[PATH]", "SSH public key file path"
|
16
|
+
base.option "--zone", "ZONE", "EC2 Availability Zone (a,b,c,d,e)"
|
17
|
+
base.option "--type", "SIZE", "Instance type"
|
18
|
+
base.option "--vpc-id", "VPC ID", "Virtual Private Cloud (VPC) ID (default: default vpc)"
|
19
|
+
base.option "--storage", "STORAGE", "Storage size (GiB)"
|
20
|
+
base.option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into (default: first subnet in vpc/az)"
|
21
|
+
base.option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
|
22
|
+
base.option "--[no-]associate-public-ip-address", :flag, "Whether to associated public IP in case the VPC defaults to not doing it", default: true, attribute_name: :associate_public_ip
|
23
|
+
base.option "--security-groups", "SECURITY GROUPS", "Comma separated list of security groups (names) where the new instance will be attached (default: create grid specific group if not already existing)"
|
24
|
+
base.option "--aws-bundled-cert", :flag, "Use CA certificate bundled in AWS SDK", default: false do |bundle|
|
25
|
+
Aws.use_bundled_cert! if bundle
|
26
|
+
end
|
17
27
|
end
|
18
|
-
end
|
19
28
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
aws_client = ::Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: region)
|
24
|
-
aws_client.describe_key_pairs.key_pairs.each{ |key_pair|
|
25
|
-
menu.choice key_pair.key_name, key_pair.key_name
|
26
|
-
}
|
29
|
+
module Defaults
|
30
|
+
def default_access_key
|
31
|
+
prompt.ask('AWS access key:', echo: false)
|
27
32
|
end
|
28
|
-
else
|
29
|
-
self.key_pair
|
30
|
-
end
|
31
|
-
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
def default_secret_key
|
35
|
+
prompt.ask('AWS secret key:', echo: false)
|
36
|
+
end
|
37
|
+
|
38
|
+
CREATE_KEYPAIR_TEXT = 'Create new key pair'
|
39
|
+
DummyPair = Struct.new(:key_name)
|
40
|
+
|
41
|
+
def default_key_pair
|
42
|
+
key_pairs = aws_client.describe_key_pairs.key_pairs
|
43
|
+
if key_pairs.empty?
|
44
|
+
import_key_pair
|
45
|
+
else
|
46
|
+
key_pairs << DummyPair.new(CREATE_KEYPAIR_TEXT)
|
47
|
+
answer = prompt.select("Choose EC2 key pair:") do |menu|
|
48
|
+
key_pairs.each do |key_pair|
|
49
|
+
menu.choice key_pair.key_name, key_pair.key_name
|
50
|
+
end
|
42
51
|
end
|
43
|
-
|
44
|
-
|
52
|
+
answer == CREATE_KEYPAIR_TEXT ? import_key_pair : answer
|
53
|
+
end
|
45
54
|
end
|
46
|
-
else
|
47
|
-
self.region
|
48
|
-
end
|
49
|
-
end
|
50
55
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
DEFAULT_SSH_KEY_PATH = File.join(Dir.home, '.ssh', 'id_rsa.pub')
|
57
|
+
|
58
|
+
def import_key_pair
|
59
|
+
if ssh_public_key
|
60
|
+
public_key = File.read(ssh_public_key)
|
61
|
+
else
|
62
|
+
public_key = prompt.ask('SSH public key: (enter a ssh key in OpenSSH format "ssh-xxx xxxxx key_name")', default: File.exist?(DEFAULT_SSH_KEY_PATH) ? File.read(DEFAULT_SSH_KEY_PATH).strip : '') do |q|
|
63
|
+
q.validate /^ssh-rsa \S+ \S+$/
|
58
64
|
end
|
59
|
-
|
65
|
+
key_name = public_key[/\A\S+\s+\S+\s+(\S+)\z/, 1]
|
66
|
+
end
|
67
|
+
|
68
|
+
prompt.yes?("Import public key '#{key_name}' to AWS?") || exit_with_error('Aborted')
|
69
|
+
pair = Kontena::Machine::Aws::KeypairProvisioner.new(access_key, secret_key, region).run!(public_key: public_key, keypair_name: key_name)
|
70
|
+
pair.name
|
60
71
|
end
|
61
|
-
else
|
62
|
-
self.zone
|
63
|
-
end
|
64
|
-
end
|
65
72
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
prompt.select("Choose
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
}
|
73
|
+
DEFAULT_REGION = 'eu-west-1'
|
74
|
+
|
75
|
+
def default_region_aws_client
|
76
|
+
@default_region_aws_client ||= Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: DEFAULT_REGION)
|
77
|
+
end
|
78
|
+
|
79
|
+
def default_region
|
80
|
+
STDERR.puts("in default_region_common")
|
81
|
+
prompt.select("Choose EC2 region:") do |menu|
|
82
|
+
i = 1
|
83
|
+
default_region_aws_client.describe_regions.regions.sort_by(&:region_name).each do |region|
|
84
|
+
menu.choice region.region_name, region.region_name
|
85
|
+
menu.default(i) if region.region_name == DEFAULT_REGION
|
86
|
+
i += 1
|
87
|
+
end
|
82
88
|
end
|
83
89
|
end
|
84
|
-
else
|
85
|
-
self.vpc_id
|
86
|
-
end
|
87
|
-
end
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
91
|
+
def default_zone
|
92
|
+
prompt.select("Choose EC2 Availability Zone:") do |menu|
|
93
|
+
aws_client.describe_availability_zones.availability_zones.sort_by(&:zone_name).select { |z| z.state == 'available' }.each do |zone|
|
94
|
+
menu.choice zone.zone_name, zone.zone_name.sub(zone.region_name, '')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
DEFAULT_INSTANCE_TYPE = 't2.small'
|
100
|
+
|
101
|
+
def default_type
|
102
|
+
prompt.ask('Instance type:', default: DEFAULT_INSTANCE_TYPE)
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_vpc_id
|
106
|
+
vpcs = aws_client.describe_vpcs.vpcs
|
107
|
+
exit_with_error("Could not find any Virtual Private Cloud (VPC). Please create one in the AWS console first.") if vpcs.size.zero?
|
108
|
+
if vpcs.size == 1 && vpcs.first.state == "available"
|
109
|
+
puts "Using VPC ID #{pastel.cyan(vpcs.first.vpc_id)}"
|
110
|
+
vpcs.first.vpc_id
|
111
|
+
else
|
112
|
+
prompt.select("Choose Virtual Private Cloud (VPC) ID:") do |menu|
|
113
|
+
vpcs.each do |vpc|
|
114
|
+
if vpc.state == 'available'
|
115
|
+
name = vpc.vpc_id
|
116
|
+
name += ' (default)' if vpc.is_default
|
117
|
+
menu.choice name, vpc.vpc_id
|
118
|
+
end
|
106
119
|
end
|
107
|
-
|
120
|
+
end
|
108
121
|
end
|
109
122
|
end
|
110
|
-
else
|
111
|
-
self.subnet_id
|
112
|
-
end
|
113
|
-
end
|
114
123
|
|
115
|
-
|
116
|
-
if self.type.nil?
|
117
|
-
prompt.ask('Instance type: ', default: default)
|
118
|
-
else
|
119
|
-
self.type
|
120
|
-
end
|
121
|
-
end
|
124
|
+
DEFAULT_STORAGE = 30
|
122
125
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
126
|
+
def default_storage
|
127
|
+
prompt.ask('Storage size (GiB):', default: DEFAULT_STORAGE)
|
128
|
+
end
|
129
|
+
|
130
|
+
def default_subnet_id
|
131
|
+
filters = [
|
132
|
+
{ name: "vpc-id", values: [vpc_id] },
|
133
|
+
{ name: "availability-zone", values: [region + zone] }
|
134
|
+
]
|
135
|
+
subnets_result = aws_client.describe_subnets(filters: filters)
|
136
|
+
subnets = subnets_result.subnets.sort_by(&:cidr_block)
|
137
|
+
exit_with_error "Failed to find any subnets" if subnets.empty?
|
138
|
+
if subnets.size == 1 && subnets.first.state == "available"
|
139
|
+
puts "Using Subnet ID #{pastel.cyan(subnets.first.subnet_id)}"
|
140
|
+
subnets.first.subnet_id
|
141
|
+
else
|
142
|
+
prompt.select("Specify subnet to launch instance into:") do |menu|
|
143
|
+
subnets.each do |subnet|
|
144
|
+
if subnet.state == 'available'
|
145
|
+
menu.choice "#{subnet.subnet_id} (#{subnet.cidr_block})", subnet.subnet_id
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
128
151
|
end
|
129
152
|
end
|
130
153
|
|
@@ -135,8 +158,8 @@ module Kontena::Plugin::Aws::Prompts
|
|
135
158
|
n['labels'] && n['labels'].include?('provider=aws'.freeze)
|
136
159
|
}
|
137
160
|
raise "Did not find any nodes with label provider=aws" if nodes.size == 0
|
138
|
-
prompt.select("Select node:
|
139
|
-
nodes.sort_by{|n| n['node_number'] }.reverse.each do |node|
|
161
|
+
prompt.select("Select node:") do |menu|
|
162
|
+
nodes.sort_by{ |n| n['node_number'] }.reverse.each do |node|
|
140
163
|
initial = node['initial_member'] ? '(initial) ' : ''
|
141
164
|
menu.choice "#{node['name']} #{initial}", node['name']
|
142
165
|
end
|
@@ -145,4 +168,13 @@ module Kontena::Plugin::Aws::Prompts
|
|
145
168
|
self.name
|
146
169
|
end
|
147
170
|
end
|
171
|
+
|
172
|
+
def resolve_region(node)
|
173
|
+
STDERR.puts("in resolve_region")
|
174
|
+
return nil if node.nil? || node['labels'].nil?
|
175
|
+
node['labels'].each do |label|
|
176
|
+
tag, value = label.split('=', 2)
|
177
|
+
return value if tag == 'region'
|
178
|
+
end
|
179
|
+
end
|
148
180
|
end
|
data/lib/kontena/plugin/aws.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kontena-plugin-aws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kontena, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kontena-cli
|
@@ -91,12 +91,15 @@ files:
|
|
91
91
|
- lib/kontena/machine/aws/cloudinit.yml
|
92
92
|
- lib/kontena/machine/aws/cloudinit_master.yml
|
93
93
|
- lib/kontena/machine/aws/common.rb
|
94
|
+
- lib/kontena/machine/aws/keypair_provisioner.rb
|
95
|
+
- lib/kontena/machine/aws/master_destroyer.rb
|
94
96
|
- lib/kontena/machine/aws/master_provisioner.rb
|
95
97
|
- lib/kontena/machine/aws/node_destroyer.rb
|
96
98
|
- lib/kontena/machine/aws/node_provisioner.rb
|
97
99
|
- lib/kontena/machine/aws/node_restarter.rb
|
98
100
|
- lib/kontena/plugin/aws.rb
|
99
101
|
- lib/kontena/plugin/aws/master/create_command.rb
|
102
|
+
- lib/kontena/plugin/aws/master/terminate_command.rb
|
100
103
|
- lib/kontena/plugin/aws/master_command.rb
|
101
104
|
- lib/kontena/plugin/aws/node_command.rb
|
102
105
|
- lib/kontena/plugin/aws/nodes/create_command.rb
|
@@ -120,9 +123,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
123
|
version: '0'
|
121
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
125
|
requirements:
|
123
|
-
- - "
|
126
|
+
- - ">"
|
124
127
|
- !ruby/object:Gem::Version
|
125
|
-
version:
|
128
|
+
version: 1.3.1
|
126
129
|
requirements: []
|
127
130
|
rubyforge_project:
|
128
131
|
rubygems_version: 2.4.5
|