kontena-plugin-aws 0.2.7 → 0.3.0.rc1
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/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
|