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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae87022c9186594dc522a5eced7bbd4758360822
4
- data.tar.gz: eec88a482f9dfcde351b10f1ea324d7f323cb747
3
+ metadata.gz: 944cc0456a0dbe057a86a2662000e2ff622ec8c3
4
+ data.tar.gz: cc12f182b670334d33db3855c8769d9167064850
5
5
  SHA512:
6
- metadata.gz: dce564e74e064e04230e13c21f3072cf86ab545fb1756439fd3165bd61776a6a03e0790f0d2b8f7e53812da399114be16f167bd6200dee915ca352220a821054
7
- data.tar.gz: 529812334bef114118e1990f1a72450dcefe46f391ec4e565d2b0f5d97c7dffa125393047f4cefe4442cb7f688a75a21bd9006b9b99436b5eed756aac34c434b
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
- if opts[:subnet].nil?
43
- subnet = default_subnet(opts[:vpc], region+opts[:zone])
44
- else
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("grids/#{grid['id']}/nodes/#{name}")
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
- subnet = default_subnet(opts[:vpc], region+opts[:zone])
38
- else
39
- subnet = ec2.subnet(opts[:subnet])
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|
@@ -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
- option "--access-key", "ACCESS_KEY", "AWS access key ID", environment_variable: "AWS_ACCESS_KEY_ID"
11
- option "--secret-key", "SECRET_KEY", "AWS secret access key", environment_variable: "AWS_SECRET_ACCESS_KEY"
12
- option "--key-pair", "KEY_PAIR", "EC2 key pair name"
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
- name: name,
47
- type: aws_type,
48
- vpc: aws_vpc_id,
49
- zone: aws_zone,
50
- subnet: aws_subnet_id,
51
- ssl_cert: ssl_cert,
52
- storage: aws_storage,
53
- version: version,
54
- key_pair: aws_key_pair,
55
- vault_secret: vault_secret || SecureRandom.hex(24),
56
- vault_iv: vault_iv || SecureRandom.hex(24),
57
- mongodb_uri: mongodb_uri,
58
- associate_public_ip: associate_public_ip?,
59
- security_groups: security_groups,
60
- initial_admin_code: SecureRandom.hex(16)
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(access_key, secret_key, region)
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
- 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 "--key-pair", "KEY_PAIR", "EC2 Key Pair"
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
- master_uri: api_url,
45
- grid_token: grid['token'],
46
- grid: current_grid,
47
- name: name,
48
- type: aws_type,
49
- vpc: aws_vpc_id,
50
- zone: aws_zone,
51
- subnet: aws_subnet_id,
52
- storage: aws_storage,
53
- version: version,
54
- key_pair: aws_key_pair,
55
- count: aws_count,
56
- associate_public_ip: associate_public_ip?,
57
- security_groups: security_groups
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(client, access_key, secret_key, region)
52
+ def provisioner
76
53
  Kontena::Machine::Aws::NodeProvisioner.new(client, access_key, secret_key, region)
77
54
  end
78
55
 
79
- def ask_instance_count
80
- if self.count.nil?
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(access_key, secret_key, region)
34
- Kontena::Machine::Aws::NodeRestarter.new(access_key, secret_key, region)
30
+ def restarter
31
+ Kontena::Machine::Aws::NodeRestarter.new(access_key, secret_key, aws_region)
35
32
  end
36
33
 
37
- def resolve_or_ask_region(node, access_key, secret_key)
38
- if node['labels'] && region_label = node['labels'].find{ |l| l.split('=').first == 'region' }
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(access_key, secret_key, region)
38
- Kontena::Machine::Aws::NodeDestroyer.new(client, access_key, secret_key, region)
34
+ def destroyer
35
+ Kontena::Machine::Aws::NodeDestroyer.new(client, access_key, secret_key, aws_region)
39
36
  end
40
37
 
41
- def resolve_or_ask_region(node, access_key, secret_key)
42
- if node['labels'] && region_label = node['labels'].find{ |l| l.split('=').first == 'region' }
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 ask_aws_access_key
5
- if self.access_key.nil?
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
- def ask_aws_secret_key
13
- if self.secret_key.nil?
14
- prompt.ask('AWS secret key: ', echo: false)
15
- else
16
- self.secret_key
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
- def ask_aws_key_pair(access_key, secret_key, region)
21
- if self.key_pair.nil?
22
- prompt.select("Choose EC2 key pair: ") do |menu|
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
- def ask_aws_region(access_key, secret_key, default = 'eu-west-1')
34
- if self.region.nil?
35
- prompt.select("Choose EC2 region: ") do |menu|
36
- aws_client = ::Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: 'eu-west-1')
37
- i = 1
38
- aws_client.describe_regions.regions.sort_by{|r| r.region_name }.each{ |region|
39
- menu.choice region.region_name, region.region_name
40
- if region.region_name == default
41
- menu.default i
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
- i += 1
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
- def ask_aws_az(access_key, secret_key, region)
52
- if self.zone.nil?
53
- prompt.select("Choose EC2 Availability Zone: ") do |menu|
54
- aws_client = ::Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: region)
55
- aws_client.describe_availability_zones.availability_zones.sort_by{|r| r.zone_name }.each{ |zone|
56
- if zone.state == 'available'
57
- menu.choice zone.zone_name, zone.zone_name.sub(zone.region_name, '')
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
- def ask_aws_vpc(access_key, secret_key, region)
67
- if self.vpc_id.nil?
68
- aws_client = ::Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: region)
69
- vpcs = aws_client.describe_vpcs.vpcs
70
- return nil if vpcs.size == 0
71
- if vpcs.size == 1 && vpcs.first.state == "available"
72
- vpcs.first.vpc_id
73
- else
74
- prompt.select("Choose Virtual Private Cloud (VPC) ID: ") do |menu|
75
- vpcs.each{ |vpc|
76
- if vpc.state == 'available'
77
- name = vpc.vpc_id
78
- name += ' (default)' if vpc.is_default
79
- menu.choice name, vpc.vpc_id
80
- end
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
- def ask_aws_subnet(access_key, secret_key, region, zone, vpc)
90
- if self.subnet_id.nil?
91
- aws_client = ::Aws::EC2::Client.new(access_key_id: access_key, secret_access_key: secret_key, region: region)
92
- subnets_result = aws_client.describe_subnets(filters: [
93
- { name: "vpc-id", values: [vpc] },
94
- { name: "availability-zone", values: [zone]}
95
- ])
96
- subnets = subnets_result.subnets.sort_by{|s| s.cidr_block}
97
- return nil if subnets.size == 0
98
- if subnets.size == 1 && subnets.first.state == "available"
99
- puts "Using Subnet (#{subnets.first.subnet_id})"
100
- subnets.first.subnet_id
101
- else
102
- prompt.select("Specify subnet to launch instance into: ") do |menu|
103
- subnets.each{ |subnet|
104
- if subnet.state == 'available'
105
- menu.choice "#{subnet.subnet_id} (#{subnet.cidr_block})", subnet.subnet_id
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
- def ask_aws_instance_type(default = 't2.small')
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
- def ask_aws_storage(default = '30')
124
- if self.storage.nil?
125
- prompt.ask('Storage size (GiB): ', default: default)
126
- else
127
- self.storage
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: ") do |menu|
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
@@ -1,7 +1,7 @@
1
1
  module Kontena
2
2
  module Plugin
3
3
  module Aws
4
- VERSION = "0.2.7"
4
+ VERSION = "0.3.0.rc1"
5
5
  end
6
6
  end
7
7
  end
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.2.7
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-05-03 00:00:00.000000000 Z
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: '0'
128
+ version: 1.3.1
126
129
  requirements: []
127
130
  rubyforge_project:
128
131
  rubygems_version: 2.4.5