kontena-cli 0.16.3 → 0.17.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +1 -0
- data/.gitignore +3 -1
- data/VERSION +1 -1
- data/lib/kontena/callbacks/master/deploy/40_install_ssl_certificate_after_deploy.rb +32 -0
- data/lib/kontena/cli/apps/deploy_command.rb +2 -2
- data/lib/kontena/cli/apps/scale_command.rb +2 -2
- data/lib/kontena/cli/apps/show_command.rb +3 -2
- data/lib/kontena/cli/apps/yaml/validations.rb +10 -6
- data/lib/kontena/cli/apps/yaml/validator.rb +1 -0
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +1 -0
- data/lib/kontena/cli/cloud/login_command.rb +66 -64
- data/lib/kontena/cli/common.rb +0 -10
- data/lib/kontena/cli/grids/logs_command.rb +0 -1
- data/lib/kontena/cli/localhost_web_server.rb +11 -3
- data/lib/kontena/cli/master/login_command.rb +213 -163
- data/lib/kontena/cli/nodes/label_command.rb +2 -0
- data/lib/kontena/cli/nodes/labels/add_command.rb +7 -8
- data/lib/kontena/cli/nodes/labels/list_command.rb +17 -0
- data/lib/kontena/cli/nodes/labels/remove_command.rb +7 -12
- data/lib/kontena/cli/nodes/show_command.rb +1 -0
- data/lib/kontena/cli/plugins/common.rb +8 -0
- data/lib/kontena/cli/plugins/install_command.rb +21 -2
- data/lib/kontena/cli/plugins/list_command.rb +4 -2
- data/lib/kontena/cli/plugins/search_command.rb +4 -2
- data/lib/kontena/cli/registry/create_command.rb +19 -12
- data/lib/kontena/cli/registry/remove_command.rb +4 -4
- data/lib/kontena/cli/registry_command.rb +0 -1
- data/lib/kontena/cli/services/create_command.rb +6 -6
- data/lib/kontena/cli/services/deploy_command.rb +8 -4
- data/lib/kontena/cli/services/list_command.rb +34 -21
- data/lib/kontena/cli/services/logs_command.rb +1 -1
- data/lib/kontena/cli/services/scale_command.rb +3 -3
- data/lib/kontena/cli/services/services_helper.rb +18 -14
- data/lib/kontena/cli/services/show_command.rb +1 -0
- data/lib/kontena/cli/services/update_command.rb +6 -6
- data/lib/kontena/cli/stack_command.rb +12 -6
- data/lib/kontena/cli/stacks/build_command.rb +110 -0
- data/lib/kontena/cli/stacks/common.rb +85 -20
- data/lib/kontena/cli/stacks/deploy_command.rb +30 -7
- data/lib/kontena/cli/stacks/install_command.rb +30 -0
- data/lib/kontena/cli/stacks/list_command.rb +74 -14
- data/lib/kontena/cli/stacks/logs_command.rb +31 -0
- data/lib/kontena/cli/stacks/monitor_command.rb +91 -0
- data/lib/kontena/cli/stacks/remove_command.rb +24 -7
- data/lib/kontena/cli/stacks/service_generator.rb +115 -0
- data/lib/kontena/cli/stacks/service_generator_v2.rb +27 -0
- data/lib/kontena/cli/stacks/show_command.rb +65 -13
- data/lib/kontena/cli/stacks/upgrade_command.rb +28 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/affinities_validator.rb +19 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/build_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/extends_validator.rb +21 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/hooks_validator.rb +54 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/secrets_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/reader.rb +219 -0
- data/lib/kontena/cli/stacks/yaml/service_extender.rb +78 -0
- data/lib/kontena/cli/stacks/yaml/validations.rb +71 -0
- data/lib/kontena/cli/stacks/yaml/validator_v3.rb +52 -0
- data/lib/kontena/cli/version_command.rb +5 -1
- data/lib/kontena/cli/vpn/create_command.rb +20 -17
- data/lib/kontena/cli/vpn/remove_command.rb +4 -3
- data/lib/kontena/client.rb +21 -20
- data/lib/kontena/machine/cert_helper.rb +4 -0
- data/lib/kontena/machine/cloud_config/cloudinit.yml +1 -1
- data/lib/kontena/main_command.rb +1 -1
- data/spec/fixtures/kontena-build.yml +2 -2
- data/spec/fixtures/kontena-invalid.yml +1 -1
- data/spec/fixtures/kontena-not-hash-service-config.yml +1 -1
- data/spec/fixtures/kontena-with-env-file.yml +2 -2
- data/spec/fixtures/kontena_build_v3.yml +23 -0
- data/spec/fixtures/kontena_v3.yml +20 -0
- data/spec/fixtures/stack-internal-extend.yml +11 -0
- data/spec/fixtures/stack-with-env-file.yml +21 -0
- data/spec/fixtures/stack-with-variables.yml +22 -0
- data/spec/kontena/cli/app/scale_spec.rb +3 -1
- data/spec/kontena/cli/cloud/login_command_spec.rb +283 -0
- data/spec/kontena/cli/master/login_command_spec.rb +324 -145
- data/spec/kontena/cli/services/link_command_spec.rb +1 -1
- data/spec/kontena/cli/services/secrets/link_command_spec.rb +4 -4
- data/spec/kontena/cli/services/secrets/unlink_command_spec.rb +2 -2
- data/spec/kontena/cli/services/services_helper_spec.rb +15 -11
- data/spec/kontena/cli/services/unlink_command_spec.rb +1 -1
- data/spec/kontena/cli/stacks/deploy_command_spec.rb +26 -0
- data/spec/kontena/cli/stacks/install_command_spec.rb +54 -0
- data/spec/kontena/cli/stacks/list_command_spec.rb +27 -0
- data/spec/kontena/cli/stacks/remove_command_spec.rb +45 -0
- data/spec/kontena/cli/stacks/service_generator_spec.rb +385 -0
- data/spec/kontena/cli/stacks/service_generator_v2_spec.rb +74 -0
- data/spec/kontena/cli/stacks/show_command_spec.rb +26 -0
- data/spec/kontena/cli/stacks/upgrade_command_spec.rb +50 -0
- data/spec/kontena/cli/stacks/yaml/reader_spec.rb +370 -0
- data/spec/kontena/cli/stacks/yaml/service_extender_spec.rb +128 -0
- data/spec/kontena/cli/stacks/yaml/validator_v3_spec.rb +302 -0
- data/spec/spec_helper.rb +6 -4
- data/spec/support/client_helpers.rb +1 -0
- metadata +57 -7
- data/lib/kontena/cli/registry/delete_command.rb +0 -18
- data/lib/kontena/cli/stacks/create_command.rb +0 -27
- data/lib/kontena/cli/stacks/update_command.rb +0 -27
@@ -32,12 +32,12 @@ module Kontena::Cli::Services
|
|
32
32
|
option "--deploy-interval", "TIME", "Auto-deploy with given interval (format: <number><unit>, where unit = min, h, d)"
|
33
33
|
option "--pid", "PID", "Pid namespace to use"
|
34
34
|
option "--secret", "SECRET", "Import secret from Vault (format: <secret>:<name>:<type>)", multivalued: true
|
35
|
-
option "--health-check-uri", "
|
36
|
-
option "--health-check-timeout", "
|
37
|
-
option "--health-check-interval", "
|
38
|
-
option "--health-check-initial-delay", "
|
39
|
-
option "--health-check-port", "
|
40
|
-
option "--health-check-protocol", "
|
35
|
+
option "--health-check-uri", "URI", "URI path for HTTP health check"
|
36
|
+
option "--health-check-timeout", "TIMEOUT", "Timeout for HTTP health check"
|
37
|
+
option "--health-check-interval", "INTERVAL", "Interval for HTTP health check"
|
38
|
+
option "--health-check-initial-delay", "DELAY", "Initial for HTTP health check"
|
39
|
+
option "--health-check-port", "PORT", "Port for HTTP health check"
|
40
|
+
option "--health-check-protocol", "PROTOCOL", "Protocol of health check"
|
41
41
|
|
42
42
|
def execute
|
43
43
|
require_api_url
|
@@ -1,19 +1,25 @@
|
|
1
|
-
require_relative 'stacks/
|
1
|
+
require_relative 'stacks/install_command'
|
2
2
|
require_relative 'stacks/remove_command'
|
3
3
|
require_relative 'stacks/deploy_command'
|
4
|
-
require_relative 'stacks/
|
4
|
+
require_relative 'stacks/upgrade_command'
|
5
5
|
require_relative 'stacks/list_command'
|
6
6
|
require_relative 'stacks/show_command'
|
7
|
+
require_relative 'stacks/build_command'
|
8
|
+
require_relative 'stacks/monitor_command'
|
9
|
+
require_relative 'stacks/logs_command'
|
7
10
|
|
8
11
|
class Kontena::Cli::StackCommand < Kontena::Command
|
9
12
|
|
10
|
-
subcommand "
|
13
|
+
subcommand "install", "Install a stack", Kontena::Cli::Stacks::InstallCommand
|
14
|
+
subcommand "build", "Build stack file images", Kontena::Cli::Stacks::BuildCommand
|
11
15
|
subcommand ["ls", "list"], "List stacks", Kontena::Cli::Stacks::ListCommand
|
12
16
|
subcommand "show", "Show stack details", Kontena::Cli::Stacks::ShowCommand
|
13
|
-
subcommand "
|
14
|
-
subcommand "deploy", "Deploy
|
17
|
+
subcommand "upgrade", "Upgrade installed stack", Kontena::Cli::Stacks::UpgradeCommand
|
18
|
+
subcommand "deploy", "Deploy stack", Kontena::Cli::Stacks::DeployCommand
|
19
|
+
subcommand "logs", "Show stack logs from stack services", Kontena::Cli::Stacks::LogsCommand
|
20
|
+
subcommand "monitor", "Monitor stack", Kontena::Cli::Stacks::MonitorCommand
|
15
21
|
subcommand ["remove","rm"], "Remove stack", Kontena::Cli::Stacks::RemoveCommand
|
16
|
-
|
22
|
+
|
17
23
|
def execute
|
18
24
|
|
19
25
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Kontena::Cli::Stacks
|
4
|
+
class BuildCommand < Clamp::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
include Common
|
7
|
+
|
8
|
+
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
9
|
+
option ['--no-cache'], :flag, 'Do not use cache when building the image', default: false
|
10
|
+
option ['--no-push'], :flag, 'Do not push images to registry', default: false
|
11
|
+
option ['--no-pull'], :flag, 'Do not attempt to pull a newer version of the image', default: false
|
12
|
+
parameter "[SERVICE] ...", "Services to build"
|
13
|
+
|
14
|
+
def execute
|
15
|
+
require_config_file(filename)
|
16
|
+
services = services_from_yaml(filename, service_list, service_prefix)
|
17
|
+
if services.none?{ |name, service| service['build'] }
|
18
|
+
abort 'Not found any service with a build option'.colorize(:red)
|
19
|
+
end
|
20
|
+
build_docker_images(services, no_cache?, no_pull?)
|
21
|
+
push_docker_images(services) unless no_push?
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [Hash] services
|
25
|
+
# @param [Boolean] no_cache
|
26
|
+
# @param [Boolean] no_pull
|
27
|
+
def build_docker_images(services, no_cache = false, no_pull = false)
|
28
|
+
services.each do |name, service|
|
29
|
+
if service['build']
|
30
|
+
dockerfile = service['build']['dockerfile'] || 'Dockerfile'
|
31
|
+
abort("'#{service['image']}' is not valid Docker image name") unless validate_image_name(service['image'])
|
32
|
+
abort("'#{service['build']['context']}' does not have #{dockerfile}") unless dockerfile_exist?(service['build']['context'], dockerfile)
|
33
|
+
if service['hooks'] && service['hooks']['pre_build']
|
34
|
+
puts "Running pre_build hook".colorize(:cyan)
|
35
|
+
run_pre_build_hook(service['hooks']['pre_build'])
|
36
|
+
end
|
37
|
+
puts "Building image #{service['image'].colorize(:cyan)}"
|
38
|
+
build_docker_image(service, no_cache, no_pull)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [Hash] services
|
44
|
+
def push_docker_images(services)
|
45
|
+
services.each do |name, service|
|
46
|
+
if service['build']
|
47
|
+
puts "Pushing image #{service['image'].colorize(:cyan)}"
|
48
|
+
push_docker_image(service['image'])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [Hash] service
|
54
|
+
# @param [Boolean] no_cache
|
55
|
+
# @param [Boolean] no_pull
|
56
|
+
# @return [Integer]
|
57
|
+
def build_docker_image(service, no_cache = false, no_pull = false)
|
58
|
+
dockerfile = dockerfile = service['build']['dockerfile'] || 'Dockerfile'
|
59
|
+
build_context = service['build']['context']
|
60
|
+
cmd = ['docker', 'build', '-t', service['image']]
|
61
|
+
cmd << ['-f', File.join(File.expand_path(build_context), dockerfile)] if dockerfile != "Dockerfile"
|
62
|
+
cmd << '--no-cache' if no_cache
|
63
|
+
cmd << '--pull' unless no_pull
|
64
|
+
args = service['build']['args'] || {}
|
65
|
+
args.each do |k, v|
|
66
|
+
cmd << "--build-arg=#{k}=#{v}"
|
67
|
+
end
|
68
|
+
cmd << build_context
|
69
|
+
ret = system(*cmd.flatten)
|
70
|
+
raise ("Failed to build image #{service['image'].colorize(:cyan)}") unless ret
|
71
|
+
ret
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [String] image
|
75
|
+
# @return [Integer]
|
76
|
+
def push_docker_image(image)
|
77
|
+
ret = system('docker', 'push', image)
|
78
|
+
raise ("Failed to push image #{image.colorize(:cyan)}") unless ret
|
79
|
+
ret
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param [String] name
|
83
|
+
# @return [Boolean]
|
84
|
+
def validate_image_name(name)
|
85
|
+
!(/^[\w.\/\-:]+:?+[\w+.]+$/ =~ name).nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param [String] image
|
89
|
+
# @return [Boolean]
|
90
|
+
def image_exist?(image)
|
91
|
+
system("docker history '#{image}' >/dev/null 2>/dev/null")
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param [String] path
|
95
|
+
# @param [String] dockerfile
|
96
|
+
# @return [Boolean]
|
97
|
+
def dockerfile_exist?(path, dockerfile)
|
98
|
+
file = File.join(File.expand_path(path), dockerfile)
|
99
|
+
File.exist?(file)
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param [Hash] hook
|
103
|
+
def run_pre_build_hook(hook)
|
104
|
+
hook.each do |h|
|
105
|
+
ret = system(h['cmd'])
|
106
|
+
raise ("Failed to run pre_build hook: #{h['name']}!") unless ret
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -1,37 +1,102 @@
|
|
1
|
-
|
1
|
+
require 'yaml'
|
2
|
+
require_relative 'yaml/reader'
|
3
|
+
require_relative '../services/services_helper'
|
4
|
+
require_relative 'service_generator_v2'
|
2
5
|
|
3
6
|
module Kontena::Cli::Stacks
|
4
7
|
module Common
|
5
|
-
include Kontena::Cli::
|
8
|
+
include Kontena::Cli::Services::ServicesHelper
|
6
9
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def stack_from_yaml(filename)
|
12
|
-
set_env_variables(service_prefix, current_grid)
|
13
|
-
outcome = read_yaml(filename)
|
14
|
-
if outcome[:version] != '2'
|
15
|
-
exit_with_error "Stack supported only in v2 YAML! Aborting."
|
16
|
-
end
|
17
|
-
if outcome[:name].nil?
|
10
|
+
def stack_from_yaml(filename, skip_validation = false)
|
11
|
+
reader = Kontena::Cli::Stacks::YAML::Reader.new(filename, skip_validation)
|
12
|
+
if reader.stack_name.nil?
|
18
13
|
exit_with_error "Stack MUST have name in YAML! Aborting."
|
19
14
|
end
|
15
|
+
set_env_variables(self.name || reader.stack_name, current_grid)
|
16
|
+
reader.reload
|
17
|
+
outcome = reader.execute
|
18
|
+
|
20
19
|
hint_on_validation_notifications(outcome[:notifications]) if outcome[:notifications].size > 0
|
21
20
|
abort_on_validation_errors(outcome[:errors]) if outcome[:errors].size > 0
|
22
21
|
kontena_services = generate_services(outcome[:services], outcome[:version])
|
23
|
-
# services now as hash, needs to be array in stacks API
|
24
|
-
services = []
|
25
|
-
kontena_services.each do |name, service|
|
26
|
-
service['name'] = prefixed_name(name)
|
27
|
-
services << service
|
28
|
-
end
|
29
22
|
stack = {
|
30
23
|
'name' => outcome[:name],
|
31
|
-
'
|
24
|
+
'stack' => outcome[:stack],
|
25
|
+
'expose' => outcome[:expose],
|
26
|
+
'version' => outcome[:version],
|
27
|
+
'source' => reader.raw,
|
28
|
+
'registry' => 'file://',
|
29
|
+
'services' => kontena_services
|
32
30
|
}
|
33
31
|
stack
|
34
32
|
end
|
35
33
|
|
34
|
+
def require_config_file(filename)
|
35
|
+
exit_with_error("File #{filename} does not exist") unless File.exists?(filename)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
##
|
40
|
+
# @param [Hash] yaml
|
41
|
+
# @param [String] version
|
42
|
+
# @return [Hash]
|
43
|
+
def generate_services(yaml_services, version)
|
44
|
+
services = []
|
45
|
+
generator_klass = ServiceGeneratorV2
|
46
|
+
yaml_services.each do |service_name, config|
|
47
|
+
exit_with_error("Image is missing for #{service_name}. Aborting.") unless config['image']
|
48
|
+
service = generator_klass.new(config).generate
|
49
|
+
service['name'] = service_name
|
50
|
+
services << service
|
51
|
+
end
|
52
|
+
services
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_yaml(filename, skip_validation = false)
|
56
|
+
reader = Kontena::Cli::Stacks::YAML::Reader.new(filename)
|
57
|
+
outcome = reader.execute
|
58
|
+
outcome
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_env_variables(stack, grid)
|
62
|
+
ENV['STACK'] = stack
|
63
|
+
ENV['GRID'] = grid
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [String]
|
67
|
+
def current_dir
|
68
|
+
File.basename(Dir.getwd)
|
69
|
+
end
|
70
|
+
|
71
|
+
def display_notifications(messages, color = :yellow)
|
72
|
+
messages.each do |files|
|
73
|
+
files.each do |file, services|
|
74
|
+
STDERR.puts "#{file}:".colorize(color)
|
75
|
+
services.each do |service|
|
76
|
+
service.each do |name, errors|
|
77
|
+
STDERR.puts " #{name}:".colorize(color)
|
78
|
+
if errors.is_a?(String)
|
79
|
+
STDERR.puts " - #{errors}".colorize(color)
|
80
|
+
else
|
81
|
+
errors.each do |key, error|
|
82
|
+
STDERR.puts " - #{key}: #{error.to_json}".colorize(color)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def hint_on_validation_notifications(errors)
|
92
|
+
STDERR.puts "YAML contains the following unsupported options and they were rejected:".colorize(:yellow)
|
93
|
+
display_notifications(errors)
|
94
|
+
end
|
95
|
+
|
96
|
+
def abort_on_validation_errors(errors)
|
97
|
+
STDERR.puts "YAML validation failed! Aborting.".colorize(:red)
|
98
|
+
display_notifications(errors, :red)
|
99
|
+
abort
|
100
|
+
end
|
36
101
|
end
|
37
102
|
end
|
@@ -10,17 +10,40 @@ module Kontena::Cli::Stacks
|
|
10
10
|
|
11
11
|
def execute
|
12
12
|
require_api_url
|
13
|
-
require_token
|
14
|
-
|
15
|
-
|
13
|
+
token = require_token
|
14
|
+
|
15
|
+
deployment = nil
|
16
|
+
spinner "Deploying stack #{pastel.cyan(name)}" do
|
17
|
+
deployment = deploy_stack(token, name)
|
18
|
+
deployment['service_deploys'].each do |service_deploy|
|
19
|
+
wait_for_deploy_to_finish(token, service_deploy)
|
20
|
+
end
|
21
|
+
end
|
16
22
|
end
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def deploy_stack
|
24
|
+
def deploy_stack(token, name)
|
22
25
|
client(token).post("stacks/#{current_grid}/#{name}/deploy", {})
|
23
26
|
end
|
24
27
|
|
28
|
+
# @param [String] token
|
29
|
+
# @param [Hash] deployment
|
30
|
+
# @return [Boolean]
|
31
|
+
def wait_for_deploy_to_finish(token, deployment, timeout = 600)
|
32
|
+
deployed = false
|
33
|
+
Timeout::timeout(timeout) do
|
34
|
+
until deployed
|
35
|
+
deployment = client(token).get("services/#{deployment['service_id']}/deploys/#{deployment['id']}")
|
36
|
+
deployed = true if deployment['finished_at']
|
37
|
+
sleep 1
|
38
|
+
end
|
39
|
+
if deployment['state'] == 'error'
|
40
|
+
raise deployment['reason']
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
deployed
|
45
|
+
rescue Timeout::Error
|
46
|
+
raise 'deploy timed out'
|
47
|
+
end
|
25
48
|
end
|
26
49
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Kontena::Cli::Stacks
|
4
|
+
class InstallCommand < Kontena::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
include Kontena::Cli::GridOptions
|
7
|
+
include Common
|
8
|
+
|
9
|
+
parameter "[FILE]", "Kontena stack file", default: "kontena.yml", attribute_name: :filename
|
10
|
+
|
11
|
+
option ['-n', '--name'], 'NAME', 'Define stack name (by default comes from stack file)'
|
12
|
+
option '--deploy', :flag, 'Deploy after installation'
|
13
|
+
|
14
|
+
def execute
|
15
|
+
require_api_url
|
16
|
+
token = require_token
|
17
|
+
require_config_file(filename)
|
18
|
+
stack = stack_from_yaml(filename)
|
19
|
+
stack['name'] = name if name
|
20
|
+
spinner "Creating stack #{pastel.cyan(stack['name'])} " do
|
21
|
+
create_stack(token, stack)
|
22
|
+
end
|
23
|
+
Kontena.run("stack deploy #{stack['name']}") if deploy?
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_stack(token, stack)
|
27
|
+
client(token).post("grids/#{current_grid}/stacks", stack)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -6,33 +6,93 @@ module Kontena::Cli::Stacks
|
|
6
6
|
include Kontena::Cli::GridOptions
|
7
7
|
include Common
|
8
8
|
|
9
|
-
COLUMNS = "%-30s %-10s %-10s".freeze
|
10
|
-
|
11
9
|
def execute
|
12
10
|
require_api_url
|
13
|
-
require_token
|
11
|
+
token = require_token
|
14
12
|
|
15
|
-
list_stacks
|
13
|
+
list_stacks(token)
|
16
14
|
end
|
17
15
|
|
18
|
-
|
16
|
+
def list_stacks(token)
|
17
|
+
response = client(token).get("grids/#{current_grid}/stacks")
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
titles = ['NAME', 'SERVICES', 'STATE']
|
24
|
-
puts COLUMNS % titles
|
19
|
+
titles = ['NAME', 'VERSION', 'SERVICES', 'STATE', 'EXPOSED PORTS']
|
20
|
+
puts "%-60s %-10s %-10s %-10s %-50s" % titles
|
25
21
|
|
26
22
|
response['stacks'].each do |stack|
|
23
|
+
ports = stack_ports(stack)
|
24
|
+
health = stack_health(stack)
|
25
|
+
if health == :unhealthy
|
26
|
+
icon = '⊗'.freeze
|
27
|
+
color = :red
|
28
|
+
elsif health == :partial
|
29
|
+
icon = '⊙'.freeze
|
30
|
+
color = :yellow
|
31
|
+
elsif health == :healthy
|
32
|
+
icon = '⊛'.freeze
|
33
|
+
color = :green
|
34
|
+
else
|
35
|
+
icon = '⊝'.freeze
|
36
|
+
color = :dim
|
37
|
+
end
|
38
|
+
|
27
39
|
vars = [
|
28
|
-
|
29
|
-
stack['
|
30
|
-
stack['
|
40
|
+
icon.colorize(color),
|
41
|
+
"#{stack['name']}",
|
42
|
+
"#{stack['version']}",
|
43
|
+
stack['services'].size,
|
44
|
+
stack['state'],
|
45
|
+
ports.join(",")
|
31
46
|
]
|
32
47
|
|
33
|
-
puts
|
48
|
+
puts "%s %-58s %-10s %-10s %-10s %-50s" % vars
|
34
49
|
end
|
35
50
|
end
|
36
51
|
|
52
|
+
# @param [Hash] stack
|
53
|
+
# @return [Array<String>]
|
54
|
+
def stack_ports(stack)
|
55
|
+
ports = []
|
56
|
+
stack['services'].each{|s|
|
57
|
+
service_ports = s['ports'].map{|p|
|
58
|
+
p['ip'] = '*' if p['ip'] == '0.0.0.0'
|
59
|
+
"#{p['ip']}:#{p['node_port']}->#{p['container_port']}/#{p['protocol']}"
|
60
|
+
}
|
61
|
+
ports = ports + service_ports unless service_ports.empty?
|
62
|
+
}
|
63
|
+
ports
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [Hash] stack
|
67
|
+
# @return [Symbol]
|
68
|
+
def stack_health(stack)
|
69
|
+
services_count = stack['services'].size
|
70
|
+
return :unknown if services_count == 0
|
71
|
+
|
72
|
+
fully_healthy_count = 0
|
73
|
+
partial_healthy_count = 0
|
74
|
+
unhealthy_count = 0
|
75
|
+
unknown_count = 0
|
76
|
+
stack['services'].each { |s|
|
77
|
+
total = s.dig('health_status', 'total').to_i
|
78
|
+
healthy = s.dig('health_status', 'healthy').to_i
|
79
|
+
if total > 0 && healthy == total
|
80
|
+
fully_healthy_count += 1
|
81
|
+
elsif healthy < total && healthy > 0
|
82
|
+
partial_healthy_count += 1
|
83
|
+
elsif healthy == 0 && total > 0
|
84
|
+
unhealthy_count += 1
|
85
|
+
else
|
86
|
+
unknown_count += 1
|
87
|
+
end
|
88
|
+
}
|
89
|
+
return :partial if partial_healthy_count > 0
|
90
|
+
return :partial if unhealthy_count > 0 && fully_healthy_count > 0
|
91
|
+
return :unhealthy if unhealthy_count == services_count
|
92
|
+
return :healthy if fully_healthy_count == services_count
|
93
|
+
return :healthy if fully_healthy_count > 0 && unknown_count > 0
|
94
|
+
|
95
|
+
:unknown
|
96
|
+
end
|
37
97
|
end
|
38
98
|
end
|