kontena-cli 1.0.2 → 1.0.3.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/kontena-cli.gemspec +1 -0
- data/lib/kontena/cli/apps/init_command.rb +2 -2
- data/lib/kontena/cli/apps/kontena_yml_generator.rb +1 -1
- data/lib/kontena/cli/apps/yaml/reader.rb +2 -2
- data/lib/kontena/cli/master/config/import_command.rb +1 -1
- data/lib/kontena/cli/services/logs_command.rb +1 -1
- data/lib/kontena/cli/services/services_helper.rb +11 -0
- data/lib/kontena/cli/stacks/build_command.rb +7 -5
- data/lib/kontena/cli/stacks/registry/show_command.rb +1 -1
- data/lib/kontena/cli/stacks/yaml/reader.rb +5 -5
- data/lib/kontena/scripts/completer +1 -1
- data/lib/kontena/stacks_cache.rb +2 -2
- data/lib/kontena_cli.rb +5 -3
- data/spec/fixtures/docker-compose-invalid.yml +12 -0
- data/spec/fixtures/stack-with-variables.yml +5 -5
- data/spec/kontena/cli/app/deploy_command_spec.rb +3 -3
- data/spec/kontena/cli/services/services_helper_spec.rb +10 -0
- data/spec/kontena/cli/stacks/build_command_spec.rb +60 -0
- data/spec/kontena/cli/stacks/yaml/reader_spec.rb +17 -0
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fea7cc00f263ca36c6b676cb044a5a3648dc0b8
|
4
|
+
data.tar.gz: d7aae3f41c4f9dab103e01114bfff4401088292f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92c78f93962b972872fc150b600cccc704711e97956e63d4f2dc9a74784c1b0ee007b26902f0df27dd26bfebcd5a93ede3ae0a5535f3fad9d156dfcc8898a060
|
7
|
+
data.tar.gz: 5a562d665b421e8c78af8c73e8ad7a9c4cad963ef3804f815f170e31f5dcb6b0be9dd648cdea90769fd86932c27e72d0548815d7d2aab887d64c2a455aba0784
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.3.rc1
|
data/kontena-cli.gemspec
CHANGED
@@ -25,12 +25,12 @@ module Kontena::Cli::Apps
|
|
25
25
|
end
|
26
26
|
|
27
27
|
if File.exist?('Procfile')
|
28
|
-
procfile = ::YAML.
|
28
|
+
procfile = ::YAML.safe_load(File.read('Procfile'))
|
29
29
|
else
|
30
30
|
procfile = {}
|
31
31
|
end
|
32
32
|
|
33
|
-
app_env = create_env_file(app_json['env']) if app_json['env']
|
33
|
+
app_env = create_env_file(app_json['env']) if app_json['env']
|
34
34
|
addons = app_json['addons'] || []
|
35
35
|
|
36
36
|
if File.exist?(docker_compose_file)
|
@@ -29,7 +29,7 @@ module Kontena::Cli::Apps
|
|
29
29
|
result[:name] = yaml['name']
|
30
30
|
result[:errors] = errors
|
31
31
|
result[:notifications] = notifications
|
32
|
-
result[:services] = parse_services(service_name)
|
32
|
+
result[:services] = errors.count == 0 ? parse_services(service_name) : {}
|
33
33
|
end
|
34
34
|
result
|
35
35
|
end
|
@@ -52,7 +52,7 @@ module Kontena::Cli::Apps
|
|
52
52
|
interpolate(content)
|
53
53
|
replace_dollar_dollars(content)
|
54
54
|
begin
|
55
|
-
@yaml = ::YAML.
|
55
|
+
@yaml = ::YAML.safe_load(content)
|
56
56
|
rescue Psych::SyntaxError => e
|
57
57
|
raise "Error while parsing #{file}".colorize(:red)+ " "+e.message
|
58
58
|
end
|
@@ -15,7 +15,7 @@ module Kontena::Cli::Services
|
|
15
15
|
require_api_url
|
16
16
|
|
17
17
|
query_params = {}
|
18
|
-
query_params[:container] =
|
18
|
+
query_params[:container] = parse_container_name(name, instance) if instance
|
19
19
|
|
20
20
|
show_logs("services/#{parse_service_id(name)}/container_logs", query_params) do |log|
|
21
21
|
show_log(log)
|
@@ -283,6 +283,17 @@ module Kontena
|
|
283
283
|
end
|
284
284
|
end
|
285
285
|
|
286
|
+
# Parses container name based on service name and instance number
|
287
|
+
# @param [String] service_name
|
288
|
+
# @param [Integer] instance_number
|
289
|
+
def parse_container_name(service_name, instance_number)
|
290
|
+
base = service_name.gsub('/', '-')
|
291
|
+
unless service_name.include?('/')
|
292
|
+
base = "null-#{base}"
|
293
|
+
end
|
294
|
+
"#{base}-#{instance_number}"
|
295
|
+
end
|
296
|
+
|
286
297
|
# @param [Array<String>] port_options
|
287
298
|
# @return [Array<Hash>]
|
288
299
|
def parse_ports(port_options)
|
@@ -15,12 +15,14 @@ module Kontena::Cli::Stacks
|
|
15
15
|
|
16
16
|
def execute
|
17
17
|
require_config_file(filename)
|
18
|
-
|
18
|
+
stack = stack_from_yaml(filename)
|
19
|
+
services = stack['services']
|
20
|
+
|
19
21
|
unless service_list.empty?
|
20
|
-
services.select! { |
|
22
|
+
services.select! { |service| service_list.include?(service['name']) }
|
21
23
|
end
|
22
24
|
|
23
|
-
if services.none?{ |
|
25
|
+
if services.none?{ |service| service['build'] }
|
24
26
|
abort 'Not found any service with a build option'.colorize(:red)
|
25
27
|
end
|
26
28
|
build_docker_images(services, no_cache?, no_pull?)
|
@@ -31,7 +33,7 @@ module Kontena::Cli::Stacks
|
|
31
33
|
# @param [Boolean] no_cache
|
32
34
|
# @param [Boolean] no_pull
|
33
35
|
def build_docker_images(services, no_cache = false, no_pull = false)
|
34
|
-
services.each do |
|
36
|
+
services.each do |service|
|
35
37
|
if service['build']
|
36
38
|
dockerfile = service['build']['dockerfile'] || 'Dockerfile'
|
37
39
|
abort("'#{service['image']}' is not valid Docker image name") unless validate_image_name(service['image'])
|
@@ -48,7 +50,7 @@ module Kontena::Cli::Stacks
|
|
48
50
|
|
49
51
|
# @param [Hash] services
|
50
52
|
def push_docker_images(services)
|
51
|
-
services.each do |
|
53
|
+
services.each do |service|
|
52
54
|
if service['build']
|
53
55
|
puts "Pushing image #{service['image'].colorize(:cyan)}"
|
54
56
|
push_docker_image(service['image'])
|
@@ -15,7 +15,7 @@ module Kontena::Cli::Stacks::Registry
|
|
15
15
|
def execute
|
16
16
|
require 'semantic'
|
17
17
|
unless versions?
|
18
|
-
stack = ::YAML.
|
18
|
+
stack = ::YAML.safe_load(stacks_client.show(stack_name, stack_version))
|
19
19
|
puts "#{stack['stack']}:"
|
20
20
|
puts " #{"latest_" unless stack_version}version: #{stack['version']}"
|
21
21
|
puts " expose: #{stack['expose'] || '-'}"
|
@@ -44,7 +44,7 @@ module Kontena::Cli::Stacks
|
|
44
44
|
return @variables if @variables
|
45
45
|
if yaml && yaml.has_key?('variables')
|
46
46
|
variables_yaml = yaml['variables'].to_yaml
|
47
|
-
variables_hash = ::YAML.
|
47
|
+
variables_hash = ::YAML.safe_load(replace_dollar_dollars(interpolate(variables_yaml, use_opto: false)))
|
48
48
|
@variables = Opto::Group.new(variables_hash, defaults: { from: :env, to: :env })
|
49
49
|
else
|
50
50
|
@variables = Opto::Group.new(defaults: { from: :env, to: :env })
|
@@ -75,14 +75,14 @@ module Kontena::Cli::Stacks
|
|
75
75
|
result[:expose] = yaml['expose']
|
76
76
|
result[:errors] = errors unless skip_validation?
|
77
77
|
result[:notifications] = notifications
|
78
|
-
result[:services] = parse_services(service_name)
|
78
|
+
result[:services] = errors.count == 0 ? parse_services(service_name) : {}
|
79
79
|
result[:variables] = variables.to_h(values_only: true).reject { |k,_| variables.option(k).to.has_key?(:vault) } unless skip_variables?
|
80
80
|
end
|
81
81
|
result
|
82
82
|
end
|
83
83
|
|
84
84
|
def stack_name
|
85
|
-
yaml = ::YAML.
|
85
|
+
yaml = ::YAML.safe_load(raw_content)
|
86
86
|
yaml['stack'].split('/').last.split(':').first if yaml['stack']
|
87
87
|
end
|
88
88
|
|
@@ -101,9 +101,9 @@ module Kontena::Cli::Stacks
|
|
101
101
|
|
102
102
|
def load_yaml(interpolate = true)
|
103
103
|
if interpolate
|
104
|
-
@yaml = ::YAML.
|
104
|
+
@yaml = ::YAML.safe_load(replace_dollar_dollars(interpolate(raw_content)))
|
105
105
|
else
|
106
|
-
@yaml = ::YAML.
|
106
|
+
@yaml = ::YAML.safe_load(raw_content)
|
107
107
|
end
|
108
108
|
rescue Psych::SyntaxError => e
|
109
109
|
raise "Error while parsing #{file}".colorize(:red)+ " "+e.message
|
data/lib/kontena/stacks_cache.rb
CHANGED
@@ -23,7 +23,7 @@ module Kontena
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def load
|
26
|
-
YAML.
|
26
|
+
YAML.safe_load(read)
|
27
27
|
end
|
28
28
|
|
29
29
|
def write(content)
|
@@ -78,7 +78,7 @@ module Kontena
|
|
78
78
|
else
|
79
79
|
dputs "Retrieving #{stack.stack}:#{stack.version} from registry"
|
80
80
|
content = client.pull(stack.stack, stack.version)
|
81
|
-
yaml = ::YAML.
|
81
|
+
yaml = ::YAML.safe_load(content)
|
82
82
|
new_stack = CachedStack.new(yaml['stack'], yaml['version'])
|
83
83
|
if new_stack.cached?
|
84
84
|
dputs "Already cached"
|
data/lib/kontena_cli.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Kontena
|
2
2
|
# Run a kontena command like it was launched from the command line.
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# @example
|
5
5
|
# Kontena.run("grid list --help")
|
6
6
|
#
|
7
|
-
# @param [String] command_line
|
7
|
+
# @param [String] command_line
|
8
8
|
# @return [Fixnum] exit_code
|
9
9
|
def self.run(cmdline = "", returning: :status)
|
10
10
|
ENV["DEBUG"] && puts("Running Kontena.run(#{cmdline.inspect}, returning: #{returning}")
|
@@ -19,7 +19,7 @@ module Kontena
|
|
19
19
|
ENV["DEBUG"] && puts("Command raised #{$!} with message: #{$!.message}\n #{$!.backtrace.join(" \n")}")
|
20
20
|
returning == :status ? 1 : nil
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
|
24
24
|
def self.version
|
25
25
|
"kontena-cli/#{Kontena::Cli::VERSION}"
|
@@ -61,6 +61,8 @@ end
|
|
61
61
|
|
62
62
|
require 'ruby_dig'
|
63
63
|
require 'shellwords'
|
64
|
+
require "safe_yaml"
|
65
|
+
SafeYAML::OPTIONS[:default_mode] = :safe
|
64
66
|
require_relative 'kontena/cli/version'
|
65
67
|
require_relative 'kontena/cli/common'
|
66
68
|
require_relative 'kontena/command'
|
@@ -6,7 +6,7 @@ variables:
|
|
6
6
|
required: true
|
7
7
|
min_length: 10
|
8
8
|
empty_is_nil: true
|
9
|
-
from:
|
9
|
+
from:
|
10
10
|
env: WORDPRESS_DB_PASSWORD # first try from local env
|
11
11
|
random_string: # if prompt returned nil, generate a random string
|
12
12
|
length: 10
|
@@ -19,10 +19,10 @@ variables:
|
|
19
19
|
random_string:
|
20
20
|
length: 16
|
21
21
|
charset: hex
|
22
|
-
to:
|
22
|
+
to:
|
23
23
|
env: test_var
|
24
24
|
TEST_ENV_VAR: # the default from/to is to set/read env of the option name
|
25
|
-
type:
|
25
|
+
type: string
|
26
26
|
|
27
27
|
services:
|
28
28
|
wordpress:
|
@@ -33,7 +33,7 @@ services:
|
|
33
33
|
stateful: true
|
34
34
|
environment:
|
35
35
|
- WORDPRESS_DB_PASSWORD=${STACK}_secret
|
36
|
-
secrets:
|
36
|
+
secrets:
|
37
37
|
- secret: WP_ADMIN_PASSWORD
|
38
38
|
name: WORDPRESS_PASSWORD
|
39
39
|
type: env
|
@@ -49,7 +49,7 @@ services:
|
|
49
49
|
service: mysql
|
50
50
|
image: ${MYSQL_IMAGE}
|
51
51
|
stateful: true
|
52
|
-
secrets:
|
52
|
+
secrets:
|
53
53
|
- secret: WP_MYSQL_ROOT_PW
|
54
54
|
name: MYSQL_PASSWORD
|
55
55
|
type: env
|
@@ -106,7 +106,7 @@ describe Kontena::Cli::Apps::DeployCommand do
|
|
106
106
|
allow(subject).to receive(:current_dir).and_return("kontena-test")
|
107
107
|
services['wordpress']['environment'] = ['MYSQL_ADMIN_PASSWORD=password']
|
108
108
|
services['wordpress']['env_file'] = %w(/path/to/env_file .env)
|
109
|
-
allow(YAML).to receive(:
|
109
|
+
allow(YAML).to receive(:safe_load).and_return(services)
|
110
110
|
|
111
111
|
expect(File).to receive(:readlines).with('/path/to/env_file').and_return(env_vars)
|
112
112
|
expect(File).to receive(:readlines).with('.env').and_return(dot_env)
|
@@ -127,7 +127,7 @@ describe Kontena::Cli::Apps::DeployCommand do
|
|
127
127
|
allow(subject).to receive(:current_dir).and_return("kontena-test")
|
128
128
|
services['wordpress']['environment'] = ['MYSQL_ADMIN_PASSWORD=password']
|
129
129
|
services['wordpress']['env_file'] = '/path/to/env_file'
|
130
|
-
allow(YAML).to receive(:
|
130
|
+
allow(YAML).to receive(:safe_load).and_return(services)
|
131
131
|
|
132
132
|
expect(File).to receive(:readlines).with('/path/to/env_file').and_return(env_vars)
|
133
133
|
|
@@ -145,7 +145,7 @@ describe Kontena::Cli::Apps::DeployCommand do
|
|
145
145
|
it 'merges external links to links' do
|
146
146
|
allow(subject).to receive(:current_dir).and_return("kontena-test")
|
147
147
|
services['wordpress']['external_links'] = ['loadbalancer:loadbalancer']
|
148
|
-
allow(YAML).to receive(:
|
148
|
+
allow(YAML).to receive(:safe_load).and_return(services)
|
149
149
|
data = {
|
150
150
|
'name' => 'kontena-test-wordpress',
|
151
151
|
'image' => 'wordpress:latest',
|
@@ -159,6 +159,16 @@ module Kontena::Cli::Services
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
+
describe '#parse_container_name' do
|
163
|
+
it 'parses stack services id properly' do
|
164
|
+
expect(subject.parse_container_name('foo/mysql', 1)).to eq('foo-mysql-1')
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'parses stackless services id properly' do
|
168
|
+
expect(subject.parse_container_name('mysql', 1)).to eq('null-mysql-1')
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
162
172
|
describe '#parse_image' do
|
163
173
|
it 'adds :default tag if no tag exist' do
|
164
174
|
expect(subject.parse_image('nginx')).to eq('nginx:latest')
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative "../../../spec_helper"
|
2
|
+
require "kontena/cli/stacks/build_command"
|
3
|
+
|
4
|
+
describe Kontena::Cli::Stacks::BuildCommand do
|
5
|
+
|
6
|
+
let(:subject) do
|
7
|
+
described_class.new(File.basename($0))
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#execute' do
|
11
|
+
let(:stack) do
|
12
|
+
{
|
13
|
+
'name' => 'stack-a',
|
14
|
+
'stack' => 'user/stack-a',
|
15
|
+
'version' => '1.0.0',
|
16
|
+
'services' => [
|
17
|
+
service
|
18
|
+
]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:service) do
|
23
|
+
{
|
24
|
+
'name' => 'test',
|
25
|
+
'image' => 'registry.kontena.local/test:latest',
|
26
|
+
'build' => {
|
27
|
+
'context' => File.expand_path('.')
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#execute' do
|
33
|
+
before(:each) do
|
34
|
+
allow(subject).to receive(:require_config_file).with('kontena.yml').and_return(true)
|
35
|
+
allow(subject).to receive(:stack_from_yaml).with('kontena.yml').and_return(stack)
|
36
|
+
allow(subject).to receive(:system).and_return(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'requires config file' do
|
40
|
+
expect(subject).to receive(:require_config_file).with('kontena.yml').and_return(true)
|
41
|
+
subject.run([])
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'reads stack file' do
|
45
|
+
expect(subject).to receive(:stack_from_yaml).with('kontena.yml').and_return(stack)
|
46
|
+
subject.run([])
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'builds docker image' do
|
50
|
+
expect(subject).to receive(:system).with('docker', 'build', '-t', 'registry.kontena.local/test:latest', '--pull', File.expand_path('.'))
|
51
|
+
subject.run([])
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'pushes docker image' do
|
55
|
+
expect(subject).to receive(:system).with('docker', 'push', 'registry.kontena.local/test:latest')
|
56
|
+
subject.run([])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -141,6 +141,23 @@ describe Kontena::Cli::Stacks::YAML::Reader do
|
|
141
141
|
expect(service_extender).to receive(:extend_from).with(kontena_yml['services']['base'])
|
142
142
|
subject.execute
|
143
143
|
end
|
144
|
+
|
145
|
+
it 'merges validation errors' do
|
146
|
+
allow(File).to receive(:read)
|
147
|
+
.with(absolute_yaml_path('docker-compose_v2.yml'))
|
148
|
+
.and_return(fixture('docker-compose-invalid.yml'))
|
149
|
+
outcome = subject.execute
|
150
|
+
expect(outcome[:errors]).to eq([{
|
151
|
+
'docker-compose_v2.yml' =>[
|
152
|
+
{
|
153
|
+
'wordpress' => {
|
154
|
+
'networks' => 'key not expected'
|
155
|
+
}
|
156
|
+
}
|
157
|
+
]
|
158
|
+
}])
|
159
|
+
end
|
160
|
+
|
144
161
|
end
|
145
162
|
|
146
163
|
context 'variable interpolation' do
|
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: 1.0.
|
4
|
+
version: 1.0.3.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kontena, Inc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '1.5'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: safe_yaml
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '1.0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '1.0'
|
167
181
|
description: Kontena command line tool
|
168
182
|
email:
|
169
183
|
- info@kontena.io
|
@@ -440,6 +454,7 @@ files:
|
|
440
454
|
- lib/kontena/util.rb
|
441
455
|
- lib/kontena_cli.rb
|
442
456
|
- spec/fixtures/app.json
|
457
|
+
- spec/fixtures/docker-compose-invalid.yml
|
443
458
|
- spec/fixtures/docker-compose.yml
|
444
459
|
- spec/fixtures/docker-compose_v2.yml
|
445
460
|
- spec/fixtures/health.yml
|
@@ -507,6 +522,7 @@ files:
|
|
507
522
|
- spec/kontena/cli/services/services_helper_spec.rb
|
508
523
|
- spec/kontena/cli/services/unlink_command_spec.rb
|
509
524
|
- spec/kontena/cli/services/update_command_spec.rb
|
525
|
+
- spec/kontena/cli/stacks/build_command_spec.rb
|
510
526
|
- spec/kontena/cli/stacks/deploy_command_spec.rb
|
511
527
|
- spec/kontena/cli/stacks/install_command_spec.rb
|
512
528
|
- spec/kontena/cli/stacks/list_command_spec.rb
|
@@ -544,9 +560,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
544
560
|
version: 2.0.0
|
545
561
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
546
562
|
requirements:
|
547
|
-
- - "
|
563
|
+
- - ">"
|
548
564
|
- !ruby/object:Gem::Version
|
549
|
-
version:
|
565
|
+
version: 1.3.1
|
550
566
|
requirements: []
|
551
567
|
rubyforge_project:
|
552
568
|
rubygems_version: 2.5.1
|
@@ -555,6 +571,7 @@ specification_version: 4
|
|
555
571
|
summary: Kontena command line tool
|
556
572
|
test_files:
|
557
573
|
- spec/fixtures/app.json
|
574
|
+
- spec/fixtures/docker-compose-invalid.yml
|
558
575
|
- spec/fixtures/docker-compose.yml
|
559
576
|
- spec/fixtures/docker-compose_v2.yml
|
560
577
|
- spec/fixtures/health.yml
|
@@ -622,6 +639,7 @@ test_files:
|
|
622
639
|
- spec/kontena/cli/services/services_helper_spec.rb
|
623
640
|
- spec/kontena/cli/services/unlink_command_spec.rb
|
624
641
|
- spec/kontena/cli/services/update_command_spec.rb
|
642
|
+
- spec/kontena/cli/stacks/build_command_spec.rb
|
625
643
|
- spec/kontena/cli/stacks/deploy_command_spec.rb
|
626
644
|
- spec/kontena/cli/stacks/install_command_spec.rb
|
627
645
|
- spec/kontena/cli/stacks/list_command_spec.rb
|