kontena-cli 1.4.0.pre6 → 1.4.0.pre7
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/Gemfile +1 -1
- data/VERSION +1 -1
- data/bin/kontena +1 -1
- data/kontena-cli.gemspec +3 -3
- data/lib/kontena/cli/certificate/authorize_command.rb +67 -6
- data/lib/kontena/cli/certificate/get_command.rb +7 -0
- data/lib/kontena/cli/certificate/list_command.rb +75 -0
- data/lib/kontena/cli/certificate/register_command.rb +13 -2
- data/lib/kontena/cli/certificate/request_command.rb +20 -0
- data/lib/kontena/cli/certificate/show_command.rb +19 -0
- data/lib/kontena/cli/certificate_command.rb +4 -1
- data/lib/kontena/cli/cloud/master/add_command.rb +1 -1
- data/lib/kontena/cli/common.rb +21 -33
- data/lib/kontena/cli/etcd/health_command.rb +21 -27
- data/lib/kontena/cli/helpers/exec_helper.rb +15 -6
- data/lib/kontena/cli/helpers/health_helper.rb +12 -0
- data/lib/kontena/cli/helpers/log_helper.rb +2 -2
- data/lib/kontena/cli/helpers/time_helper.rb +29 -0
- data/lib/kontena/cli/master/init_cloud_command.rb +19 -0
- data/lib/kontena/cli/master/list_command.rb +1 -1
- data/lib/kontena/cli/master/ssh_command.rb +3 -1
- data/lib/kontena/cli/master/use_command.rb +1 -2
- data/lib/kontena/cli/node_command.rb +1 -0
- data/lib/kontena/cli/nodes/health_command.rb +28 -13
- data/lib/kontena/cli/nodes/list_command.rb +19 -3
- data/lib/kontena/cli/nodes/show_command.rb +4 -2
- data/lib/kontena/cli/nodes/ssh_command.rb +5 -2
- data/lib/kontena/cli/nodes/update_command.rb +2 -0
- data/lib/kontena/cli/plugins/install_command.rb +11 -8
- data/lib/kontena/cli/plugins/list_command.rb +5 -3
- data/lib/kontena/cli/plugins/search_command.rb +4 -2
- data/lib/kontena/cli/plugins/show_command.rb +17 -0
- data/lib/kontena/cli/plugins/uninstall_command.rb +9 -13
- data/lib/kontena/cli/registry/create_command.rb +1 -1
- data/lib/kontena/cli/services/create_command.rb +6 -0
- data/lib/kontena/cli/services/services_helper.rb +33 -6
- data/lib/kontena/cli/services/update_command.rb +6 -0
- data/lib/kontena/cli/stacks/build_command.rb +3 -3
- data/lib/kontena/cli/stacks/common.rb +105 -90
- data/lib/kontena/cli/stacks/deploy_command.rb +7 -3
- data/lib/kontena/cli/stacks/install_command.rb +39 -6
- data/lib/kontena/cli/stacks/list_command.rb +36 -4
- data/lib/kontena/cli/stacks/logs_command.rb +9 -2
- data/lib/kontena/cli/stacks/registry/pull_command.rb +2 -2
- data/lib/kontena/cli/stacks/registry/push_command.rb +20 -9
- data/lib/kontena/cli/stacks/registry/remove_command.rb +4 -4
- data/lib/kontena/cli/stacks/registry/show_command.rb +4 -4
- data/lib/kontena/cli/stacks/remove_command.rb +27 -1
- data/lib/kontena/cli/stacks/service_generator.rb +12 -2
- data/lib/kontena/cli/stacks/show_command.rb +35 -5
- data/lib/kontena/cli/stacks/stack_name.rb +71 -0
- data/lib/kontena/cli/stacks/upgrade_command.rb +127 -14
- data/lib/kontena/cli/stacks/validate_command.rb +38 -10
- data/lib/kontena/cli/stacks/yaml/custom_validators/certificates_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +1 -2
- data/lib/kontena/cli/stacks/yaml/reader.rb +211 -185
- data/lib/kontena/cli/stacks/yaml/service_extender.rb +6 -12
- data/lib/kontena/cli/stacks/yaml/stack_file_loader.rb +97 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader/file_loader.rb +41 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader/registry_loader.rb +24 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader/uri_loader.rb +23 -0
- data/lib/kontena/cli/stacks/yaml/validations.rb +16 -0
- data/lib/kontena/cli/stacks/yaml/validator_v3.rb +25 -8
- data/lib/kontena/client.rb +2 -2
- data/lib/kontena/command.rb +11 -0
- data/lib/kontena/main_command.rb +3 -1
- data/lib/kontena/plugin_manager.rb +11 -198
- data/lib/kontena/plugin_manager/cleaner.rb +33 -0
- data/lib/kontena/plugin_manager/common.rb +86 -0
- data/lib/kontena/plugin_manager/installer.rb +54 -0
- data/lib/kontena/plugin_manager/loader.rb +93 -0
- data/lib/kontena/plugin_manager/rubygems_client.rb +42 -23
- data/lib/kontena/plugin_manager/uninstaller.rb +34 -0
- data/lib/kontena/util.rb +24 -0
- data/lib/kontena_cli.rb +1 -0
- data/omnibus/config/projects/kontena.rb +7 -1
- data/omnibus/config/software/{kontena.rb → kontena-cli.rb} +2 -0
- data/spec/fixtures/api/node.json +2 -1
- data/spec/fixtures/stack-internal-extend.yml +6 -1
- data/spec/fixtures/stack-with-dependencies-dep-1-1.yml +8 -0
- data/spec/fixtures/stack-with-dependencies-dep-1.yml +17 -0
- data/spec/fixtures/stack-with-dependencies-dep-2.yml +8 -0
- data/spec/fixtures/stack-with-dependencies-dep-3.yml +5 -0
- data/spec/fixtures/stack-with-dependencies-dep_2-removed.yml +17 -0
- data/spec/fixtures/stack-with-dependencies-dep_3-added.yml +25 -0
- data/spec/fixtures/stack-with-dependencies.yml +22 -0
- data/spec/fixtures/stack-with-variables.yml +3 -0
- data/spec/kontena/cli/etcd/health_command_spec.rb +45 -33
- data/spec/kontena/cli/helpers/exec_helper_spec.rb +2 -1
- data/spec/kontena/cli/master/init_cloud_command_spec.rb +14 -0
- data/spec/kontena/cli/nodes/health_command_spec.rb +74 -10
- data/spec/kontena/cli/nodes/list_command_spec.rb +381 -232
- data/spec/kontena/cli/nodes/show_command_spec.rb +31 -0
- data/spec/kontena/cli/nodes/ssh_command_spec.rb +18 -3
- data/spec/kontena/cli/plugins/install_command_spec.rb +1 -1
- data/spec/kontena/cli/stacks/build_command_spec.rb +6 -12
- data/spec/kontena/cli/stacks/common_spec.rb +42 -69
- data/spec/kontena/cli/stacks/install_command_spec.rb +57 -31
- data/spec/kontena/cli/stacks/list_command_spec.rb +44 -0
- data/spec/kontena/cli/stacks/logs_command_spec.rb +12 -1
- data/spec/kontena/cli/stacks/remove_command_spec.rb +39 -0
- data/spec/kontena/cli/stacks/show_command_spec.rb +16 -0
- data/spec/kontena/cli/stacks/stack_name_spec.rb +21 -0
- data/spec/kontena/cli/stacks/upgrade_command_spec.rb +73 -56
- data/spec/kontena/cli/stacks/validate_command_spec.rb +81 -0
- data/spec/kontena/cli/stacks/yaml/custom_validators/affinities_validator_spec.rb +22 -0
- data/spec/kontena/cli/stacks/yaml/reader_spec.rb +173 -169
- data/spec/kontena/cli/stacks/yaml/service_extender_spec.rb +12 -3
- data/spec/kontena/cli/stacks/yaml/stack_file_loader/file_loader_spec.rb +47 -0
- data/spec/kontena/cli/stacks/yaml/stack_file_loader/registry_loader_spec.rb +53 -0
- data/spec/kontena/cli/stacks/yaml/stack_file_loader/uri_loader_spec.rb +53 -0
- data/spec/kontena/cli/stacks/yaml/stack_file_loader_spec.rb +104 -0
- data/spec/kontena/cli/stacks/yaml/validator_v3_spec.rb +19 -0
- data/spec/kontena/plugin_manager/cleaner_spec.rb +20 -0
- data/spec/kontena/plugin_manager/common_spec.rb +39 -0
- data/spec/kontena/plugin_manager/installer_spec.rb +50 -0
- data/spec/kontena/plugin_manager/loader_spec.rb +5 -0
- data/spec/kontena/plugin_manager/rubygems_client_spec.rb +11 -25
- data/spec/kontena/plugin_manager/uninstaller_spec.rb +19 -0
- data/spec/kontena/plugin_manager_spec.rb +7 -7
- metadata +64 -97
- data/lib/kontena/cli/app_command.rb +0 -22
- data/lib/kontena/cli/apps/build_command.rb +0 -28
- data/lib/kontena/cli/apps/common.rb +0 -172
- data/lib/kontena/cli/apps/config_command.rb +0 -25
- data/lib/kontena/cli/apps/deploy_command.rb +0 -137
- data/lib/kontena/cli/apps/docker_compose_generator.rb +0 -61
- data/lib/kontena/cli/apps/docker_helper.rb +0 -80
- data/lib/kontena/cli/apps/dockerfile_generator.rb +0 -16
- data/lib/kontena/cli/apps/init_command.rb +0 -89
- data/lib/kontena/cli/apps/kontena_yml_generator.rb +0 -105
- data/lib/kontena/cli/apps/list_command.rb +0 -59
- data/lib/kontena/cli/apps/logs_command.rb +0 -37
- data/lib/kontena/cli/apps/monitor_command.rb +0 -93
- data/lib/kontena/cli/apps/remove_command.rb +0 -74
- data/lib/kontena/cli/apps/restart_command.rb +0 -39
- data/lib/kontena/cli/apps/scale_command.rb +0 -33
- data/lib/kontena/cli/apps/service_generator.rb +0 -114
- data/lib/kontena/cli/apps/service_generator_v2.rb +0 -27
- data/lib/kontena/cli/apps/show_command.rb +0 -23
- data/lib/kontena/cli/apps/start_command.rb +0 -40
- data/lib/kontena/cli/apps/stop_command.rb +0 -40
- data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +0 -19
- data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +0 -22
- data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +0 -20
- data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +0 -54
- data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +0 -22
- data/lib/kontena/cli/apps/yaml/reader.rb +0 -213
- data/lib/kontena/cli/apps/yaml/service_extender.rb +0 -77
- data/lib/kontena/cli/apps/yaml/validations.rb +0 -71
- data/lib/kontena/cli/apps/yaml/validator.rb +0 -38
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +0 -53
- data/spec/fixtures/app.json +0 -42
- data/spec/fixtures/health.yml +0 -26
- data/spec/fixtures/kontena-build.yml +0 -16
- data/spec/fixtures/kontena-internal-extend.yml +0 -8
- data/spec/fixtures/kontena-invalid.yml +0 -4
- data/spec/fixtures/kontena-with-env-file.yml +0 -18
- data/spec/fixtures/kontena-with-variables.yml +0 -19
- data/spec/fixtures/kontena.yml +0 -17
- data/spec/fixtures/kontena_build_v2.yml +0 -26
- data/spec/fixtures/kontena_numeric_version.yml +0 -9
- data/spec/fixtures/kontena_v2.yml +0 -35
- data/spec/fixtures/mysql.yml +0 -3
- data/spec/fixtures/wordpress-scaled.yml +0 -3
- data/spec/fixtures/wordpress.yml +0 -2
- data/spec/kontena/cli/app/build_command_spec.rb +0 -55
- data/spec/kontena/cli/app/common_spec.rb +0 -110
- data/spec/kontena/cli/app/config_command_spec.rb +0 -78
- data/spec/kontena/cli/app/deploy_command_spec.rb +0 -217
- data/spec/kontena/cli/app/docker_helper_spec.rb +0 -155
- data/spec/kontena/cli/app/init_command_spec.rb +0 -109
- data/spec/kontena/cli/app/logs_command_spec.rb +0 -131
- data/spec/kontena/cli/app/scale_spec.rb +0 -51
- data/spec/kontena/cli/app/service_generator_spec.rb +0 -384
- data/spec/kontena/cli/app/service_generator_v2_spec.rb +0 -73
- data/spec/kontena/cli/app/yaml/reader_spec.rb +0 -457
- data/spec/kontena/cli/app/yaml/service_extender_spec.rb +0 -127
- data/spec/kontena/cli/app/yaml/validator_spec.rb +0 -380
- data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +0 -301
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require_relative '../../../util'
|
|
2
|
-
|
|
3
1
|
module Kontena::Cli::Stacks
|
|
4
2
|
module YAML
|
|
5
3
|
class ServiceExtender
|
|
@@ -14,7 +12,7 @@ module Kontena::Cli::Stacks
|
|
|
14
12
|
# @param [Hash] from
|
|
15
13
|
# @return [Hash]
|
|
16
14
|
def extend_from(from)
|
|
17
|
-
service_config['environment'] = extend_env_vars(from['
|
|
15
|
+
service_config['environment'] = extend_env_vars(from['env'], service_config['environment'])
|
|
18
16
|
service_config['secrets'] = extend_secrets( from['secrets'], service_config['secrets'])
|
|
19
17
|
build_args = extend_build_args(safe_dig(from, 'build', 'args'), safe_dig(service_config, 'build', 'args'))
|
|
20
18
|
unless build_args.empty?
|
|
@@ -26,6 +24,10 @@ module Kontena::Cli::Stacks
|
|
|
26
24
|
|
|
27
25
|
private
|
|
28
26
|
|
|
27
|
+
def env_to_hash(env_array)
|
|
28
|
+
env_array.map { |env| env.split('=', 2) }.to_h
|
|
29
|
+
end
|
|
30
|
+
|
|
29
31
|
# Takes two arrays of "key=value" pairs and merges them. Keys in "from"-array
|
|
30
32
|
# will not overwrite keys that already exist in "to"-array.
|
|
31
33
|
#
|
|
@@ -33,15 +35,7 @@ module Kontena::Cli::Stacks
|
|
|
33
35
|
# @param [Array] to
|
|
34
36
|
# @return [Array]
|
|
35
37
|
def extend_env_vars(from, to)
|
|
36
|
-
|
|
37
|
-
if from
|
|
38
|
-
from.each do |env|
|
|
39
|
-
env_vars << env unless to && to.find do |key|
|
|
40
|
-
key.split('=').first == env.split('=').first
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
env_vars
|
|
38
|
+
env_to_hash(from || []).merge(env_to_hash(to || [])).map { |k,v| [k.to_s, v.to_s].join('=') }
|
|
45
39
|
end
|
|
46
40
|
|
|
47
41
|
# Takes two arrays of hashes containing { 'secret' => 'str', 'type' => 'str', 'name' => 'str' }
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require_relative '../stack_name'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require_relative 'reader'
|
|
4
|
+
|
|
5
|
+
module Kontena::Cli::Stacks
|
|
6
|
+
module YAML
|
|
7
|
+
class StackFileLoader
|
|
8
|
+
# A base class for loading stack files. You can define more loaders by
|
|
9
|
+
# inheriting from this class.
|
|
10
|
+
#
|
|
11
|
+
# The purpose of StackFileLoader is to provide a generic interface for
|
|
12
|
+
# loading stack YAML's from different sources, such as local files,
|
|
13
|
+
# stack registry or URLs
|
|
14
|
+
|
|
15
|
+
def self.inherited(where)
|
|
16
|
+
loaders << where
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.loaders
|
|
20
|
+
@loaders ||= []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The main interface for getting a new loader
|
|
24
|
+
#
|
|
25
|
+
# @param source [String] stack file source string (filename, url, ..)
|
|
26
|
+
# @param parent [StackFileLoader] define a parent for recursion
|
|
27
|
+
# @return [StackFileLoader]
|
|
28
|
+
def self.for(source, parent = nil)
|
|
29
|
+
loader = loaders.find { |l| l.match?(source, parent) }
|
|
30
|
+
if loader.nil?
|
|
31
|
+
raise RuntimeError, "Not found: no such file #{source} or invalid uri scheme"
|
|
32
|
+
end
|
|
33
|
+
loader.new(source, parent)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_reader :source, :parent
|
|
37
|
+
|
|
38
|
+
# @param source [String] stack file source string (filename, url, ..)
|
|
39
|
+
# @param parent [StackFileLoader] define a parent for recursion
|
|
40
|
+
# @return [StackFileLoader]
|
|
41
|
+
def initialize(source, parent = nil)
|
|
42
|
+
@source = source
|
|
43
|
+
@parent = parent
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Hash] a hash parsed from the YAML content
|
|
47
|
+
def yaml
|
|
48
|
+
@yaml ||= ::YAML.safe_load(content, [], [], true, source)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [String] raw file content
|
|
52
|
+
def content
|
|
53
|
+
@content ||= read_content
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def read_content
|
|
57
|
+
raise "Implement in inheriting class"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [StackName] an accessor to StackName for the target file
|
|
61
|
+
def stack_name
|
|
62
|
+
@stack_name ||= Kontena::Cli::Stacks::StackName.new(yaml['stack'], yaml['version'])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Reader] an accessor to YAML::Reader for the target file
|
|
66
|
+
def reader(*args)
|
|
67
|
+
@reader ||= Reader.new(self, *args)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Builds an array of hashes that represent the dependency tree starting
|
|
71
|
+
# from the target file. Unless recurse is set to false, the tree will
|
|
72
|
+
# contain also nested dependencies from any child stacks.
|
|
73
|
+
#
|
|
74
|
+
# @param recurse [TrueClass,FalseClass] recurse child dependencies?
|
|
75
|
+
# @return [Array<Hash>] an array of hashes ('name', 'stack', 'variables', and 'depends')
|
|
76
|
+
def dependencies(recurse: true)
|
|
77
|
+
return @dependencies if @dependencies
|
|
78
|
+
depends = yaml['depends']
|
|
79
|
+
if depends.nil? || depends.empty?
|
|
80
|
+
@dependencies = nil
|
|
81
|
+
else
|
|
82
|
+
@dependencies = depends.map do |name, dependency|
|
|
83
|
+
reader = StackFileLoader.for(dependency['stack'], self)
|
|
84
|
+
deps = { 'name' => name, 'stack' => reader.source, 'variables' => dependency.fetch('variables', Hash.new) }
|
|
85
|
+
if recurse
|
|
86
|
+
child_deps = reader.dependencies
|
|
87
|
+
deps['depends'] = child_deps unless child_deps.nil?
|
|
88
|
+
end
|
|
89
|
+
deps
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Dir[File.expand_path('../stack_file_loader/*.rb', __FILE__)].each { |f| require f }
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module Kontena::Cli::Stacks
|
|
4
|
+
module YAML
|
|
5
|
+
class FileLoader < StackFileLoader
|
|
6
|
+
def self.match?(source, parent = nil)
|
|
7
|
+
::File.exist?(with_context(source, parent))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.is_file?(parent)
|
|
11
|
+
parent.is_a?(FileLoader)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.with_context(source, parent = nil)
|
|
15
|
+
if is_file?(parent)
|
|
16
|
+
File.join(File.dirname(parent.source), source)
|
|
17
|
+
else
|
|
18
|
+
File.absolute_path(source)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(*args)
|
|
23
|
+
super
|
|
24
|
+
@source = self.class.with_context(@source, parent)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def read_content
|
|
28
|
+
::File.read(source)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def origin
|
|
32
|
+
"file"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def registry
|
|
36
|
+
"file://"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Kontena::Cli::Stacks
|
|
2
|
+
module YAML
|
|
3
|
+
class RegistryLoader < StackFileLoader
|
|
4
|
+
def self.match?(source, parent = nil)
|
|
5
|
+
source =~ /\A[a-zA-Z0-9\_\.\-]+\/[a-zA-Z0-9\_\.\-]+(?::.*)?\z/ && !FileLoader.match?(source, parent)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def read_content
|
|
9
|
+
Kontena::StacksCache.pull(source)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def origin
|
|
13
|
+
"registry"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def registry
|
|
17
|
+
account = Kontena::Cli::Config.current_account
|
|
18
|
+
raise "Current account not set" if account.nil?
|
|
19
|
+
account.stacks_url
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Kontena::Cli::Stacks
|
|
2
|
+
module YAML
|
|
3
|
+
class UriLoader < StackFileLoader
|
|
4
|
+
def self.match?(source, parent = nil)
|
|
5
|
+
source.include?('://') && !FileLoader.match?(source, parent)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def read_content
|
|
9
|
+
require 'open-uri'
|
|
10
|
+
stream = open(source)
|
|
11
|
+
stream.read
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def origin
|
|
15
|
+
"uri"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def registry
|
|
19
|
+
"file://"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -6,6 +6,7 @@ module Kontena::Cli::Stacks::YAML
|
|
|
6
6
|
require_relative 'custom_validators/extends_validator'
|
|
7
7
|
require_relative 'custom_validators/hooks_validator'
|
|
8
8
|
require_relative 'custom_validators/secrets_validator'
|
|
9
|
+
require_relative 'custom_validators/certificates_validator'
|
|
9
10
|
|
|
10
11
|
def self.load
|
|
11
12
|
return if @loaded
|
|
@@ -13,6 +14,7 @@ module Kontena::Cli::Stacks::YAML
|
|
|
13
14
|
HashValidator.append_validator(BuildValidator.new)
|
|
14
15
|
HashValidator.append_validator(ExtendsValidator.new)
|
|
15
16
|
HashValidator.append_validator(SecretsValidator.new)
|
|
17
|
+
HashValidator.append_validator(CertificatesValidator.new)
|
|
16
18
|
HashValidator.append_validator(HooksValidator.new)
|
|
17
19
|
@loaded = true
|
|
18
20
|
end
|
|
@@ -27,10 +29,12 @@ module Kontena::Cli::Stacks::YAML
|
|
|
27
29
|
'cap_add' => optional('array'),
|
|
28
30
|
'cap_drop' => optional('array'),
|
|
29
31
|
'command' => optional('string'),
|
|
32
|
+
'cpus' => optional('float'),
|
|
30
33
|
'cpu_shares' => optional('integer'),
|
|
31
34
|
'external_links' => optional('array'),
|
|
32
35
|
'mem_limit' => optional('string'),
|
|
33
36
|
'mem_swaplimit' => optional('string'),
|
|
37
|
+
'shm_size' => optional('string'),
|
|
34
38
|
'environment' => optional(-> (value) {
|
|
35
39
|
if value.is_a?(Hash)
|
|
36
40
|
value.all? do |k,v|
|
|
@@ -58,6 +62,7 @@ module Kontena::Cli::Stacks::YAML
|
|
|
58
62
|
'volumes' => optional('array'),
|
|
59
63
|
'volumes_from' => optional('array'),
|
|
60
64
|
'secrets' => optional('stacks_valid_secrets'),
|
|
65
|
+
'certificates' => optional('stacks_valid_certificates'),
|
|
61
66
|
'hooks' => optional('stacks_valid_hooks'),
|
|
62
67
|
'only_if' => optional(-> (value) { value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array) }),
|
|
63
68
|
'skip_if' => optional(-> (value) { value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array) }),
|
|
@@ -92,10 +97,21 @@ module Kontena::Cli::Stacks::YAML
|
|
|
92
97
|
HashValidator.validate(volume_config, volume_schema, true)
|
|
93
98
|
end
|
|
94
99
|
|
|
100
|
+
def validate_dependencies(dependency_config)
|
|
101
|
+
HashValidator.validate(dependency_config, dependency_schema, true)
|
|
102
|
+
end
|
|
103
|
+
|
|
95
104
|
def volume_schema
|
|
96
105
|
{
|
|
97
106
|
'external' => optional(-> (value) { value.is_a?(TrueClass) || (value.is_a?(Hash) && value['name'].is_a?(String)) })
|
|
98
107
|
}
|
|
99
108
|
end
|
|
109
|
+
|
|
110
|
+
def dependency_schema
|
|
111
|
+
{
|
|
112
|
+
'stack' => optional('string'),
|
|
113
|
+
'variables' => optional(-> (value) { value.is_a?(Hash) })
|
|
114
|
+
}
|
|
115
|
+
end
|
|
100
116
|
end
|
|
101
117
|
end
|
|
@@ -6,7 +6,7 @@ module Kontena::Cli::Stacks
|
|
|
6
6
|
require_relative 'validations'
|
|
7
7
|
include Validations
|
|
8
8
|
|
|
9
|
-
KNOWN_TOP_LEVEL_KEYS = %
|
|
9
|
+
KNOWN_TOP_LEVEL_KEYS = %i(
|
|
10
10
|
services
|
|
11
11
|
errors
|
|
12
12
|
volumes
|
|
@@ -17,6 +17,7 @@ module Kontena::Cli::Stacks
|
|
|
17
17
|
data
|
|
18
18
|
description
|
|
19
19
|
expose
|
|
20
|
+
depends
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
def initialize
|
|
@@ -26,7 +27,7 @@ module Kontena::Cli::Stacks
|
|
|
26
27
|
@schema['network_mode'] = optional(%w(host bridge))
|
|
27
28
|
@schema['logging'] = optional({
|
|
28
29
|
'driver' => optional('string'),
|
|
29
|
-
'options' => optional(-> (value) { value.
|
|
30
|
+
'options' => optional(-> (value) { value.kind_of?(Hash) })
|
|
30
31
|
})
|
|
31
32
|
Validations::CustomValidators.load
|
|
32
33
|
end
|
|
@@ -59,14 +60,16 @@ module Kontena::Cli::Stacks
|
|
|
59
60
|
notifications: []
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
yaml.keys.each do |key|
|
|
64
|
+
unless KNOWN_TOP_LEVEL_KEYS.include?(key) || KNOWN_TOP_LEVEL_KEYS.include?(key.to_sym)
|
|
65
|
+
result[:notifications] << { key.to_s => "unknown top level key" }
|
|
66
|
+
end
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
if yaml.key?('services')
|
|
67
|
-
if yaml['services'].
|
|
70
|
+
if yaml['services'].kind_of?(Hash)
|
|
68
71
|
yaml['services'].each do |service, options|
|
|
69
|
-
unless options.
|
|
72
|
+
unless options.kind_of?(Hash)
|
|
70
73
|
result[:errors] << { 'services' => { service => { 'options' => "must be a mapping not a #{options.class}"} } }
|
|
71
74
|
next
|
|
72
75
|
end
|
|
@@ -107,9 +110,9 @@ module Kontena::Cli::Stacks
|
|
|
107
110
|
end
|
|
108
111
|
|
|
109
112
|
if yaml.key?('volumes')
|
|
110
|
-
if yaml['volumes'].
|
|
113
|
+
if yaml['volumes'].kind_of?(Hash)
|
|
111
114
|
yaml['volumes'].each do |volume, options|
|
|
112
|
-
if options.
|
|
115
|
+
if options.kind_of?(Hash)
|
|
113
116
|
option_errors = validate_volume_options(options)
|
|
114
117
|
unless option_errors.valid?
|
|
115
118
|
result[:errors] << { 'volumes' => { volume => option_errors.errors } }
|
|
@@ -130,6 +133,20 @@ module Kontena::Cli::Stacks
|
|
|
130
133
|
if (yaml['volumes'].nil? || yaml['volumes'].empty?) && (yaml['services'].nil? || yaml['services'].empty?)
|
|
131
134
|
result[:errors] << { 'file' => 'does not list any services or volumes' }
|
|
132
135
|
end
|
|
136
|
+
|
|
137
|
+
if yaml.key?('depends')
|
|
138
|
+
unless yaml['depends'].kind_of?(Hash)
|
|
139
|
+
result[:errors] << { 'depends' => "Must be a mapping, not #{yaml['depends'].class}" }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
yaml['depends'].each do |name, dependency_options|
|
|
143
|
+
validator = validate_dependencies(dependency_options)
|
|
144
|
+
result[:errors] << { 'depends' => { name => validator.errors } } unless validator.valid?
|
|
145
|
+
if yaml.key?('services') && yaml['services'][name]
|
|
146
|
+
result[:errors] << { 'depends' => { name => 'is defined both as service and dependency name' } }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
133
150
|
result
|
|
134
151
|
end
|
|
135
152
|
end
|
data/lib/kontena/client.rb
CHANGED
|
@@ -50,9 +50,9 @@ module Kontena
|
|
|
50
50
|
|
|
51
51
|
excon_opts = {
|
|
52
52
|
omit_default_port: true,
|
|
53
|
-
connect_timeout: ENV["EXCON_CONNECT_TIMEOUT"] ? ENV["EXCON_CONNECT_TIMEOUT"].to_i :
|
|
53
|
+
connect_timeout: ENV["EXCON_CONNECT_TIMEOUT"] ? ENV["EXCON_CONNECT_TIMEOUT"].to_i : 10,
|
|
54
54
|
read_timeout: ENV["EXCON_READ_TIMEOUT"] ? ENV["EXCON_READ_TIMEOUT"].to_i : 30,
|
|
55
|
-
write_timeout: ENV["EXCON_WRITE_TIMEOUT"] ? ENV["EXCON_WRITE_TIMEOUT"].to_i :
|
|
55
|
+
write_timeout: ENV["EXCON_WRITE_TIMEOUT"] ? ENV["EXCON_WRITE_TIMEOUT"].to_i : 10,
|
|
56
56
|
ssl_verify_peer: ignore_ssl_errors? ? false : true
|
|
57
57
|
}
|
|
58
58
|
if ENV["DEBUG"]
|
data/lib/kontena/command.rb
CHANGED
|
@@ -183,6 +183,17 @@ class Kontena::Command < Clamp::Command
|
|
|
183
183
|
false
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
+
# Returns an instance of the command, just like with Kontena.run! but before calling "execute"
|
|
187
|
+
# You can use it for specs or reuse of instancemethods.
|
|
188
|
+
# Example:
|
|
189
|
+
# cmd = Kontena::FooCommand.instance(['-n', 'foo'])
|
|
190
|
+
# cmd.fetch_stuff
|
|
191
|
+
def instance(arguments)
|
|
192
|
+
@arguments = arguments
|
|
193
|
+
parse @arguments
|
|
194
|
+
self
|
|
195
|
+
end
|
|
196
|
+
|
|
186
197
|
def run(arguments)
|
|
187
198
|
Kontena.logger.debug { "Running #{self.class.name} with #{arguments.inspect} -- callback matcher = '#{self.class.callback_matcher.nil? ? "nil" : self.class.callback_matcher.map(&:to_s).join(' ')}'" }
|
|
188
199
|
@arguments = arguments
|
data/lib/kontena/main_command.rb
CHANGED
|
@@ -29,7 +29,6 @@ class Kontena::MainCommand < Kontena::Command
|
|
|
29
29
|
subcommand "registry", "Registry specific commands", load_subcommand('registry_command')
|
|
30
30
|
subcommand "external-registry", "External registry specific commands", load_subcommand('external_registry_command')
|
|
31
31
|
subcommand "volume", "Volume specific commands [EXPERIMENTAL]", load_subcommand('volume_command')
|
|
32
|
-
subcommand "app", "App specific commands", load_subcommand('app_command')
|
|
33
32
|
subcommand "plugin", "Plugin specific commands", load_subcommand('plugin_command')
|
|
34
33
|
subcommand "whoami", "Shows current logged in user", load_subcommand('whoami_command')
|
|
35
34
|
subcommand "version", "Show CLI and current master version", load_subcommand('version_command')
|
|
@@ -54,6 +53,9 @@ class Kontena::MainCommand < Kontena::Command
|
|
|
54
53
|
elsif name == 'logout'
|
|
55
54
|
exit_with_error "Use 'kontena master logout' to log out from a Kontena Master\n"+
|
|
56
55
|
" or 'kontena cloud logout' for logging out from your Kontena Cloud account"
|
|
56
|
+
elsif name == 'app'
|
|
57
|
+
exit_with_error "The deprecated app subcommand has been moved into a plugin. You can install\n" +
|
|
58
|
+
" it by using 'kontena plugin install app-command'"
|
|
57
59
|
end
|
|
58
60
|
super
|
|
59
61
|
end
|
|
@@ -1,213 +1,26 @@
|
|
|
1
|
-
require 'singleton'
|
|
2
|
-
|
|
3
1
|
module Kontena
|
|
4
|
-
|
|
2
|
+
module PluginManager
|
|
5
3
|
autoload :RubygemsClient, 'kontena/plugin_manager/rubygems_client'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Gem.autoload :StreamUI, 'rubygems/user_interaction'
|
|
12
|
-
Gem::Commands.autoload :CleanupCommand, 'rubygems/commands/cleanup_command'
|
|
13
|
-
|
|
14
|
-
include Singleton
|
|
15
|
-
|
|
16
|
-
CLI_GEM = 'kontena-cli'.freeze
|
|
17
|
-
MIN_CLI_VERSION = '0.15.99'.freeze
|
|
4
|
+
autoload :Loader, 'kontena/plugin_manager/loader'
|
|
5
|
+
autoload :Installer, 'kontena/plugin_manager/installer'
|
|
6
|
+
autoload :Uninstaller, 'kontena/plugin_manager/uninstaller'
|
|
7
|
+
autoload :Cleaner, 'kontena/plugin_manager/cleaner'
|
|
8
|
+
autoload :Common, 'kontena/plugin_manager/common'
|
|
18
9
|
|
|
19
10
|
# Initialize plugin manager
|
|
20
11
|
def init
|
|
21
|
-
ENV["GEM_HOME"] = install_dir
|
|
12
|
+
ENV["GEM_HOME"] = Common.install_dir
|
|
22
13
|
Gem.paths = ENV
|
|
14
|
+
Common.use_dummy_ui unless ENV["DEBUG"]
|
|
23
15
|
plugins
|
|
24
|
-
use_dummy_ui unless ENV["DEBUG"]
|
|
25
16
|
true
|
|
26
17
|
end
|
|
27
|
-
|
|
28
|
-
# Install a plugin
|
|
29
|
-
# @param plugin_name [String]
|
|
30
|
-
# @param pre [Boolean] install a prerelease version if available
|
|
31
|
-
# @param version [String] install a specific version
|
|
32
|
-
def install_plugin(plugin_name, pre: false, version: nil)
|
|
33
|
-
cmd = Gem::DependencyInstaller.new(
|
|
34
|
-
document: false,
|
|
35
|
-
force: true,
|
|
36
|
-
prerelease: pre,
|
|
37
|
-
minimal_deps: true
|
|
38
|
-
)
|
|
39
|
-
plugin_version = version.nil? ? Gem::Requirement.default : Gem::Requirement.new(version)
|
|
40
|
-
cmd.install(prefix(plugin_name), plugin_version)
|
|
41
|
-
cmd.installed_gems
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Uninstall a plugin
|
|
45
|
-
# @param plugin_name [String]
|
|
46
|
-
def uninstall_plugin(plugin_name)
|
|
47
|
-
installed = installed(plugin_name)
|
|
48
|
-
raise "Plugin #{plugin_name} not installed" unless installed
|
|
49
|
-
|
|
50
|
-
cmd = Gem::Uninstaller.new(
|
|
51
|
-
installed.name,
|
|
52
|
-
all: true,
|
|
53
|
-
executables: true,
|
|
54
|
-
force: true,
|
|
55
|
-
install_dir: installed.base_dir
|
|
56
|
-
)
|
|
57
|
-
cmd.uninstall
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Search rubygems for kontena plugins
|
|
61
|
-
# @param pattern [String] optional search pattern
|
|
62
|
-
def search_plugins(pattern = nil)
|
|
63
|
-
RubygemsClient.new.search(prefix(pattern))
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Retrieve plugin versions from rubygems
|
|
67
|
-
# @param plugin_name [String]
|
|
68
|
-
def gem_versions(plugin_name)
|
|
69
|
-
RubygemsClient.new.versions(prefix(plugin_name))
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Get the latest version number from rubygems
|
|
73
|
-
# @param plugin_name [String]
|
|
74
|
-
# @param pre [Boolean] include prerelease versions
|
|
75
|
-
def latest_version(plugin_name, pre: false)
|
|
76
|
-
return gem_versions(plugin_name).first if pre
|
|
77
|
-
gem_versions(plugin_name).find { |version| !version.prerelease? }
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Find a plugin by name from installed plugins
|
|
81
|
-
# @param plugin_name [String]
|
|
82
|
-
def installed(plugin_name)
|
|
83
|
-
search = prefix(plugin_name)
|
|
84
|
-
plugins.find {|plugin| plugin.name == search }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Upgrade an installed plugin
|
|
88
|
-
# @param plugin_name [String]
|
|
89
|
-
# @param pre [Boolean] upgrade to a prerelease version if available. Will happen always when the installed version is a prerelease version.
|
|
90
|
-
def upgrade_plugin(plugin_name, pre: false)
|
|
91
|
-
installed = installed(plugin_name)
|
|
92
|
-
if installed.version.prerelease?
|
|
93
|
-
pre = true
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
if installed
|
|
97
|
-
latest = latest_version(plugin_name, pre: pre)
|
|
98
|
-
if latest > installed.version
|
|
99
|
-
install_plugin(plugin_name, version: latest.to_s)
|
|
100
|
-
end
|
|
101
|
-
else
|
|
102
|
-
raise "Plugin #{plugin_name} not installed"
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Runs gem cleanup, removes remains from previous versions
|
|
107
|
-
# @param plugin_name [String]
|
|
108
|
-
def cleanup_plugin(plugin_name)
|
|
109
|
-
cmd = Gem::Commands::CleanupCommand.new
|
|
110
|
-
options = []
|
|
111
|
-
options += ['-q', '--no-verbose'] unless ENV["DEBUG"]
|
|
112
|
-
cmd.handle_options options
|
|
113
|
-
cmd.execute
|
|
114
|
-
rescue Gem::SystemExitException => e
|
|
115
|
-
return true if e.exit_code == 0
|
|
116
|
-
raise
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Gem installation directory
|
|
120
|
-
# @return [String]
|
|
121
|
-
def install_dir
|
|
122
|
-
return @install_dir if @install_dir
|
|
123
|
-
install_dir = File.join(Dir.home, '.kontena', 'gems', RUBY_VERSION)
|
|
124
|
-
unless File.directory?(install_dir)
|
|
125
|
-
require 'fileutils'
|
|
126
|
-
FileUtils.mkdir_p(install_dir, mode: 0700)
|
|
127
|
-
end
|
|
128
|
-
@install_dir = install_dir
|
|
129
|
-
end
|
|
130
|
-
|
|
18
|
+
module_function :init
|
|
131
19
|
|
|
132
20
|
# @return [Array<Gem::Specification>]
|
|
133
21
|
def plugins
|
|
134
|
-
@plugins ||= load_plugins
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
private
|
|
138
|
-
|
|
139
|
-
def plugin_debug?
|
|
140
|
-
@plugin_debug ||= ENV['DEBUG'] == 'plugin'
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def load_plugins
|
|
144
|
-
plugins = []
|
|
145
|
-
Gem::Specification.to_a.each do |spec|
|
|
146
|
-
spec.require_paths.to_a.each do |require_path|
|
|
147
|
-
plugin = File.join(spec.gem_dir, require_path, 'kontena_cli_plugin.rb')
|
|
148
|
-
if File.exist?(plugin) && !plugins.find{ |p| p.name == spec.name }
|
|
149
|
-
begin
|
|
150
|
-
if spec_has_valid_dependency?(spec)
|
|
151
|
-
|
|
152
|
-
loaded_features_before = $LOADED_FEATURES.dup
|
|
153
|
-
load_path_before = $LOAD_PATH.dup
|
|
154
|
-
|
|
155
|
-
Kontena.logger.debug { "Activating plugin #{spec.name}" } if plugin_debug?
|
|
156
|
-
spec.activate
|
|
157
|
-
spec.activate_dependencies
|
|
158
|
-
|
|
159
|
-
Kontena.logger.debug { "Loading plugin #{spec.name}" }
|
|
160
|
-
require(plugin)
|
|
161
|
-
Kontena.logger.debug { "Loaded plugin #{spec.name}" } if plugin_debug?
|
|
162
|
-
|
|
163
|
-
if plugin_debug?
|
|
164
|
-
added_features = ($LOADED_FEATURES - loaded_features_before).map {|feat| "- #{feat}"}
|
|
165
|
-
added_paths = ($LOAD_PATH - load_path_before).map {|feat| "- #{feat}"}
|
|
166
|
-
Kontena.logger.debug { "Plugin manager loaded features for #{spec.name}:" } unless added_features.empty?
|
|
167
|
-
added_features.each { |feat| Kontena.logger.debug { feat } }
|
|
168
|
-
Kontena.logger.debug { "Plugin manager load paths added for #{spec.name}:" } unless added_paths.empty?
|
|
169
|
-
added_paths.each { |path| Kontena.logger.debug { path } }
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
plugins << spec
|
|
173
|
-
else
|
|
174
|
-
plugin_name = spec.name.sub('kontena-plugin-', '')
|
|
175
|
-
$stderr.puts " [#{Kontena.pastel.red('error')}] Plugin #{Kontena.pastel.cyan(plugin_name)} (#{spec.version}) is not compatible with the current cli version."
|
|
176
|
-
$stderr.puts " To update the plugin, run 'kontena plugin install #{plugin_name}'"
|
|
177
|
-
end
|
|
178
|
-
rescue ScriptError, StandardError => ex
|
|
179
|
-
warn " [#{Kontena.pastel.red('error')}] Failed to load plugin: #{spec.name} from #{spec.gem_dir}\n\tRerun the command with environment DEBUG=true set to get the full exception."
|
|
180
|
-
Kontena.logger.error(ex)
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
plugins
|
|
186
|
-
rescue => ex
|
|
187
|
-
$stderr.puts Kontena.pastel.red(ex.message)
|
|
188
|
-
Kontena.logger.error(ex)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def prefix(plugin_name)
|
|
192
|
-
return plugin_name if plugin_name.to_s.start_with?('kontena-plugin-')
|
|
193
|
-
"kontena-plugin-#{plugin_name}"
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def dummy_ui
|
|
197
|
-
Gem::StreamUI.new(StringIO.new, StringIO.new, StringIO.new, false)
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def use_dummy_ui
|
|
201
|
-
Gem::DefaultUserInteraction.ui = dummy_ui
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# @param [Gem::Specification] spec
|
|
205
|
-
# @return [Boolean]
|
|
206
|
-
def spec_has_valid_dependency?(spec)
|
|
207
|
-
kontena_cli = spec.runtime_dependencies.find{ |d| d.name == CLI_GEM }
|
|
208
|
-
!kontena_cli.match?(CLI_GEM, MIN_CLI_VERSION)
|
|
209
|
-
rescue
|
|
210
|
-
false
|
|
22
|
+
@plugins ||= Loader.new.load_plugins
|
|
211
23
|
end
|
|
24
|
+
module_function :plugins
|
|
212
25
|
end
|
|
213
26
|
end
|