kontena-cli 1.0.0 → 1.0.1.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/kontena/cli/stacks/common.rb +1 -2
- data/lib/kontena/cli/stacks/deploy_command.rb +12 -6
- data/lib/kontena/cli/stacks/logs_command.rb +1 -8
- data/lib/kontena/cli/stacks/registry/push_command.rb +1 -0
- data/lib/kontena/cli/stacks/yaml/reader.rb +22 -30
- data/lib/kontena/cli/stacks/yaml/service_extender.rb +9 -6
- data/lib/kontena/command.rb +1 -1
- data/spec/kontena/cli/stacks/deploy_command_spec.rb +4 -0
- data/spec/kontena/cli/stacks/yaml/reader_spec.rb +59 -57
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c16073e087d4cdae97421bd4eb5e8dee42b65462
|
4
|
+
data.tar.gz: c6f92090afa63cd4f6a416f27823746138d63244
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 743dd0816028f2ec8a2a4a2c9d96d3196af3354c00d804c8dd5e996878379c9e9d6329f38a74023c4e40aea9525b7acce8184917a04bf59c174eb5abd670e4b5
|
7
|
+
data.tar.gz: c801bd06d17d3f201222acc9a05494b692124fbd428a2cacc994faac08f0f3c2ceaebc5cd8ca125e7acf55ab9acacf3d5d9f04e3d7682fc2e9f16bab1401d35a
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.1.rc1
|
@@ -29,8 +29,7 @@ module Kontena::Cli::Stacks
|
|
29
29
|
if reader.stack_name.nil?
|
30
30
|
exit_with_error "Stack MUST have stack name in YAML top level field 'stack'! Aborting."
|
31
31
|
end
|
32
|
-
set_env_variables(reader.stack_name, current_grid)
|
33
|
-
#reader.reload
|
32
|
+
set_env_variables(reader.stack_name, current_grid)
|
34
33
|
outcome = reader.execute
|
35
34
|
|
36
35
|
hint_on_validation_notifications(outcome[:notifications]) if outcome[:notifications].size > 0
|
@@ -17,9 +17,7 @@ module Kontena::Cli::Stacks
|
|
17
17
|
deployment = nil
|
18
18
|
spinner "Deploying stack #{pastel.cyan(name)}" do
|
19
19
|
deployment = deploy_stack(name)
|
20
|
-
deployment
|
21
|
-
wait_for_deploy_to_finish(service_deploy)
|
22
|
-
end
|
20
|
+
wait_for_deploy_to_finish(deployment)
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
@@ -31,14 +29,22 @@ module Kontena::Cli::Stacks
|
|
31
29
|
# @return [Boolean]
|
32
30
|
def wait_for_deploy_to_finish(deployment, timeout = 600)
|
33
31
|
deployed = false
|
32
|
+
progress = []
|
33
|
+
states = %w(success error)
|
34
34
|
Timeout::timeout(timeout) do
|
35
35
|
until deployed
|
36
|
-
deployment = client.get("
|
37
|
-
deployed = true if deployment['
|
36
|
+
deployment = client.get("stacks/#{deployment['stack_id']}/deploys/#{deployment['id']}")
|
37
|
+
deployed = true if states.include?(deployment['state'])
|
38
38
|
sleep 1
|
39
39
|
end
|
40
40
|
if deployment['state'] == 'error'
|
41
|
-
|
41
|
+
deployment['service_deploys'].each do |service_deploy|
|
42
|
+
if service_deploy['state'] == 'error'
|
43
|
+
puts " #{service_deploy['reason']}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
raise 'deploy failed'
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
@@ -7,19 +7,12 @@ module Kontena::Cli::Stacks
|
|
7
7
|
banner "Shows logs from services in a stack"
|
8
8
|
|
9
9
|
parameter "NAME", "Stack name"
|
10
|
-
option ["-t", "--tail"], :flag, "Tail (follow) logs", default: false
|
11
|
-
option ["-l", "--lines"], "LINES", "How many lines to show", default: '100'
|
12
|
-
option "--since", "SINCE", "Show logs since given timestamp"
|
13
10
|
|
14
11
|
requires_current_master
|
15
12
|
requires_current_master_token
|
16
13
|
|
17
14
|
def execute
|
18
|
-
|
19
|
-
query_params[:limit] = lines if lines
|
20
|
-
query_params[:since] = since if since
|
21
|
-
|
22
|
-
show_logs("stacks/#{current_grid}/#{name}/container_logs", query_params) do |log|
|
15
|
+
show_logs("stacks/#{current_grid}/#{name}/container_logs") do |log|
|
23
16
|
show_log(log)
|
24
17
|
end
|
25
18
|
end
|
@@ -13,6 +13,7 @@ module Kontena::Cli::Stacks::Registry
|
|
13
13
|
|
14
14
|
def execute
|
15
15
|
file = Kontena::Cli::Stacks::YAML::Reader.new(filename, skip_variables: true, replace_missing: "filler")
|
16
|
+
file.execute
|
16
17
|
name = "#{file.yaml['stack']}:#{file.yaml['version']}"
|
17
18
|
spinner("Pushing #{pastel.cyan(name)} to stacks registry") do
|
18
19
|
stacks_client.push(file.yaml['stack'], file.yaml['version'], file.raw_content)
|
@@ -33,8 +33,6 @@ module Kontena::Cli::Stacks
|
|
33
33
|
@skip_validation = skip_validation
|
34
34
|
@skip_variables = skip_variables
|
35
35
|
@replace_missing = replace_missing
|
36
|
-
parse_variables unless skip_variables
|
37
|
-
parse_yaml
|
38
36
|
end
|
39
37
|
|
40
38
|
def from_registry?
|
@@ -44,9 +42,10 @@ module Kontena::Cli::Stacks
|
|
44
42
|
# @return [Opto::Group]
|
45
43
|
def variables
|
46
44
|
return @variables if @variables
|
47
|
-
yaml = ::YAML.load(interpolate(raw_content, 'filler'))
|
48
45
|
if yaml && yaml.has_key?('variables')
|
49
|
-
|
46
|
+
variables_yaml = yaml['variables'].to_yaml
|
47
|
+
variables_hash = ::YAML.load(replace_dollar_dollars(interpolate(variables_yaml)))
|
48
|
+
@variables = Opto::Group.new(variables_hash, defaults: { from: :env, to: :env })
|
50
49
|
else
|
51
50
|
@variables = Opto::Group.new(defaults: { from: :env, to: :env })
|
52
51
|
end
|
@@ -62,6 +61,11 @@ module Kontena::Cli::Stacks
|
|
62
61
|
# @param [String] service_name
|
63
62
|
# @return [Hash]
|
64
63
|
def execute(service_name = nil)
|
64
|
+
load_yaml(false)
|
65
|
+
parse_variables unless skip_variables?
|
66
|
+
load_yaml
|
67
|
+
validate unless skip_validation?
|
68
|
+
|
65
69
|
result = {}
|
66
70
|
Dir.chdir(from_registry? ? Dir.pwd : File.dirname(File.expand_path(file))) do
|
67
71
|
result[:stack] = yaml['stack']
|
@@ -77,15 +81,8 @@ module Kontena::Cli::Stacks
|
|
77
81
|
result
|
78
82
|
end
|
79
83
|
|
80
|
-
def reload
|
81
|
-
@errors = []
|
82
|
-
@notifications = []
|
83
|
-
@variables = nil
|
84
|
-
parse_variables unless skip_variables?
|
85
|
-
parse_yaml
|
86
|
-
end
|
87
|
-
|
88
84
|
def stack_name
|
85
|
+
yaml = ::YAML.load(raw_content)
|
89
86
|
yaml['stack'].split('/').last.split(':').first if yaml['stack']
|
90
87
|
end
|
91
88
|
|
@@ -102,13 +99,12 @@ module Kontena::Cli::Stacks
|
|
102
99
|
@content_variables ||= raw_content.scan(/((?<!\$)\$(?!\$)\{?(\w+)\}?)/m)
|
103
100
|
end
|
104
101
|
|
105
|
-
def
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
@yaml = ::YAML.load(replace_dollar_dollars(interpolate(raw_content)))
|
102
|
+
def load_yaml(interpolate = true)
|
103
|
+
if interpolate
|
104
|
+
@yaml = ::YAML.load(replace_dollar_dollars(interpolate(raw_content)))
|
105
|
+
else
|
106
|
+
@yaml = ::YAML.load(raw_content)
|
107
|
+
end
|
112
108
|
rescue Psych::SyntaxError => e
|
113
109
|
raise "Error while parsing #{file}".colorize(:red)+ " "+e.message
|
114
110
|
end
|
@@ -224,21 +220,17 @@ module Kontena::Cli::Stacks
|
|
224
220
|
|
225
221
|
##
|
226
222
|
# @param [String] text - content of YAML file
|
227
|
-
def interpolate(text
|
223
|
+
def interpolate(text)
|
228
224
|
text.gsub(/(?<!\$)\$(?!\$)\{?\w+\}?/) do |v| # searches $VAR and ${VAR} and not $$VAR
|
229
|
-
|
230
|
-
|
225
|
+
var = v.tr('${}', '')
|
226
|
+
val = ENV[var]
|
227
|
+
if val
|
228
|
+
val
|
231
229
|
elsif @replace_missing
|
232
230
|
@replace_missing
|
233
231
|
else
|
234
|
-
|
235
|
-
|
236
|
-
if val
|
237
|
-
val
|
238
|
-
else
|
239
|
-
puts "Value for #{var} is not set. Substituting with an empty string." unless skip_validation?
|
240
|
-
''
|
241
|
-
end
|
232
|
+
puts "Value for #{var} is not set. Substituting with an empty string." unless skip_validation?
|
233
|
+
''
|
242
234
|
end
|
243
235
|
end
|
244
236
|
end
|
@@ -34,12 +34,15 @@ module Kontena::Cli::Stacks
|
|
34
34
|
# @param [Array] to
|
35
35
|
# @return [Array]
|
36
36
|
def extend_env_vars(from, to)
|
37
|
-
to
|
38
|
-
from
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
env_vars = to || []
|
38
|
+
if from
|
39
|
+
from.each do |env|
|
40
|
+
env_vars << env unless to && to.find do |key|
|
41
|
+
key.split('=').first == env.split('=').first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
env_vars
|
43
46
|
end
|
44
47
|
|
45
48
|
# Takes two arrays of hashes containing { 'secret' => 'str', 'type' => 'str', 'name' => 'str' }
|
data/lib/kontena/command.rb
CHANGED
@@ -151,7 +151,7 @@ class Kontena::Command < Clamp::Command
|
|
151
151
|
Kontena::Cli::Config.instance.require_current_master_token
|
152
152
|
rescue Kontena::Cli::Config::TokenExpiredError
|
153
153
|
success = Kontena::Client.new(
|
154
|
-
Kontena::Cli::Config.instance.current_master,
|
154
|
+
Kontena::Cli::Config.instance.current_master.url,
|
155
155
|
Kontena::Cli::Config.instance.current_master.token
|
156
156
|
).refresh_token
|
157
157
|
if success && !retried
|
@@ -6,6 +6,10 @@ describe Kontena::Cli::Stacks::DeployCommand do
|
|
6
6
|
include ClientHelpers
|
7
7
|
|
8
8
|
describe '#execute' do
|
9
|
+
before(:each) do
|
10
|
+
allow(subject).to receive(:wait_for_deploy_to_finish).and_return(spy)
|
11
|
+
end
|
12
|
+
|
9
13
|
it 'requires api url' do
|
10
14
|
expect(described_class.requires_current_master?).to be_truthy
|
11
15
|
subject.run(['test-stack'])
|
@@ -63,62 +63,6 @@ describe Kontena::Cli::Stacks::YAML::Reader do
|
|
63
63
|
.and_return(fixture('kontena_v3.yml'))
|
64
64
|
subject
|
65
65
|
end
|
66
|
-
|
67
|
-
context 'variable interpolation' do
|
68
|
-
before(:each) do
|
69
|
-
allow(ENV).to receive(:key?).and_return(true)
|
70
|
-
allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
|
71
|
-
allow(ENV).to receive(:[]).with('STACK').and_return('test')
|
72
|
-
allow(ENV).to receive(:[]).with('GRID').and_return('test-grid')
|
73
|
-
allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'interpolates $VAR variables' do
|
77
|
-
allow(File).to receive(:read)
|
78
|
-
.with(absolute_yaml_path)
|
79
|
-
.and_return(fixture('stack-with-variables.yml'))
|
80
|
-
services = subject.yaml['services']
|
81
|
-
expect(services['wordpress']['image']).to eq('wordpress:4.1')
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'interpolates ${VAR} variables' do
|
85
|
-
allow(File).to receive(:read)
|
86
|
-
.with(absolute_yaml_path)
|
87
|
-
.and_return(fixture('stack-with-variables.yml'))
|
88
|
-
services = subject.yaml['services']
|
89
|
-
expect(services['mysql']['image']).to eq('mariadb:latest')
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'warns about empty variables' do
|
93
|
-
allow(File).to receive(:read)
|
94
|
-
.with(absolute_yaml_path)
|
95
|
-
.and_return(fixture('stack-with-variables.yml'))
|
96
|
-
allow(ENV).to receive(:[])
|
97
|
-
.with('MYSQL_IMAGE')
|
98
|
-
.and_return(nil)
|
99
|
-
|
100
|
-
expect {
|
101
|
-
subject
|
102
|
-
}.to output("Value for MYSQL_IMAGE is not set. Substituting with an empty string.\n").to_stdout
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'replaces $$VAR variables to $VAR format' do
|
107
|
-
allow(ENV).to receive(:key?).and_return(true)
|
108
|
-
allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
|
109
|
-
allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
|
110
|
-
allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
|
111
|
-
allow(ENV).to receive(:[]).with('STACK').and_return('test')
|
112
|
-
allow(ENV).to receive(:[]).with('GRID').and_return('test-grid')
|
113
|
-
allow(File).to receive(:read)
|
114
|
-
.with(absolute_yaml_path)
|
115
|
-
.and_return(fixture('stack-with-variables.yml'))
|
116
|
-
allow(File).to receive(:read)
|
117
|
-
.with(absolute_yaml_path('docker-compose_v2.yml'))
|
118
|
-
.and_return(fixture('docker-compose_v2.yml'))
|
119
|
-
services = subject.execute[:services]
|
120
|
-
expect(services['mysql']['environment'].first).to eq('INTERNAL_VAR=$INTERNAL_VAR')
|
121
|
-
end
|
122
66
|
end
|
123
67
|
|
124
68
|
context 'when yaml file is malformed' do
|
@@ -199,6 +143,65 @@ describe Kontena::Cli::Stacks::YAML::Reader do
|
|
199
143
|
end
|
200
144
|
end
|
201
145
|
|
146
|
+
context 'variable interpolation' do
|
147
|
+
before(:each) do
|
148
|
+
allow(ENV).to receive(:key?).and_return(true)
|
149
|
+
allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
|
150
|
+
allow(ENV).to receive(:[]).with('STACK').and_return('test')
|
151
|
+
allow(ENV).to receive(:[]).with('GRID').and_return('test-grid')
|
152
|
+
allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
|
153
|
+
allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'interpolates $VAR variables' do
|
157
|
+
allow(File).to receive(:read)
|
158
|
+
.with(absolute_yaml_path)
|
159
|
+
.and_return(fixture('stack-with-variables.yml'))
|
160
|
+
subject.execute
|
161
|
+
services = subject.yaml['services']
|
162
|
+
expect(services['wordpress']['image']).to eq('wordpress:4.1')
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'interpolates ${VAR} variables' do
|
166
|
+
allow(File).to receive(:read)
|
167
|
+
.with(absolute_yaml_path)
|
168
|
+
.and_return(fixture('stack-with-variables.yml'))
|
169
|
+
subject.execute
|
170
|
+
services = subject.yaml['services']
|
171
|
+
expect(services['mysql']['image']).to eq('mariadb:latest')
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'warns about empty variables' do
|
175
|
+
allow(File).to receive(:read)
|
176
|
+
.with(absolute_yaml_path)
|
177
|
+
.and_return(fixture('stack-with-variables.yml'))
|
178
|
+
allow(ENV).to receive(:[])
|
179
|
+
.with('MYSQL_IMAGE')
|
180
|
+
.and_return(nil)
|
181
|
+
|
182
|
+
expect {
|
183
|
+
subject.execute
|
184
|
+
}.to output("Value for MYSQL_IMAGE is not set. Substituting with an empty string.\n").to_stdout
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'replaces $$VAR variables to $VAR format' do
|
189
|
+
allow(ENV).to receive(:key?).and_return(true)
|
190
|
+
allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
|
191
|
+
allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
|
192
|
+
allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
|
193
|
+
allow(ENV).to receive(:[]).with('STACK').and_return('test')
|
194
|
+
allow(ENV).to receive(:[]).with('GRID').and_return('test-grid')
|
195
|
+
allow(File).to receive(:read)
|
196
|
+
.with(absolute_yaml_path)
|
197
|
+
.and_return(fixture('stack-with-variables.yml'))
|
198
|
+
allow(File).to receive(:read)
|
199
|
+
.with(absolute_yaml_path('docker-compose_v2.yml'))
|
200
|
+
.and_return(fixture('docker-compose_v2.yml'))
|
201
|
+
services = subject.execute[:services]
|
202
|
+
expect(services['mysql']['environment'].first).to eq('INTERNAL_VAR=$INTERNAL_VAR')
|
203
|
+
end
|
204
|
+
|
202
205
|
context 'environment variables' do
|
203
206
|
it 'converts env hash to array' do
|
204
207
|
result = subject.execute[:services]
|
@@ -282,7 +285,6 @@ describe Kontena::Cli::Stacks::YAML::Reader do
|
|
282
285
|
.with(absolute_yaml_path)
|
283
286
|
.and_return(fixture('kontena_build_v3.yml'))
|
284
287
|
outcome = subject.execute
|
285
|
-
puts outcome
|
286
288
|
expect(outcome[:services]['webapp']['build']['context']).to eq(File.expand_path('.'))
|
287
289
|
end
|
288
290
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kontena-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1.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-
|
11
|
+
date: 2016-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -543,9 +543,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
543
543
|
version: 2.0.0
|
544
544
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
545
545
|
requirements:
|
546
|
-
- - "
|
546
|
+
- - ">"
|
547
547
|
- !ruby/object:Gem::Version
|
548
|
-
version:
|
548
|
+
version: 1.3.1
|
549
549
|
requirements: []
|
550
550
|
rubyforge_project:
|
551
551
|
rubygems_version: 2.5.1
|