kontena-cli 0.12.0 → 0.12.1
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/VERSION +1 -1
- data/lib/kontena/cli/grids/common.rb +15 -4
- data/lib/kontena/cli/grids/list_command.rb +1 -1
- data/lib/kontena/cli/master/aws/create_command.rb +7 -7
- data/lib/kontena/cli/master/users/remove_command.rb +22 -0
- data/lib/kontena/cli/master/users_command.rb +3 -1
- data/lib/kontena/cli/nodes/aws/create_command.rb +3 -3
- data/lib/kontena/cli/nodes/aws/terminate_command.rb +1 -1
- data/lib/kontena/cli/services/containers_command.rb +1 -1
- data/lib/kontena/cli/vault/list_command.rb +4 -2
- data/lib/kontena/machine/aws.rb +3 -3
- data/lib/kontena/machine/aws/common.rb +17 -1
- data/lib/kontena/machine/aws/master_provisioner.rb +85 -64
- data/lib/kontena/machine/aws/node_destroyer.rb +17 -5
- data/lib/kontena/machine/aws/node_provisioner.rb +103 -63
- data/lib/kontena/machine/cert_helper.rb +39 -0
- data/spec/kontena/cli/master/users/remove_command_spec.rb +28 -0
- data/spec/kontena/cli/services/containers_command_spec.rb +48 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4c7616444c2dfd31cc66c625d49168d8ab8623d
|
4
|
+
data.tar.gz: f28c78f100bed2695c99b12d5ed757dfd0e4eaa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b594019efe570386ebbf644888636a28f31ed88c0fe01425322dce0057fd232f1707d70e82899aefac3576a179ebccc87726a4b90e53a5c473bc354320947dcb
|
7
|
+
data.tar.gz: 9202690e4f3cff809576272a11108351266b97c421c189fbfe742c4f7d0ccc1c90e143a182f754c09a586f37a4f1a23a33724ea8f848352f0167fce50045c06f
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.12.
|
1
|
+
0.12.1
|
@@ -21,10 +21,8 @@ module Kontena::Cli::Grids
|
|
21
21
|
puts " load: #{(loads[:'1m'] || 0.0).round(2)} #{(loads[:'5m'] || 0.0).round(2)} #{(loads[:'15m'] || 0.0).round(2)}"
|
22
22
|
|
23
23
|
mem_total = nodes.map{|n| n['mem_total'].to_i}.inject(:+)
|
24
|
-
|
25
|
-
|
26
|
-
}.inject(:+)
|
27
|
-
puts " memory: #{to_gigabytes(mem_wired)} of #{to_gigabytes(mem_total)} GB"
|
24
|
+
mem_used = calculate_mem_used(nodes)
|
25
|
+
puts " memory: #{to_gigabytes(mem_used)} of #{to_gigabytes(mem_total)} GB"
|
28
26
|
|
29
27
|
total_fs = calculate_filesystem_stats(nodes)
|
30
28
|
puts " filesystem: #{to_gigabytes(total_fs['used'])} of #{to_gigabytes(total_fs['total'])} GB"
|
@@ -55,6 +53,19 @@ module Kontena::Cli::Grids
|
|
55
53
|
loads
|
56
54
|
end
|
57
55
|
|
56
|
+
# @param [Array<Hash>] nodes
|
57
|
+
# @return [Float]
|
58
|
+
def calculate_mem_used(nodes)
|
59
|
+
nodes.map{|n|
|
60
|
+
mem = n.dig('resource_usage', 'memory')
|
61
|
+
if mem
|
62
|
+
mem['used'] - (mem['cached'] + mem['buffers'])
|
63
|
+
else
|
64
|
+
0.0
|
65
|
+
end
|
66
|
+
}.inject(:+)
|
67
|
+
end
|
68
|
+
|
58
69
|
# @param [Array<Hash>] nodes
|
59
70
|
# @return [Hash]
|
60
71
|
def calculate_filesystem_stats(nodes)
|
@@ -9,7 +9,7 @@ module Kontena::Cli::Grids
|
|
9
9
|
require_api_url
|
10
10
|
|
11
11
|
if grids['grids'].size == 0
|
12
|
-
puts "You don't have any grids yet. Create first one with 'kontena
|
12
|
+
puts "You don't have any grids yet. Create first one with 'kontena grid create' command".colorize(:yellow)
|
13
13
|
end
|
14
14
|
|
15
15
|
puts '%-30.30s %-8s %-12s %-10s' % ['Name', 'Nodes', 'Services', 'Users']
|
@@ -6,18 +6,18 @@ module Kontena::Cli::Master::Aws
|
|
6
6
|
|
7
7
|
option "--access-key", "ACCESS_KEY", "AWS access key ID", required: true
|
8
8
|
option "--secret-key", "SECRET_KEY", "AWS secret key", required: true
|
9
|
-
option "--key-pair", "KEY_PAIR", "EC2
|
10
|
-
option "--ssl-cert", "SSL CERT", "SSL certificate file"
|
9
|
+
option "--key-pair", "KEY_PAIR", "EC2 key pair name", required: true
|
10
|
+
option "--ssl-cert", "SSL CERT", "SSL certificate file (default: generate self-signed cert)"
|
11
11
|
option "--region", "REGION", "EC2 Region", default: 'eu-west-1'
|
12
12
|
option "--zone", "ZONE", "EC2 Availability Zone", default: 'a'
|
13
|
-
option "--vpc-id", "VPC ID", "Virtual Private Cloud (VPC) ID"
|
14
|
-
option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into"
|
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 from vpc/az)"
|
15
15
|
option "--type", "SIZE", "Instance type", default: 't2.small'
|
16
16
|
option "--storage", "STORAGE", "Storage size (GiB)", default: '30'
|
17
|
-
option "--vault-secret", "VAULT_SECRET", "Secret key for Vault"
|
18
|
-
option "--vault-iv", "VAULT_IV", "Initialization vector for Vault"
|
17
|
+
option "--vault-secret", "VAULT_SECRET", "Secret key for Vault (default: generate random secret)"
|
18
|
+
option "--vault-iv", "VAULT_IV", "Initialization vector for Vault (default: generate random iv)"
|
19
19
|
option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
|
20
|
-
option "--auth-provider-url", "AUTH_PROVIDER_URL", "Define authentication provider url"
|
20
|
+
option "--auth-provider-url", "AUTH_PROVIDER_URL", "Define authentication provider url (optional)"
|
21
21
|
|
22
22
|
def execute
|
23
23
|
require 'kontena/machine/aws'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative '../../common'
|
2
|
+
|
3
|
+
module Kontena::Cli::Master::Users
|
4
|
+
class RemoveCommand < Clamp::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
|
7
|
+
parameter "EMAIL ...", "List of emails"
|
8
|
+
|
9
|
+
def execute
|
10
|
+
require_api_url
|
11
|
+
token = require_token
|
12
|
+
email_list.each do |email|
|
13
|
+
begin
|
14
|
+
client(token).delete("users/#{email}")
|
15
|
+
rescue => exc
|
16
|
+
STDERR.puts "Failed to remove user #{email}".colorize(:red)
|
17
|
+
STDERR.puts exc.message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Kontena::Cli::Master
|
2
2
|
|
3
|
-
require_relative 'users/add_role_command'
|
4
3
|
require_relative 'users/invite_command'
|
4
|
+
require_relative 'users/remove_command'
|
5
5
|
require_relative 'users/list_command'
|
6
|
+
require_relative 'users/add_role_command'
|
6
7
|
require_relative 'users/remove_role_command'
|
7
8
|
|
8
9
|
class UsersCommand < Clamp::Command
|
9
10
|
subcommand "invite", "Invite user to Kontena Master", Users::InviteCommand
|
11
|
+
subcommand ["remove", "rm"], "Remove user from Kontena Master", Users::RemoveCommand
|
10
12
|
subcommand ["list", "ls"], "List users", Users::ListCommand
|
11
13
|
subcommand "add-role", "Add role to user", Users::AddRoleCommand
|
12
14
|
subcommand "remove-role", "Remove role from user", Users::RemoveRoleCommand
|
@@ -6,11 +6,11 @@ module Kontena::Cli::Nodes::Aws
|
|
6
6
|
parameter "[NAME]", "Node name"
|
7
7
|
option "--access-key", "ACCESS_KEY", "AWS access key ID", required: true
|
8
8
|
option "--secret-key", "SECRET_KEY", "AWS secret key", required: true
|
9
|
+
option "--key-pair", "KEY_PAIR", "EC2 Key Pair", required: true
|
9
10
|
option "--region", "REGION", "EC2 Region", default: 'eu-west-1'
|
10
11
|
option "--zone", "ZONE", "EC2 Availability Zone", default: 'a'
|
11
|
-
option "--vpc-id", "VPC ID", "Virtual Private Cloud (VPC) ID"
|
12
|
-
option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into"
|
13
|
-
option "--key-pair", "KEY_PAIR", "EC2 Key Pair", required: true
|
12
|
+
option "--vpc-id", "VPC ID", "Virtual Private Cloud (VPC) ID (default: default vpc)"
|
13
|
+
option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into (default: first subnet in vpc/az)"
|
14
14
|
option "--type", "SIZE", "Instance type", default: 't2.small'
|
15
15
|
option "--storage", "STORAGE", "Storage size (GiB)", default: '30'
|
16
16
|
option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
|
@@ -6,7 +6,7 @@ module Kontena::Cli::Nodes::Aws
|
|
6
6
|
parameter "NAME", "Node name"
|
7
7
|
option "--access-key", "ACCESS_KEY", "AWS access key ID", required: true
|
8
8
|
option "--secret-key", "SECRET_KEY", "AWS secret key", required: true
|
9
|
-
option "--region", "REGION", "EC2 Region",
|
9
|
+
option "--region", "REGION", "EC2 Region", default: 'eu-west-1'
|
10
10
|
|
11
11
|
def execute
|
12
12
|
require_api_url
|
@@ -18,7 +18,7 @@ module Kontena::Cli::Services
|
|
18
18
|
puts " rev: #{container['deploy_rev']}"
|
19
19
|
puts " node: #{container['node']['name']}"
|
20
20
|
puts " dns: #{container['name']}.#{current_grid}.kontena.local"
|
21
|
-
puts " ip: #{container['overlay_cidr'].split('/')[0]}"
|
21
|
+
puts " ip: #{container['overlay_cidr'].to_s.split('/')[0]}"
|
22
22
|
puts " public ip: #{container['node']['public_ip']}"
|
23
23
|
if container['status'] == 'unknown'
|
24
24
|
puts " status: #{container['status'].colorize(:yellow)}"
|
@@ -7,9 +7,11 @@ module Kontena::Cli::Vault
|
|
7
7
|
require_api_url
|
8
8
|
token = require_token
|
9
9
|
result = client(token).get("grids/#{current_grid}/secrets")
|
10
|
-
|
10
|
+
|
11
|
+
column_width_paddings = '%-54s %-25.25s'
|
12
|
+
puts column_width_paddings % ['NAME', 'CREATED AT']
|
11
13
|
result['secrets'].each do |secret|
|
12
|
-
puts
|
14
|
+
puts column_width_paddings % [secret['name'], secret['created_at']]
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
data/lib/kontena/machine/aws.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
begin
|
2
|
-
require "
|
2
|
+
require "aws-sdk"
|
3
3
|
rescue LoadError
|
4
4
|
puts "It seems that you don't have gem for AWS API installed."
|
5
|
-
puts "Install it using: gem install
|
5
|
+
puts "Install it using: gem install aws-sdk"
|
6
6
|
exit 1
|
7
7
|
end
|
8
8
|
|
9
9
|
require_relative 'random_name'
|
10
|
+
require_relative 'cert_helper'
|
10
11
|
require_relative 'aws/master_provisioner'
|
11
12
|
require_relative 'aws/node_provisioner'
|
12
13
|
require_relative 'aws/node_destroyer'
|
13
|
-
|
@@ -20,7 +20,23 @@ module Kontena
|
|
20
20
|
}
|
21
21
|
images[region]
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
|
+
# @param [String] vpc_id
|
25
|
+
# @param [String] zone
|
26
|
+
# @return [Aws::EC2::Types::Subnet, NilClass]
|
27
|
+
def default_subnet(vpc_id, zone)
|
28
|
+
ec2.subnets({
|
29
|
+
filters: [
|
30
|
+
{name: "vpc-id", values: [vpc_id]},
|
31
|
+
{name: "availability-zone", values: [zone]}
|
32
|
+
]
|
33
|
+
}).first
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Aws::EC2::Types::Vpc, NilClass]
|
37
|
+
def default_vpc
|
38
|
+
ec2.vpcs({filters: [{name: "is-default", values: ["true"]}]}).first
|
39
|
+
end
|
24
40
|
end
|
25
41
|
end
|
26
42
|
end
|
@@ -10,35 +10,37 @@ module Kontena
|
|
10
10
|
class MasterProvisioner
|
11
11
|
include RandomName
|
12
12
|
include Common
|
13
|
-
|
13
|
+
include Machine::CertHelper
|
14
|
+
attr_reader :ec2, :http_client, :region
|
14
15
|
|
15
16
|
# @param [String] access_key_id aws_access_key_id
|
16
17
|
# @param [String] secret_key aws_secret_access_key
|
17
18
|
# @param [String] region
|
18
19
|
def initialize(access_key_id, secret_key, region)
|
19
|
-
@
|
20
|
-
:
|
21
|
-
:aws_access_key_id => access_key_id,
|
22
|
-
:aws_secret_access_key => secret_key,
|
23
|
-
:region => region
|
20
|
+
@ec2 = ::Aws::EC2::Resource.new(
|
21
|
+
region: region, credentials: ::Aws::Credentials.new(access_key_id, secret_key)
|
24
22
|
)
|
25
23
|
end
|
26
24
|
|
27
25
|
# @param [Hash] opts
|
28
26
|
def run!(opts)
|
27
|
+
ssl_cert = nil
|
29
28
|
if opts[:ssl_cert]
|
30
29
|
abort('Invalid ssl cert') unless File.exists?(File.expand_path(opts[:ssl_cert]))
|
31
30
|
ssl_cert = File.read(File.expand_path(opts[:ssl_cert]))
|
31
|
+
else
|
32
|
+
ShellSpinner "Generating self-signed SSL certificate" do
|
33
|
+
ssl_cert = generate_self_signed_cert
|
34
|
+
end
|
32
35
|
end
|
33
36
|
|
34
|
-
ami = resolve_ami(
|
37
|
+
ami = resolve_ami(region)
|
35
38
|
abort('No valid AMI found for region') unless ami
|
36
|
-
opts[:vpc] = default_vpc.
|
39
|
+
opts[:vpc] = default_vpc.vpc_id unless opts[:vpc]
|
37
40
|
if opts[:subnet].nil?
|
38
|
-
subnet = default_subnet(opts[:vpc],
|
39
|
-
opts[:subnet] = subnet.subnet_id
|
41
|
+
subnet = default_subnet(opts[:vpc], region+opts[:zone])
|
40
42
|
else
|
41
|
-
subnet =
|
43
|
+
subnet = ec2.subnet(opts[:subnet])
|
42
44
|
end
|
43
45
|
userdata_vars = {
|
44
46
|
ssl_cert: ssl_cert,
|
@@ -50,41 +52,39 @@ module Kontena
|
|
50
52
|
|
51
53
|
security_group = ensure_security_group(opts[:vpc])
|
52
54
|
name = generate_name
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
55
|
+
ec2_instance = ec2.create_instances({
|
56
|
+
image_id: ami,
|
57
|
+
min_count: 1,
|
58
|
+
max_count: 1,
|
59
|
+
instance_type: opts[:type],
|
60
|
+
security_group_ids: [security_group.group_id],
|
61
|
+
key_name: opts[:key_pair],
|
62
|
+
subnet_id: subnet.subnet_id,
|
63
|
+
user_data: Base64.encode64(user_data(userdata_vars)),
|
64
|
+
block_device_mappings: [
|
65
|
+
{
|
66
|
+
device_name: '/dev/xvda',
|
67
|
+
virtual_name: 'Root',
|
68
|
+
ebs: {
|
69
|
+
volume_size: opts[:storage],
|
70
|
+
volume_type: 'gp2'
|
71
|
+
}
|
72
|
+
}
|
73
|
+
]
|
74
|
+
}).first
|
75
|
+
ec2_instance.create_tags({
|
76
|
+
tags: [
|
77
|
+
{key: 'Name', value: name}
|
78
|
+
]
|
79
|
+
})
|
75
80
|
ShellSpinner "Creating AWS instance #{name.colorize(:cyan)} " do
|
76
|
-
|
77
|
-
end
|
78
|
-
if opts[:ssl_cert]
|
79
|
-
master_url = "https://#{instance.public_ip_address}"
|
80
|
-
else
|
81
|
-
master_url = "http://#{instance.public_ip_address}"
|
81
|
+
sleep 5 until ec2_instance.reload.state.name == 'running'
|
82
82
|
end
|
83
|
+
master_url = "https://#{ec2_instance.public_ip_address}"
|
83
84
|
Excon.defaults[:ssl_verify_peer] = false
|
84
|
-
|
85
|
-
|
85
|
+
http_client = Excon.new(master_url, :connect_timeout => 10)
|
86
86
|
ShellSpinner "Waiting for #{name.colorize(:cyan)} to start" do
|
87
|
-
sleep 5 until master_running?
|
87
|
+
sleep 5 until master_running?(http_client)
|
88
88
|
end
|
89
89
|
|
90
90
|
puts "Kontena Master is now running at #{master_url}"
|
@@ -92,38 +92,57 @@ module Kontena
|
|
92
92
|
end
|
93
93
|
|
94
94
|
##
|
95
|
-
# @param [String]
|
96
|
-
# @return
|
95
|
+
# @param [String] vpc_id
|
96
|
+
# @return [Aws::EC2::SecurityGroup]
|
97
97
|
def ensure_security_group(vpc_id)
|
98
98
|
group_name = "kontena_master"
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
99
|
+
sg = ec2.security_groups({
|
100
|
+
filters: [
|
101
|
+
{name: 'group-name', values: [group_name]},
|
102
|
+
{name: 'vpc-id', values: [vpc_id]}
|
103
|
+
]
|
104
|
+
}).first
|
105
|
+
unless sg
|
106
|
+
ShellSpinner "Creating AWS security group" do
|
107
|
+
sg = create_security_group(group_name, vpc_id)
|
108
|
+
end
|
103
109
|
end
|
110
|
+
sg
|
104
111
|
end
|
105
112
|
|
106
113
|
##
|
107
114
|
# creates security_group and authorizes default port ranges
|
108
115
|
#
|
109
116
|
# @param [String] name
|
110
|
-
# @
|
117
|
+
# @param [String, NilClass] vpc_id
|
118
|
+
# @return Aws::EC2::SecurityGroup
|
111
119
|
def create_security_group(name, vpc_id = nil)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
120
|
+
sg = ec2.create_security_group({
|
121
|
+
group_name: name,
|
122
|
+
description: "Kontena Master",
|
123
|
+
vpc_id: vpc_id
|
124
|
+
})
|
125
|
+
|
126
|
+
sg.authorize_ingress({
|
127
|
+
ip_protocol: 'tcp',
|
128
|
+
from_port: 443,
|
129
|
+
to_port: 443,
|
130
|
+
cidr_ip: '0.0.0.0/0'
|
131
|
+
})
|
132
|
+
|
133
|
+
sg.authorize_ingress({
|
134
|
+
ip_protocol: 'tcp',
|
135
|
+
from_port: 22,
|
136
|
+
to_port: 22,
|
137
|
+
cidr_ip: '0.0.0.0/0'
|
138
|
+
})
|
139
|
+
|
140
|
+
sg
|
119
141
|
end
|
120
142
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def default_vpc
|
126
|
-
client.vpcs.all('isDefault' => true).first
|
143
|
+
# @return [String]
|
144
|
+
def region
|
145
|
+
ec2.client.config.region
|
127
146
|
end
|
128
147
|
|
129
148
|
def user_data(vars)
|
@@ -135,14 +154,16 @@ module Kontena
|
|
135
154
|
"kontena-master-#{super}-#{rand(1..99)}"
|
136
155
|
end
|
137
156
|
|
138
|
-
def master_running?
|
157
|
+
def master_running?(http_client)
|
139
158
|
http_client.get(path: '/').status == 200
|
140
159
|
rescue
|
141
160
|
false
|
142
161
|
end
|
143
162
|
|
144
163
|
def erb(template, vars)
|
145
|
-
ERB.new(template).result(
|
164
|
+
ERB.new(template).result(
|
165
|
+
OpenStruct.new(vars).instance_eval { binding }
|
166
|
+
)
|
146
167
|
end
|
147
168
|
end
|
148
169
|
end
|
@@ -5,7 +5,7 @@ module Kontena
|
|
5
5
|
module Aws
|
6
6
|
class NodeDestroyer
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :ec2, :api_client
|
9
9
|
|
10
10
|
# @param [Kontena::Client] api_client Kontena api client
|
11
11
|
# @param [String] access_key_id aws_access_key_id
|
@@ -13,15 +13,27 @@ module Kontena
|
|
13
13
|
# @param [String] region
|
14
14
|
def initialize(api_client, access_key_id, secret_key, region = 'eu-west-1')
|
15
15
|
@api_client = api_client
|
16
|
-
@
|
16
|
+
@ec2 = ::Aws::EC2::Resource.new(
|
17
|
+
region: region,
|
18
|
+
credentials: ::Aws::Credentials.new(access_key_id, secret_key)
|
19
|
+
)
|
17
20
|
end
|
18
21
|
|
19
22
|
def run!(grid, name)
|
20
|
-
|
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
|
21
31
|
if instance
|
22
32
|
ShellSpinner "Terminating AWS instance #{name.colorize(:cyan)} " do
|
23
|
-
instance.
|
24
|
-
|
33
|
+
instance.terminate
|
34
|
+
until instance.reload.state.name.to_s == 'terminated'
|
35
|
+
sleep 2
|
36
|
+
end
|
25
37
|
end
|
26
38
|
else
|
27
39
|
abort "Cannot find instance #{name.colorize(:cyan)} in AWS"
|
@@ -11,7 +11,7 @@ module Kontena
|
|
11
11
|
include RandomName
|
12
12
|
include Common
|
13
13
|
|
14
|
-
attr_reader :
|
14
|
+
attr_reader :ec2, :api_client
|
15
15
|
|
16
16
|
# @param [Kontena::Client] api_client Kontena api client
|
17
17
|
# @param [String] access_key_id aws_access_key_id
|
@@ -19,106 +19,141 @@ module Kontena
|
|
19
19
|
# @param [String] region
|
20
20
|
def initialize(api_client, access_key_id, secret_key, region)
|
21
21
|
@api_client = api_client
|
22
|
-
@
|
23
|
-
:
|
24
|
-
:aws_secret_access_key => secret_key, :region => region
|
22
|
+
@ec2 = ::Aws::EC2::Resource.new(
|
23
|
+
region: region, credentials: ::Aws::Credentials.new(access_key_id, secret_key)
|
25
24
|
)
|
26
25
|
end
|
27
26
|
|
28
27
|
# @param [Hash] opts
|
29
28
|
def run!(opts)
|
30
|
-
ami = resolve_ami(
|
29
|
+
ami = resolve_ami(region)
|
31
30
|
abort('No valid AMI found for region') unless ami
|
32
31
|
|
32
|
+
opts[:vpc] = default_vpc.vpc_id unless opts[:vpc]
|
33
|
+
|
33
34
|
security_group = ensure_security_group(opts[:grid], opts[:vpc])
|
34
35
|
name = opts[:name ] || generate_name
|
35
36
|
|
36
|
-
|
37
|
+
|
37
38
|
if opts[:subnet].nil?
|
38
|
-
subnet = default_subnet(opts[:vpc],
|
39
|
-
opts[:subnet] = subnet.subnet_id
|
39
|
+
subnet = default_subnet(opts[:vpc], region+opts[:zone])
|
40
40
|
else
|
41
|
-
subnet =
|
41
|
+
subnet = ec2.subnet(opts[:subnet])
|
42
42
|
end
|
43
43
|
dns_server = aws_dns_supported?(opts[:vpc]) ? '169.254.169.253' : '8.8.8.8'
|
44
44
|
userdata_vars = {
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
name: name,
|
46
|
+
version: opts[:version],
|
47
|
+
master_uri: opts[:master_uri],
|
48
|
+
grid_token: opts[:grid_token],
|
49
|
+
dns_server: dns_server
|
50
50
|
}
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
52
|
+
ec2_instance = ec2.create_instances({
|
53
|
+
image_id: ami,
|
54
|
+
min_count: 1,
|
55
|
+
max_count: 1,
|
56
|
+
instance_type: opts[:type],
|
57
|
+
security_group_ids: [security_group.group_id],
|
58
|
+
key_name: opts[:key_pair],
|
59
|
+
subnet_id: subnet.subnet_id,
|
60
|
+
user_data: Base64.encode64(user_data(userdata_vars)),
|
61
|
+
block_device_mappings: [
|
62
|
+
{
|
63
|
+
device_name: '/dev/xvda',
|
64
|
+
virtual_name: 'Root',
|
65
|
+
ebs: {
|
66
|
+
volume_size: opts[:storage],
|
67
|
+
volume_type: 'gp2'
|
68
|
+
}
|
69
|
+
}
|
70
|
+
]
|
71
|
+
}).first
|
72
|
+
ec2_instance.create_tags({
|
73
|
+
tags: [
|
74
|
+
{key: 'Name', value: name},
|
75
|
+
{key: 'kontena_grid', value: opts[:grid]}
|
76
|
+
]
|
77
|
+
})
|
72
78
|
|
73
|
-
instance = client.servers.get(instance_id)
|
74
79
|
ShellSpinner "Creating AWS instance #{name.colorize(:cyan)} " do
|
75
|
-
|
80
|
+
sleep 5 until ec2_instance.reload.state.name == 'running'
|
76
81
|
end
|
77
|
-
client.create_tags(instance.id, {'Name' => name, 'kontena_grid' => opts[:grid]})
|
78
82
|
node = nil
|
79
83
|
ShellSpinner "Waiting for node #{name.colorize(:cyan)} join to grid #{opts[:grid].colorize(:cyan)} " do
|
80
84
|
sleep 2 until node = instance_exists_in_grid?(opts[:grid], name)
|
81
85
|
end
|
82
|
-
labels = ["region=#{
|
86
|
+
labels = ["region=#{region}", "az=#{opts[:zone]}"]
|
83
87
|
set_labels(node, labels)
|
84
88
|
end
|
85
89
|
|
86
90
|
##
|
87
91
|
# @param [String] grid
|
88
|
-
# @return
|
92
|
+
# @return [Aws::EC2::SecurityGroup]
|
89
93
|
def ensure_security_group(grid, vpc_id)
|
90
94
|
group_name = "kontena_grid_#{grid}"
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
sg = ec2.security_groups({
|
96
|
+
filters: [
|
97
|
+
{name: 'group-name', values: [group_name]},
|
98
|
+
{name: 'vpc-id', values: [vpc_id]}
|
99
|
+
]
|
100
|
+
}).first
|
101
|
+
unless sg
|
102
|
+
ShellSpinner "Creating AWS security group" do
|
103
|
+
sg = create_security_group(group_name, vpc_id)
|
104
|
+
end
|
95
105
|
end
|
106
|
+
sg
|
96
107
|
end
|
97
108
|
|
98
109
|
##
|
99
110
|
# creates security_group and authorizes default port ranges
|
100
111
|
#
|
101
112
|
# @param [String] name
|
102
|
-
# @
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
# @param [String] vpc_id
|
114
|
+
# @return [Aws::EC2::SecurityGroup]
|
115
|
+
def create_security_group(name, vpc_id)
|
116
|
+
sg = ec2.create_security_group({
|
117
|
+
group_name: name,
|
118
|
+
description: "Kontena Grid",
|
119
|
+
vpc_id: vpc_id
|
120
|
+
})
|
121
|
+
|
122
|
+
sg.authorize_ingress({ # SSH
|
123
|
+
ip_protocol: 'tcp', from_port: 22, to_port: 22, cidr_ip: '0.0.0.0/0'
|
124
|
+
})
|
125
|
+
sg.authorize_ingress({ # HTTP
|
126
|
+
ip_protocol: 'tcp', from_port: 80, to_port: 80, cidr_ip: '0.0.0.0/0'
|
127
|
+
})
|
128
|
+
sg.authorize_ingress({ # HTTPS
|
129
|
+
ip_protocol: 'tcp', from_port: 443, to_port: 443, cidr_ip: '0.0.0.0/0'
|
130
|
+
})
|
131
|
+
sg.authorize_ingress({ # OpenVPN
|
132
|
+
ip_protocol: 'udp', from_port: 1194, to_port: 1194, cidr_ip: '0.0.0.0/0'
|
133
|
+
})
|
134
|
+
sg.authorize_ingress({ # Overlay / Weave network
|
135
|
+
ip_permissions: [
|
136
|
+
{
|
137
|
+
from_port: 6783, to_port: 6783, ip_protocol: 'tcp',
|
138
|
+
user_id_group_pairs: [
|
139
|
+
{ group_id: sg.group_id, vpc_id: vpc_id }
|
140
|
+
]
|
141
|
+
},
|
142
|
+
{
|
143
|
+
from_port: 6783, to_port: 6784, ip_protocol: 'udp',
|
144
|
+
user_id_group_pairs: [
|
145
|
+
{ group_id: sg.group_id, vpc_id: vpc_id }
|
146
|
+
]
|
147
|
+
}
|
148
|
+
]
|
149
|
+
})
|
150
|
+
|
151
|
+
sg
|
118
152
|
end
|
119
153
|
|
120
|
-
|
121
|
-
|
154
|
+
# @return [String]
|
155
|
+
def region
|
156
|
+
ec2.client.config.region
|
122
157
|
end
|
123
158
|
|
124
159
|
def user_data(vars)
|
@@ -138,15 +173,20 @@ module Kontena
|
|
138
173
|
ERB.new(template).result(OpenStruct.new(vars).instance_eval { binding })
|
139
174
|
end
|
140
175
|
|
176
|
+
# @param [Hash] node
|
177
|
+
# @param [Array<String>] labels
|
141
178
|
def set_labels(node, labels)
|
142
179
|
data = {}
|
143
180
|
data[:labels] = labels
|
144
181
|
api_client.put("nodes/#{node['id']}", data, {}, {'Kontena-Grid-Token' => node['grid']['token']})
|
145
182
|
end
|
146
183
|
|
184
|
+
# @param [String] vpc_id
|
185
|
+
# @return [Boolean]
|
147
186
|
def aws_dns_supported?(vpc_id)
|
148
|
-
|
149
|
-
response.
|
187
|
+
vpc = ec2.vpc(vpc_id)
|
188
|
+
response = vpc.describe_attribute({attribute: 'enableDnsSupport'})
|
189
|
+
response.enable_dns_support
|
150
190
|
end
|
151
191
|
end
|
152
192
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Kontena
|
4
|
+
module Machine
|
5
|
+
module CertHelper
|
6
|
+
|
7
|
+
def generate_self_signed_cert
|
8
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
9
|
+
public_key = key.public_key
|
10
|
+
|
11
|
+
subject = "/C=FI/O=Test/OU=Test/CN=Test"
|
12
|
+
|
13
|
+
cert = OpenSSL::X509::Certificate.new
|
14
|
+
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
|
15
|
+
cert.not_before = Time.now
|
16
|
+
cert.not_after = Time.now + (10 * 365 * 24 * 60 * 60)
|
17
|
+
cert.public_key = public_key
|
18
|
+
cert.serial = 0x0
|
19
|
+
cert.version = 2
|
20
|
+
|
21
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
22
|
+
ef.subject_certificate = cert
|
23
|
+
ef.issuer_certificate = cert
|
24
|
+
cert.extensions = [
|
25
|
+
ef.create_extension("basicConstraints","CA:TRUE", true),
|
26
|
+
ef.create_extension("subjectKeyIdentifier", "hash")
|
27
|
+
]
|
28
|
+
cert.add_extension ef.create_extension("authorityKeyIdentifier",
|
29
|
+
"keyid:always,issuer:always")
|
30
|
+
|
31
|
+
cert.sign key, OpenSSL::Digest::SHA1.new
|
32
|
+
|
33
|
+
pem = cert.to_pem
|
34
|
+
pem << key.to_s
|
35
|
+
pem
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative "../../../../spec_helper"
|
2
|
+
require 'kontena/cli/master/users_command'
|
3
|
+
require "kontena/cli/master/users/remove_command"
|
4
|
+
|
5
|
+
describe Kontena::Cli::Master::Users::RemoveCommand do
|
6
|
+
|
7
|
+
include ClientHelpers
|
8
|
+
|
9
|
+
describe '#execute' do
|
10
|
+
|
11
|
+
it 'requires api url' do
|
12
|
+
expect(subject).to receive(:require_api_url).once
|
13
|
+
subject.run(['john@domain.com'])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'requires token' do
|
17
|
+
expect(subject).to receive(:require_token).and_return(token)
|
18
|
+
subject.run(['john@domain.com'])
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'sends email to master' do
|
22
|
+
expect(client).to receive(:delete).with(
|
23
|
+
'users/john@domain.com'
|
24
|
+
)
|
25
|
+
subject.run(['john@domain.com'])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative "../../../spec_helper"
|
2
|
+
require "kontena/cli/services/containers_command"
|
3
|
+
|
4
|
+
describe Kontena::Cli::Services::ContainersCommand do
|
5
|
+
|
6
|
+
include ClientHelpers
|
7
|
+
|
8
|
+
describe '#execute' do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
allow(client).to receive(:get).and_return({
|
12
|
+
'containers' => []
|
13
|
+
})
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'requires api url' do
|
17
|
+
expect(subject).to receive(:require_api_url).once
|
18
|
+
subject.run(['service-a'])
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'requires token' do
|
22
|
+
expect(subject).to receive(:require_token).and_return(token)
|
23
|
+
subject.run(['service-a'])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'to not throw on missing "overlay_cidr" property' do
|
27
|
+
allow(client).to receive(:get).and_return({
|
28
|
+
'containers' => [
|
29
|
+
{'id' => "service-a-id", 'node' => {'public_ip' => ""}}
|
30
|
+
]
|
31
|
+
})
|
32
|
+
expect {
|
33
|
+
subject.run(['service-a'])
|
34
|
+
}.to_not raise_error(NoMethodError)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'to not throw on nil "overlay_cidr" property' do
|
38
|
+
allow(client).to receive(:get).and_return({
|
39
|
+
'containers' => [
|
40
|
+
{'id' => "service-a-id", 'node' => {'public_ip' => ""}, 'overlay_cidr' => nil}
|
41
|
+
]
|
42
|
+
})
|
43
|
+
expect {
|
44
|
+
subject.run(['service-a'])
|
45
|
+
}.to_not raise_error(NoMethodError)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kontena-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kontena, Inc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -205,6 +205,7 @@ files:
|
|
205
205
|
- lib/kontena/cli/master/users/add_role_command.rb
|
206
206
|
- lib/kontena/cli/master/users/invite_command.rb
|
207
207
|
- lib/kontena/cli/master/users/list_command.rb
|
208
|
+
- lib/kontena/cli/master/users/remove_command.rb
|
208
209
|
- lib/kontena/cli/master/users/remove_role_command.rb
|
209
210
|
- lib/kontena/cli/master/users_command.rb
|
210
211
|
- lib/kontena/cli/master/vagrant/create_command.rb
|
@@ -305,6 +306,7 @@ files:
|
|
305
306
|
- lib/kontena/machine/azure/master_provisioner.rb
|
306
307
|
- lib/kontena/machine/azure/node_destroyer.rb
|
307
308
|
- lib/kontena/machine/azure/node_provisioner.rb
|
309
|
+
- lib/kontena/machine/cert_helper.rb
|
308
310
|
- lib/kontena/machine/cloud_config/cloudinit.yml
|
309
311
|
- lib/kontena/machine/cloud_config/node_generator.rb
|
310
312
|
- lib/kontena/machine/common.rb
|
@@ -339,9 +341,11 @@ files:
|
|
339
341
|
- spec/kontena/cli/master/use_command_spec.rb
|
340
342
|
- spec/kontena/cli/master/users/add_role_command_spec.rb
|
341
343
|
- spec/kontena/cli/master/users/invite_command_spec.rb
|
344
|
+
- spec/kontena/cli/master/users/remove_command_spec.rb
|
342
345
|
- spec/kontena/cli/master/users/remove_role_command_spec.rb
|
343
346
|
- spec/kontena/cli/register_command_spec.rb
|
344
347
|
- spec/kontena/cli/services/add_secret_command_spec.rb
|
348
|
+
- spec/kontena/cli/services/containers_command_spec.rb
|
345
349
|
- spec/kontena/cli/services/link_command_spec.rb
|
346
350
|
- spec/kontena/cli/services/remove_secret_command_spec.rb
|
347
351
|
- spec/kontena/cli/services/restart_command_spec.rb
|
@@ -391,9 +395,11 @@ test_files:
|
|
391
395
|
- spec/kontena/cli/master/use_command_spec.rb
|
392
396
|
- spec/kontena/cli/master/users/add_role_command_spec.rb
|
393
397
|
- spec/kontena/cli/master/users/invite_command_spec.rb
|
398
|
+
- spec/kontena/cli/master/users/remove_command_spec.rb
|
394
399
|
- spec/kontena/cli/master/users/remove_role_command_spec.rb
|
395
400
|
- spec/kontena/cli/register_command_spec.rb
|
396
401
|
- spec/kontena/cli/services/add_secret_command_spec.rb
|
402
|
+
- spec/kontena/cli/services/containers_command_spec.rb
|
397
403
|
- spec/kontena/cli/services/link_command_spec.rb
|
398
404
|
- spec/kontena/cli/services/remove_secret_command_spec.rb
|
399
405
|
- spec/kontena/cli/services/restart_command_spec.rb
|