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 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