kontena-cli 0.13.4 → 0.14.0
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/kontena-cli.gemspec +2 -0
- data/lib/kontena/cli/app_command.rb +2 -0
- data/lib/kontena/cli/apps/common.rb +80 -74
- data/lib/kontena/cli/apps/config_command.rb +29 -0
- data/lib/kontena/cli/apps/deploy_command.rb +12 -81
- data/lib/kontena/cli/apps/docker_helper.rb +3 -3
- data/lib/kontena/cli/apps/init_command.rb +0 -3
- data/lib/kontena/cli/apps/list_command.rb +2 -3
- data/lib/kontena/cli/apps/logs_command.rb +2 -3
- data/lib/kontena/cli/apps/monitor_command.rb +3 -4
- data/lib/kontena/cli/apps/remove_command.rb +4 -4
- data/lib/kontena/cli/apps/restart_command.rb +2 -3
- data/lib/kontena/cli/apps/scale_command.rb +3 -5
- data/lib/kontena/cli/apps/service_generator.rb +123 -0
- data/lib/kontena/cli/apps/service_generator_v2.rb +26 -0
- data/lib/kontena/cli/apps/show_command.rb +1 -2
- data/lib/kontena/cli/apps/start_command.rb +2 -3
- data/lib/kontena/cli/apps/stop_command.rb +2 -3
- data/lib/kontena/cli/apps/yaml/reader.rb +150 -0
- data/lib/kontena/cli/apps/yaml/service_extender.rb +60 -0
- data/lib/kontena/cli/apps/yaml/validations.rb +79 -0
- data/lib/kontena/cli/apps/yaml/validator.rb +55 -0
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +74 -0
- data/lib/kontena/cli/common.rb +23 -0
- data/lib/kontena/cli/etcd/remove_command.rb +2 -0
- data/lib/kontena/cli/grids/remove_command.rb +2 -0
- data/lib/kontena/cli/grids/users/remove_command.rb +3 -0
- data/lib/kontena/cli/master/azure/create_command.rb +0 -2
- data/lib/kontena/cli/master/packet/create_command.rb +42 -0
- data/lib/kontena/cli/master/packet_command.rb +14 -0
- data/lib/kontena/cli/master/upcloud/create_command.rb +39 -0
- data/lib/kontena/cli/master/upcloud_command.rb +13 -0
- data/lib/kontena/cli/master/users/remove_command.rb +3 -0
- data/lib/kontena/cli/master/users/roles/remove_command.rb +2 -0
- data/lib/kontena/cli/master_command.rb +4 -0
- data/lib/kontena/cli/node_command.rb +4 -0
- data/lib/kontena/cli/nodes/azure/create_command.rb +0 -2
- data/lib/kontena/cli/nodes/list_command.rb +4 -8
- data/lib/kontena/cli/nodes/packet/create_command.rb +35 -0
- data/lib/kontena/cli/nodes/packet/restart_command.rb +17 -0
- data/lib/kontena/cli/nodes/packet/terminate_command.rb +20 -0
- data/lib/kontena/cli/nodes/packet_command.rb +15 -0
- data/lib/kontena/cli/nodes/remove_command.rb +2 -0
- data/lib/kontena/cli/nodes/show_command.rb +3 -1
- data/lib/kontena/cli/nodes/upcloud/create_command.rb +33 -0
- data/lib/kontena/cli/nodes/upcloud/restart_command.rb +20 -0
- data/lib/kontena/cli/nodes/upcloud/terminate_command.rb +20 -0
- data/lib/kontena/cli/nodes/upcloud_command.rb +15 -0
- data/lib/kontena/cli/registry/remove_command.rb +3 -0
- data/lib/kontena/cli/services/remove_command.rb +2 -0
- data/lib/kontena/cli/services/services_helper.rb +1 -0
- data/lib/kontena/cli/vault/list_command.rb +2 -0
- data/lib/kontena/cli/vault/read_command.rb +2 -0
- data/lib/kontena/cli/vault/remove_command.rb +4 -0
- data/lib/kontena/cli/vault/update_command.rb +8 -1
- data/lib/kontena/cli/vault/write_command.rb +2 -0
- data/lib/kontena/cli/vpn/remove_command.rb +3 -0
- data/lib/kontena/machine/azure/master_provisioner.rb +2 -2
- data/lib/kontena/machine/azure/node_provisioner.rb +7 -4
- data/lib/kontena/machine/digital_ocean/node_provisioner.rb +1 -1
- data/lib/kontena/machine/packet.rb +17 -0
- data/lib/kontena/machine/packet/cloudinit.yml +66 -0
- data/lib/kontena/machine/packet/cloudinit_master.yml +118 -0
- data/lib/kontena/machine/packet/master_provisioner.rb +93 -0
- data/lib/kontena/machine/packet/node_destroyer.rb +42 -0
- data/lib/kontena/machine/packet/node_provisioner.rb +77 -0
- data/lib/kontena/machine/packet/node_restarter.rb +41 -0
- data/lib/kontena/machine/packet/packet_common.rb +89 -0
- data/lib/kontena/machine/upcloud.rb +9 -0
- data/lib/kontena/machine/upcloud/cloudinit.yml +64 -0
- data/lib/kontena/machine/upcloud/cloudinit_master.yml +118 -0
- data/lib/kontena/machine/upcloud/master_provisioner.rb +136 -0
- data/lib/kontena/machine/upcloud/node_destroyer.rb +82 -0
- data/lib/kontena/machine/upcloud/node_provisioner.rb +119 -0
- data/lib/kontena/machine/upcloud/node_restarter.rb +47 -0
- data/lib/kontena/machine/upcloud/upcloud_common.rb +70 -0
- data/lib/kontena/scripts/completer +8 -3
- data/spec/fixtures/docker-compose_v2.yml +10 -0
- data/spec/fixtures/kontena-invalid.yml +4 -0
- data/spec/fixtures/kontena-with-variables.yml +19 -0
- data/spec/fixtures/kontena.yml +2 -2
- data/spec/fixtures/kontena_v2.yml +35 -0
- data/spec/kontena/cli/app/common_spec.rb +39 -101
- data/spec/kontena/cli/app/deploy_command_spec.rb +37 -388
- data/spec/kontena/cli/app/docker_helper_spec.rb +4 -4
- data/spec/kontena/cli/app/service_generator_spec.rb +374 -0
- data/spec/kontena/cli/app/service_generator_v2_spec.rb +74 -0
- data/spec/kontena/cli/app/yaml/reader_spec.rb +249 -0
- data/spec/kontena/cli/app/yaml/service_extender_spec.rb +104 -0
- data/spec/kontena/cli/app/yaml/validator_spec.rb +263 -0
- data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +309 -0
- data/spec/kontena/cli/common_spec.rb +39 -1
- data/spec/kontena/cli/master/users/remove_command_spec.rb +9 -0
- data/spec/kontena/cli/master/users/roles/remove_command_spec.rb +2 -0
- metadata +86 -2
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'shell-spinner'
|
|
2
|
+
|
|
3
|
+
module Kontena
|
|
4
|
+
module Machine
|
|
5
|
+
module Upcloud
|
|
6
|
+
class NodeDestroyer
|
|
7
|
+
include RandomName
|
|
8
|
+
include UpcloudCommon
|
|
9
|
+
|
|
10
|
+
attr_reader :api_client, :username, :password
|
|
11
|
+
|
|
12
|
+
# @param [Kontena::Client] api_client Kontena api client
|
|
13
|
+
# @param [String] token Upcloud token
|
|
14
|
+
def initialize(api_client, upcloud_username, upcloud_password)
|
|
15
|
+
@api_client = api_client
|
|
16
|
+
@username = upcloud_username
|
|
17
|
+
@password = upcloud_password
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run!(grid, name)
|
|
21
|
+
servers = get('server')
|
|
22
|
+
unless servers && servers.has_key?(:servers)
|
|
23
|
+
abort('Upcloud API error')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
server = servers[:servers][:server].find{|s| s[:hostname] == name}
|
|
27
|
+
|
|
28
|
+
abort "Cannot find node #{name.colorize(:cyan)} in Upcloud" unless server
|
|
29
|
+
|
|
30
|
+
server_data = get("server/#{server[:uuid]}")
|
|
31
|
+
|
|
32
|
+
storage_devices = server_data.fetch(:server, {}).fetch(:storage_devices, {}).fetch(:storage_device, [])
|
|
33
|
+
storage_uuids = storage_devices.map{|s| s[:storage]}
|
|
34
|
+
|
|
35
|
+
abort('No storage devices found for Upcloud node') if storage_uuids.empty?
|
|
36
|
+
|
|
37
|
+
if server
|
|
38
|
+
unless server[:state].eql?('stopped')
|
|
39
|
+
ShellSpinner "Shutting down Upcloud node #{name.colorize(:cyan)} " do
|
|
40
|
+
device_data = post(
|
|
41
|
+
"server/#{server[:uuid]}/stop", body: {
|
|
42
|
+
stop_server: {
|
|
43
|
+
stop_type: 'soft',
|
|
44
|
+
timeout: 120
|
|
45
|
+
}
|
|
46
|
+
}.to_json
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
until device_data && device_data.fetch(:state, nil).to_s.eql?('stopped')
|
|
50
|
+
device_data = get("server/#{server[:uuid]}").fetch(:server, {}) rescue nil
|
|
51
|
+
sleep 5
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
ShellSpinner "Terminating Upcloud node #{name.colorize(:cyan)} " do
|
|
57
|
+
response = delete("server/#{server[:uuid]}")
|
|
58
|
+
abort "Cannot delete node #{name.colorize(:cyan)} in Upcloud" unless response[:success]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
storage_uuids.each do |uuid|
|
|
62
|
+
ShellSpinner "Deleting Upcloud storage device '#{uuid.colorize(:cyan)}' " do
|
|
63
|
+
response = delete("storage/#{uuid}")
|
|
64
|
+
unless response[:success]
|
|
65
|
+
puts "#{"WARNING".colorize(:red)}: Couldn't delete Upcloud storage '#{uuid.colorize(:cyan)}', check manually."
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
abort "Cannot find node #{name.colorize(:cyan)} in Upcloud"
|
|
71
|
+
end
|
|
72
|
+
node = api_client.get("grids/#{grid['id']}/nodes")['nodes'].find{|n| n['name'] == name}
|
|
73
|
+
if node
|
|
74
|
+
ShellSpinner "Removing node #{name.colorize(:cyan)} from grid #{grid['name'].colorize(:cyan)} " do
|
|
75
|
+
api_client.delete("grids/#{grid['id']}/nodes/#{name}")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'erb'
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'shell-spinner'
|
|
5
|
+
|
|
6
|
+
module Kontena
|
|
7
|
+
module Machine
|
|
8
|
+
module Upcloud
|
|
9
|
+
class NodeProvisioner
|
|
10
|
+
include RandomName
|
|
11
|
+
include UpcloudCommon
|
|
12
|
+
|
|
13
|
+
attr_reader :api_client, :username, :password
|
|
14
|
+
|
|
15
|
+
# @param [Kontena::Client] api_client Kontena api client
|
|
16
|
+
# @param [String] upcloud_username Upcloud username
|
|
17
|
+
# @param [String] upcloud_password Upcloud password
|
|
18
|
+
def initialize(api_client, upcloud_username, upcloud_password)
|
|
19
|
+
@api_client = api_client
|
|
20
|
+
@username = upcloud_username
|
|
21
|
+
@password = upcloud_password
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def run!(opts)
|
|
25
|
+
if File.readable?(File.expand_path(opts[:ssh_key]))
|
|
26
|
+
ssh_key = File.read(File.expand_path(opts[:ssh_key])).strip
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
abort('Invalid ssh key') unless ssh_key && ssh_key.start_with?('ssh-')
|
|
30
|
+
|
|
31
|
+
userdata_vars = {
|
|
32
|
+
version: opts[:version],
|
|
33
|
+
master_uri: opts[:master_uri],
|
|
34
|
+
grid_token: opts[:grid_token],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
abort('CoreOS template not found on Upcloud') unless coreos_template = find_template('CoreOS Stable')
|
|
38
|
+
abort('Server plan not found on Upcloud') unless plan = find_plan(opts[:plan])
|
|
39
|
+
abort('Zone not found on Upcloud') unless zone_exist?(opts[:zone])
|
|
40
|
+
|
|
41
|
+
hostname = generate_name
|
|
42
|
+
|
|
43
|
+
device_data = {
|
|
44
|
+
server: {
|
|
45
|
+
zone: opts[:zone],
|
|
46
|
+
title: "Kontena Grid #{opts[:grid]} Node #{hostname}",
|
|
47
|
+
hostname: hostname,
|
|
48
|
+
plan: plan[:name],
|
|
49
|
+
vnc: 'off',
|
|
50
|
+
timezone: 'UTC',
|
|
51
|
+
user_data: user_data(userdata_vars),
|
|
52
|
+
firewall: 'off',
|
|
53
|
+
storage_devices: {
|
|
54
|
+
storage_device: [
|
|
55
|
+
{
|
|
56
|
+
action: 'clone',
|
|
57
|
+
storage: coreos_template[:uuid],
|
|
58
|
+
title: "From template #{coreos_template[:title]}",
|
|
59
|
+
size: plan[:storage_size],
|
|
60
|
+
tier: 'maxiops'
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
login_user: {
|
|
65
|
+
create_password: 'no',
|
|
66
|
+
username: 'root',
|
|
67
|
+
ssh_keys: {
|
|
68
|
+
ssh_key: [ssh_key]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}.to_json
|
|
73
|
+
|
|
74
|
+
ShellSpinner "Creating Upcloud node #{hostname.colorize(:cyan)} " do
|
|
75
|
+
response = post('server', body: device_data)
|
|
76
|
+
|
|
77
|
+
if response.has_key?(:error)
|
|
78
|
+
abort("\nUpcloud server creation failed (#{response[:error].fetch(:error_message, '')})")
|
|
79
|
+
end
|
|
80
|
+
device_data = response[:server]
|
|
81
|
+
|
|
82
|
+
until device_data && device_data.fetch(:state, nil).to_s == 'maintenance'
|
|
83
|
+
device_data = get("server/#{device[:uuid]}").fetch(:server, {}) rescue nil
|
|
84
|
+
sleep 5
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
node = nil
|
|
89
|
+
ShellSpinner "Waiting for node #{hostname.colorize(:cyan)} join to grid #{opts[:grid].colorize(:cyan)} " do
|
|
90
|
+
sleep 2 until node = node_exists_in_grid?(opts[:grid], hostname)
|
|
91
|
+
end
|
|
92
|
+
set_labels(node, ["region=#{opts[:zone]}", "provider=upcloud"])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def user_data(vars)
|
|
96
|
+
cloudinit_template = File.join(__dir__ , '/cloudinit.yml')
|
|
97
|
+
erb(File.read(cloudinit_template), vars)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def generate_name
|
|
101
|
+
"#{super}-#{rand(1..99)}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def node_exists_in_grid?(grid, hostname)
|
|
105
|
+
api_client.get("grids/#{grid}/nodes")['nodes'].find{|n| n['name'] == hostname}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def erb(template, vars)
|
|
109
|
+
ERB.new(template).result(OpenStruct.new(vars).instance_eval { binding })
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def set_labels(node, labels)
|
|
113
|
+
data = {labels: labels}
|
|
114
|
+
api_client.put("nodes/#{node['id']}", data, {}, {'Kontena-Grid-Token' => node['grid']['token']})
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'shell-spinner'
|
|
2
|
+
|
|
3
|
+
module Kontena
|
|
4
|
+
module Machine
|
|
5
|
+
module Upcloud
|
|
6
|
+
class NodeRestarter
|
|
7
|
+
include RandomName
|
|
8
|
+
include UpcloudCommon
|
|
9
|
+
|
|
10
|
+
attr_reader :username, :password
|
|
11
|
+
|
|
12
|
+
# @param [String] upcloud_username Upcloud username
|
|
13
|
+
# @param [String] upcloud_password Upcloud password
|
|
14
|
+
def initialize(upcloud_username, upcloud_password)
|
|
15
|
+
@username = upcloud_username
|
|
16
|
+
@password = upcloud_password
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run!(name)
|
|
20
|
+
servers = get('server')
|
|
21
|
+
unless servers && servers.has_key?(:servers)
|
|
22
|
+
abort('Upcloud API error')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
server = servers[:servers][:server].find{|s| s[:hostname] == name}
|
|
26
|
+
|
|
27
|
+
if server
|
|
28
|
+
ShellSpinner "Restarting Upcloud node #{name.colorize(:cyan)} " do
|
|
29
|
+
result = post(
|
|
30
|
+
"server/#{server[:uuid]}/restart", body: {
|
|
31
|
+
restart_server: {
|
|
32
|
+
stop_type: 'soft',
|
|
33
|
+
timeout: 600,
|
|
34
|
+
timeout_action: 'ignore'
|
|
35
|
+
}
|
|
36
|
+
}.to_json
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
abort "Cannot find node #{name.colorize(:cyan)} in Upcloud"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'excon'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Kontena
|
|
5
|
+
module Machine
|
|
6
|
+
module Upcloud
|
|
7
|
+
module UpcloudCommon
|
|
8
|
+
|
|
9
|
+
attr_reader :username
|
|
10
|
+
attr_reader :password
|
|
11
|
+
|
|
12
|
+
def client
|
|
13
|
+
@client ||= Excon.new(
|
|
14
|
+
'https://api.upcloud.com',
|
|
15
|
+
omit_default_port: true,
|
|
16
|
+
user: username,
|
|
17
|
+
password: password,
|
|
18
|
+
headers: { "Accept-Encoding" => 'application/json' }
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def find_template(name)
|
|
23
|
+
get('storage/template')[:storages][:storage].find{|s| s[:title].downcase.start_with?(name.downcase)}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def find_plan(name)
|
|
27
|
+
get('plan')[:plans][:plan].find{|s| s[:name].downcase.eql?(name.downcase)}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def zone_exist?(name)
|
|
31
|
+
get('zone')[:zones][:zone].map{|p| p[:id]}.include?(name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def get_server(id)
|
|
35
|
+
get("server/#{id}").fetch(:server, nil)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
[:get, :post, :delete].each do |http_method|
|
|
39
|
+
define_method http_method do |path, opts={}|
|
|
40
|
+
response = client.send(
|
|
41
|
+
http_method,
|
|
42
|
+
opts.merge(
|
|
43
|
+
path: File.join('/1.2', path),
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json'
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
if response.body && response.body.start_with?('{')
|
|
50
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
51
|
+
elsif response.status.to_s.start_with?('2')
|
|
52
|
+
{success: true}
|
|
53
|
+
else
|
|
54
|
+
{error: response.status}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class Testing
|
|
64
|
+
include Kontena::Machine::Upcloud::UpcloudCommon
|
|
65
|
+
def initialize(user, pass)
|
|
66
|
+
@username = user
|
|
67
|
+
@password = pass
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
@@ -55,8 +55,13 @@ class Helper
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def yml_services
|
|
58
|
-
if File.exist?('kontena.yml')
|
|
59
|
-
|
|
58
|
+
if File.exist?('kontena.yml')
|
|
59
|
+
yaml = YAML.load(File.read('kontena.yml'))
|
|
60
|
+
if yaml['version'] == '2'
|
|
61
|
+
services = yaml['services']
|
|
62
|
+
else
|
|
63
|
+
services = yaml
|
|
64
|
+
end
|
|
60
65
|
services.keys
|
|
61
66
|
end
|
|
62
67
|
rescue
|
|
@@ -144,7 +149,7 @@ if words.size > 0
|
|
|
144
149
|
completion.push %w(add list delete)
|
|
145
150
|
when 'app'
|
|
146
151
|
completion.clear
|
|
147
|
-
sub_commands = %w(init build deploy start stop remove rm ps list
|
|
152
|
+
sub_commands = %w(init build config deploy start stop remove rm ps list
|
|
148
153
|
logs monitor show)
|
|
149
154
|
if words[1]
|
|
150
155
|
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
wordpress:
|
|
2
|
+
extends:
|
|
3
|
+
file: docker-compose.yml
|
|
4
|
+
service: wordpress
|
|
5
|
+
image: wordpress:$TAG
|
|
6
|
+
stateful: true
|
|
7
|
+
environment:
|
|
8
|
+
- WORDPRESS_DB_PASSWORD=%{project}_secret
|
|
9
|
+
instances: 2
|
|
10
|
+
deploy:
|
|
11
|
+
strategy: ha
|
|
12
|
+
mysql:
|
|
13
|
+
extends:
|
|
14
|
+
file: docker-compose.yml
|
|
15
|
+
service: mysql
|
|
16
|
+
image: ${MYSQL_IMAGE}
|
|
17
|
+
stateful: true
|
|
18
|
+
environment:
|
|
19
|
+
- INTERNAL_VAR=$$INTERNAL_VAR
|
data/spec/fixtures/kontena.yml
CHANGED
|
@@ -4,7 +4,7 @@ wordpress:
|
|
|
4
4
|
service: wordpress
|
|
5
5
|
stateful: true
|
|
6
6
|
environment:
|
|
7
|
-
|
|
7
|
+
WORDPRESS_DB_PASSWORD: %{project}_secret
|
|
8
8
|
instances: 2
|
|
9
9
|
deploy:
|
|
10
10
|
strategy: ha
|
|
@@ -14,4 +14,4 @@ mysql:
|
|
|
14
14
|
service: mysql
|
|
15
15
|
stateful: true
|
|
16
16
|
environment:
|
|
17
|
-
- MYSQL_ROOT_PASSWORD=%{project}_secret
|
|
17
|
+
- MYSQL_ROOT_PASSWORD=%{project}_secret
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
version: '2'
|
|
2
|
+
name: test-project
|
|
3
|
+
services:
|
|
4
|
+
wordpress:
|
|
5
|
+
extends:
|
|
6
|
+
file: docker-compose_v2.yml
|
|
7
|
+
service: wordpress
|
|
8
|
+
stateful: true
|
|
9
|
+
environment:
|
|
10
|
+
WORDPRESS_DB_PASSWORD: ${project}_secret
|
|
11
|
+
instances: 2
|
|
12
|
+
deploy:
|
|
13
|
+
strategy: ha
|
|
14
|
+
networks:
|
|
15
|
+
- front-tier
|
|
16
|
+
- back-tier
|
|
17
|
+
mysql:
|
|
18
|
+
extends:
|
|
19
|
+
file: docker-compose_v2.yml
|
|
20
|
+
service: mysql
|
|
21
|
+
stateful: true
|
|
22
|
+
environment:
|
|
23
|
+
- MYSQL_ROOT_PASSWORD=${project}_secret
|
|
24
|
+
volumes:
|
|
25
|
+
- mysql-data:/var/lib/mysql
|
|
26
|
+
networks:
|
|
27
|
+
- back-tier
|
|
28
|
+
volumes:
|
|
29
|
+
mysql-data:
|
|
30
|
+
driver: local
|
|
31
|
+
networks:
|
|
32
|
+
front-tier:
|
|
33
|
+
driver: bridge
|
|
34
|
+
back-tier:
|
|
35
|
+
driver: bridge
|
|
@@ -13,6 +13,10 @@ describe Kontena::Cli::Apps::Common do
|
|
|
13
13
|
fixture('kontena.yml')
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
let(:kontena_v2_yml) do
|
|
17
|
+
fixture('kontena_v2.yml')
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
let(:docker_compose_yml) do
|
|
17
21
|
fixture('docker-compose.yml')
|
|
18
22
|
end
|
|
@@ -30,122 +34,56 @@ describe Kontena::Cli::Apps::Common do
|
|
|
30
34
|
}
|
|
31
35
|
end
|
|
32
36
|
|
|
33
|
-
describe '#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
allow(File).to receive(:read).with("#{Dir.getwd}/docker-compose.yml").and_return(docker_compose_yml)
|
|
38
|
-
services = subject.parse_services('kontena.yml')
|
|
39
|
-
expect(services['wordpress']).not_to be_nil
|
|
37
|
+
describe '#service_prefix' do
|
|
38
|
+
it 'returns given project name' do
|
|
39
|
+
allow(subject).to receive(:project_name).and_return('test')
|
|
40
|
+
expect(subject.service_prefix).to eq('test')
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
it '
|
|
43
|
-
allow(
|
|
44
|
-
allow(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}.to raise_error(SystemExit)
|
|
43
|
+
it 'returns app name from yaml if project name not given' do
|
|
44
|
+
allow(subject).to receive(:project_name).and_return(nil)
|
|
45
|
+
allow(subject).to receive(:filename).and_return('kontena.yml')
|
|
46
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/docker-compose_v2.yml").and_return(docker_compose_yml)
|
|
47
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/kontena.yml").and_return(kontena_v2_yml)
|
|
48
|
+
expect(subject.service_prefix).to eq('test-project')
|
|
49
49
|
end
|
|
50
|
-
end
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
subject.normalize_env_vars(opts)
|
|
61
|
-
env = opts['environment']
|
|
62
|
-
expect(env).to include('FOO=bar')
|
|
63
|
-
expect(env).to include('BAR=baz')
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it 'does nothing to env array' do
|
|
67
|
-
opts = {
|
|
68
|
-
'environment' => [
|
|
69
|
-
'FOO=bar', 'BAR=baz'
|
|
70
|
-
]
|
|
71
|
-
}
|
|
72
|
-
subject.normalize_env_vars(opts)
|
|
73
|
-
env = opts['environment']
|
|
74
|
-
expect(env).to include('FOO=bar')
|
|
75
|
-
expect(env).to include('BAR=baz')
|
|
51
|
+
it 'returns current dir as default' do
|
|
52
|
+
allow(subject).to receive(:project_name).and_return(nil)
|
|
53
|
+
allow(subject).to receive(:filename).and_return('kontena.yml')
|
|
54
|
+
allow(subject).to receive(:project_name_from_yaml).and_return(nil)
|
|
55
|
+
allow(subject).to receive(:current_dir).and_return('working_dir')
|
|
56
|
+
expect(subject.service_prefix).to eq('working_dir')
|
|
76
57
|
end
|
|
77
58
|
end
|
|
78
59
|
|
|
79
|
-
describe '#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
to
|
|
83
|
-
env_vars = subject.extend_env_vars(from, to)
|
|
84
|
-
expect(env_vars).to eq(['FOO=bar'])
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it 'overrides values' do
|
|
88
|
-
from = ['FOO=bar']
|
|
89
|
-
to = ['FOO=baz']
|
|
90
|
-
env_vars = subject.extend_env_vars(from, to)
|
|
91
|
-
expect(env_vars).to eq(['FOO=baz'])
|
|
60
|
+
describe '#load_from_yaml' do
|
|
61
|
+
before(:each) do
|
|
62
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/kontena.yml").and_return(kontena_yml)
|
|
63
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/docker-compose.yml").and_return(docker_compose_yml)
|
|
92
64
|
end
|
|
93
65
|
|
|
94
|
-
it '
|
|
95
|
-
|
|
96
|
-
to
|
|
97
|
-
|
|
98
|
-
expect(env_vars).to eq(['BAR=baz', 'FOO=bar'])
|
|
66
|
+
it 'populates env variables' do
|
|
67
|
+
services = subject.services_from_yaml('kontena.yml',[],'load-test')
|
|
68
|
+
expect(ENV['grid']).to eq('test-grid')
|
|
69
|
+
expect(ENV['project']).to eq('load-test')
|
|
99
70
|
end
|
|
100
|
-
end
|
|
101
71
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
'secret' => 'CUSTOMER_DB_PASSWORD',
|
|
106
|
-
'name' => 'MYSQL_PASSWORD',
|
|
107
|
-
'type' => 'env'
|
|
108
|
-
}
|
|
109
|
-
from = [secret]
|
|
110
|
-
to = nil
|
|
111
|
-
secrets = subject.extend_secrets(from, to)
|
|
112
|
-
expect(secrets).to eq([secret])
|
|
72
|
+
it 'returns services from given YAML file' do
|
|
73
|
+
services = subject.services_from_yaml('kontena.yml',[],'')
|
|
74
|
+
expect(services['wordpress']).not_to be_nil
|
|
113
75
|
end
|
|
114
76
|
|
|
115
|
-
it '
|
|
116
|
-
|
|
117
|
-
'
|
|
118
|
-
|
|
119
|
-
'type' => 'env'
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
to_secret = {
|
|
123
|
-
'secret' => 'CUSTOMER_DB_PASSWORD',
|
|
124
|
-
'name' => 'MYSQL_ROOT_PASSWORD',
|
|
125
|
-
'type' => 'env'
|
|
126
|
-
}
|
|
127
|
-
from = [from_secret]
|
|
128
|
-
to = [to_secret]
|
|
129
|
-
secrets = subject.extend_secrets(from, to)
|
|
130
|
-
expect(secrets).to eq([to_secret])
|
|
77
|
+
it 'aborts on validation failure' do
|
|
78
|
+
allow_any_instance_of(Kontena::Cli::Apps::YAML::Validator).to receive(:validate)
|
|
79
|
+
.and_return({ :errors => [{ 'wordress' => [] }], :notifications => [] })
|
|
80
|
+
expect { subject.services_from_yaml('kontena.yml',[],'') }.to raise_error(SystemExit)
|
|
131
81
|
end
|
|
132
82
|
|
|
133
|
-
it '
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
'type' => 'env'
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
to_secret = {
|
|
141
|
-
'secret' => 'CUSTOMER_API_TOKEN',
|
|
142
|
-
'name' => 'API_TOKEN',
|
|
143
|
-
'type' => 'env'
|
|
144
|
-
}
|
|
145
|
-
from = [from_secret]
|
|
146
|
-
to = [to_secret]
|
|
147
|
-
secrets = subject.extend_secrets(from, to)
|
|
148
|
-
expect(secrets).to eq([to_secret, from_secret])
|
|
83
|
+
it 'returns given service from given YAML file' do
|
|
84
|
+
services = subject.services_from_yaml('kontena.yml',['wordpress'],'')
|
|
85
|
+
expect(services['wordpress']).not_to be_nil
|
|
86
|
+
expect(services.size).to eq(1)
|
|
149
87
|
end
|
|
150
88
|
end
|
|
151
89
|
end
|