kontena-cli 0.15.0.rc1 → 0.15.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2097ac9b19a1b3ffe77759d51f750de06bab2af
4
- data.tar.gz: 0484b872ce20c995506cba6cc651716b7a4ffec3
3
+ metadata.gz: de5064548c891f2a1dc2b8382efcd81ef47a4e3b
4
+ data.tar.gz: 0d1262bfcc83048a591ab04b1040c94cbbb0ca02
5
5
  SHA512:
6
- metadata.gz: 6fce1e0c1cac772357d580e0c05284130ad1632a234c47e48f722028d37bc5ec6c025628e343fe5cc6567e9703775c64bd04a34e46ee71d072c52525d1ace02e
7
- data.tar.gz: 929389765338d0c8db3dbc56973052f4cb5b7c384ea37f6ba2125a393436b83260161c1ff9aa0111aa1f6446e1afe8d9fe3cd5a7f6ccb2928e7369ef03938421
6
+ metadata.gz: 3c540225c876e38452bcf9308fa2ae356464b17e7f0433c65e078afaaf20b9cfeedee96fb76e77a921f2f0d2269a3fbbac127cad33abb81a8b5d4af49829ca06
7
+ data.tar.gz: 2c723b83ee5fe5a81e853f45e22cb44be1decb8dee9e4f8b83defa1ef2c0a3c03af8afc750ac38d3f90d1a6c7c6abd92dda7d921e83a8ac311891a487b23e8af
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.15.0.rc1
1
+ 0.15.0.rc2
@@ -26,7 +26,7 @@ module Kontena::Cli::Apps
26
26
  data = {}
27
27
  data['container_count'] = options['instances']
28
28
  data['image'] = parse_image(options['image'])
29
- data['env'] = merge_env_vars(options)
29
+ data['env'] = options['environment'] if options['environment']
30
30
  data['container_count'] = options['instances']
31
31
  data['links'] = parse_links(options['links'] || [])
32
32
  data['external_links'] = parse_links(options['external_links'] || [])
@@ -73,24 +73,6 @@ module Kontena::Cli::Apps
73
73
  data
74
74
  end
75
75
 
76
- # @param [Hash] options
77
- def merge_env_vars(options)
78
- return options['environment'] unless options['env_file']
79
-
80
- options['env_file'] = [options['env_file']] if options['env_file'].is_a?(String)
81
- options['environment'] = [] unless options['environment']
82
- options['env_file'].each do |env_file|
83
- options['environment'].concat(read_env_file(env_file))
84
- end
85
- options['environment'].uniq {|s| s.split('=').first}
86
- end
87
-
88
-
89
- # @param [String] path
90
- def read_env_file(path)
91
- File.readlines(path).delete_if { |line| line.start_with?('#') || line.strip.empty? }
92
- end
93
-
94
76
  # @param [Array<String>] port_options
95
77
  # @return [Array<Hash>]
96
78
  def parse_stringified_ports(port_options)
@@ -89,6 +89,8 @@ module Kontena::Cli::Apps
89
89
  # @param [Hash] service_config
90
90
  def process_config(service_config)
91
91
  normalize_env_vars(service_config)
92
+ merge_env_vars(service_config)
93
+ expand_build_context(service_config)
92
94
  normalize_build_args(service_config)
93
95
  service_config = extend_config(service_config) if service_config.key?('extends')
94
96
  service_config
@@ -146,7 +148,33 @@ module Kontena::Cli::Apps
146
148
  options['environment'] = options['environment'].map { |k, v| "#{k}=#{v}" }
147
149
  end
148
150
  end
149
-
151
+
152
+ # @param [Hash] options
153
+ def merge_env_vars(options)
154
+ return options['environment'] unless options['env_file']
155
+
156
+ options['env_file'] = [options['env_file']] if options['env_file'].is_a?(String)
157
+ options['environment'] = [] unless options['environment']
158
+ options['env_file'].each do |env_file|
159
+ options['environment'].concat(read_env_file(env_file))
160
+ end
161
+ options.delete('env_file')
162
+ options['environment'].uniq! { |s| s.split('=').first }
163
+ end
164
+
165
+ # @param [String] path
166
+ def read_env_file(path)
167
+ File.readlines(path).map { |line| line.strip }.delete_if { |line| line.start_with?('#') || line.empty? }
168
+ end
169
+
170
+ def expand_build_context(options)
171
+ if options['build'].is_a?(String)
172
+ options['build'] = File.expand_path(options['build'])
173
+ elsif context = options.dig('build', 'context')
174
+ options['build']['context'] = File.expand_path(context)
175
+ end
176
+ end
177
+
150
178
  # @param [Hash] options - service config
151
179
  def normalize_build_args(options)
152
180
  if v2? && options.dig('build', 'args')
@@ -4,8 +4,9 @@ module Kontena::Cli::Grids
4
4
  ##
5
5
  # @param [Hash] grid
6
6
  def print_grid(grid)
7
+ host = ENV['KONTENA_URL'] || self.current_master['url']
7
8
  puts "#{grid['name']}:"
8
- puts " uri: #{self.current_master['url'].sub('http', 'ws')}"
9
+ puts " uri: #{host.sub('http', 'ws')}"
9
10
  puts " token: #{grid['token']}"
10
11
  root_dir = grid['engine_root_dir']
11
12
  nodes = client(require_token).get("grids/#{grid['name']}/nodes")
@@ -12,8 +12,8 @@ module Kontena::Cli::Services
12
12
 
13
13
  grids = client(token).get("grids/#{current_grid}/services")
14
14
  services = grids['services'].sort_by{|s| s['updated_at'] }.reverse
15
- titles = ['NAME', 'INSTANCES', 'STATEFUL', 'STATE', 'HEALTH', 'EXPOSED PORTS']
16
- puts "%-60s %-10s %-8s %-10s %-10s %-50s" % titles
15
+ titles = ['NAME', 'INSTANCES', 'STATEFUL', 'STATE', 'EXPOSED PORTS']
16
+ puts "%-60s %-10s %-8s %-10s %-50s" % titles
17
17
  services.each do |service|
18
18
  stateful = service['stateful'] ? 'yes' : 'no'
19
19
  running = service['instances']['running']
@@ -22,28 +22,15 @@ module Kontena::Cli::Services
22
22
  ports = service['ports'].map{|p|
23
23
  "#{p['ip']}:#{p['node_port']}->#{p['container_port']}/#{p['protocol']}"
24
24
  }.join(", ")
25
- health = 'unknown'
26
- if service['health_status']
27
- icon = "■"
28
- healthy = service.dig('health_status', 'healthy')
29
- total = service.dig('health_status', 'total')
30
- color = :green
31
- if healthy == 0
32
- color = :red
33
- elsif healthy > 0 && healthy < total
34
- color = :yellow
35
- end
36
- health = "■".colorize(color)
37
- end
25
+ health = health_status(service)
38
26
  vars = [
39
- service['name'],
27
+ "#{health_status_icon(health)} #{service['name']}",
40
28
  instances,
41
29
  stateful,
42
30
  service['state'],
43
- health,
44
31
  ports
45
32
  ]
46
- puts "%-60.60s %-10.10s %-8s %-10s %-10s %-50s" % vars
33
+ puts "%-74s %-10.10s %-8s %-10s %-50s" % vars
47
34
  end
48
35
  end
49
36
  end
@@ -180,7 +180,7 @@ module Kontena
180
180
  puts " interval: #{service['health_check']['interval']}"
181
181
  puts " initial_delay: #{service['health_check']['initial_delay']}"
182
182
  end
183
-
183
+
184
184
  if service['health_status']
185
185
  puts " health status:"
186
186
  puts " healthy: #{service['health_status']['healthy']}"
@@ -282,7 +282,7 @@ module Kontena
282
282
  ip: ip,
283
283
  container_port: container_port,
284
284
  node_port: node_port,
285
- protocol: protocol
285
+ protocol: protocol
286
286
  }
287
287
  }
288
288
  end
@@ -399,6 +399,42 @@ module Kontena
399
399
 
400
400
  build_args
401
401
  end
402
+
403
+ # @param [Symbol] health
404
+ # @return [String]
405
+ def health_status_icon(health)
406
+ if health == :unhealthy
407
+ icon = '⊗'.freeze
408
+ icon.colorize(:red)
409
+ elsif health == :partial
410
+ icon = '⊙'.freeze
411
+ icon.colorize(:yellow)
412
+ elsif health == :healthy
413
+ icon = '⊛'.freeze
414
+ icon.colorize(:green)
415
+ else
416
+ icon = '⊝'.freeze
417
+ icon.colorize(:default)
418
+ end
419
+ end
420
+
421
+ # @param [Hash] service
422
+ # @return [Symbol]
423
+ def health_status(service)
424
+ if service['health_status']
425
+ healthy = service.dig('health_status', 'healthy')
426
+ total = service.dig('health_status', 'total')
427
+ if healthy == 0
428
+ :unhealthy
429
+ elsif healthy > 0 && healthy < total
430
+ :partial
431
+ else
432
+ :healthy
433
+ end
434
+ else
435
+ :unknown
436
+ end
437
+ end
402
438
  end
403
439
  end
404
440
  end
@@ -10,10 +10,10 @@ module Kontena::Cli::Vault
10
10
  token = require_token
11
11
  result = client(token).get("grids/#{current_grid}/secrets")
12
12
 
13
- column_width_paddings = '%-54s %-25.25s'
14
- puts column_width_paddings % ['NAME', 'CREATED AT']
13
+ column_width_paddings = '%-54s %-25.25s %-25.25s'
14
+ puts column_width_paddings % ['NAME', 'CREATED AT', 'UPDATED AT']
15
15
  result['secrets'].sort_by { |s| s['name'] }.each do |secret|
16
- puts column_width_paddings % [secret['name'], secret['created_at']]
16
+ puts column_width_paddings % [secret['name'], secret['created_at'], secret['updated_at']]
17
17
  end
18
18
  end
19
19
  end
@@ -10,27 +10,26 @@ class Kontena::Cli::WhoamiCommand < Clamp::Command
10
10
  end
11
11
 
12
12
  require_api_url
13
- puts "Master: #{self.current_master['name']}"
14
- puts "URL: #{api_url}"
15
- puts "Grid: #{current_grid}"
16
- if current_master['email']
17
- puts "User: #{current_master['email']}"
18
- else # In case local storage doesn't have the user email yet
19
- token = require_token
20
- user = client(token).get('user')
21
- puts "User: #{user['email']}"
22
- master = {
23
- 'name' => current_master['name'],
24
- 'url' => current_master['url'],
25
- 'token' => current_master['token'],
26
- 'email' => user['email'],
27
- 'grid' => current_master['grid']
28
- }
13
+ puts "Master: #{ENV['KONTENA_URL'] || self.current_master['name']}"
14
+ puts "URL: #{ENV['KONTENA_URL'] || api_url}"
15
+ puts "Grid: #{ENV['KONTENA_GRID'] || current_grid}"
16
+ unless ENV['KONTENA_URL']
17
+ if current_master['email']
18
+ puts "User: #{current_master['email']}"
19
+ else # In case local storage doesn't have the user email yet
20
+ token = require_token
21
+ user = client(token).get('user')
22
+ puts "User: #{user['email']}"
23
+ master = {
24
+ 'name' => current_master['name'],
25
+ 'url' => current_master['url'],
26
+ 'token' => current_master['token'],
27
+ 'email' => user['email'],
28
+ 'grid' => current_master['grid']
29
+ }
29
30
 
30
- self.add_master(current_master['name'], master)
31
+ self.add_master(current_master['name'], master)
32
+ end
31
33
  end
32
-
33
-
34
34
  end
35
-
36
35
  end
@@ -27,10 +27,11 @@ require_relative 'cli/stack_command'
27
27
  require_relative 'cli/certificate_command'
28
28
 
29
29
  class Kontena::MainCommand < Kontena::Command
30
+ include Kontena::Util
30
31
 
31
32
  subcommand "grid", "Grid specific commands", Kontena::Cli::GridCommand
32
33
  subcommand "app", "App specific commands", Kontena::Cli::AppCommand
33
- subcommand "stack", "Stack specific commands", Kontena::Cli::StackCommand
34
+ subcommand "stack", "Stack specific commands", Kontena::Cli::StackCommand if experimental?
34
35
  subcommand "service", "Service specific commands", Kontena::Cli::ServiceCommand
35
36
  subcommand "vault", "Vault specific commands", Kontena::Cli::VaultCommand
36
37
  subcommand "certificate", "LE Certificate specific commands", Kontena::Cli::CertificateCommand
data/lib/kontena/util.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  module Kontena
2
2
  module Util
3
3
 
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
4
8
  # @param [String] cmd
5
9
  def which(cmd)
6
10
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
@@ -13,5 +17,12 @@ module Kontena
13
17
  return nil
14
18
  end
15
19
  module_function(:which)
20
+
21
+ module ClassMethods
22
+ def experimental?
23
+ ENV.has_key?('KONTENA_EXPERIMENTAL')
24
+ end
25
+ end
26
+
16
27
  end
17
28
  end
@@ -0,0 +1,18 @@
1
+ wordpress:
2
+ extends:
3
+ file: docker-compose.yml
4
+ service: wordpress
5
+ stateful: true
6
+ env_file: .env
7
+ environment:
8
+ WORDPRESS_DB_PASSWORD: %{project}_secret
9
+ instances: 2
10
+ deploy:
11
+ strategy: ha
12
+ mysql:
13
+ extends:
14
+ file: docker-compose.yml
15
+ service: mysql
16
+ stateful: true
17
+ environment:
18
+ - MYSQL_ROOT_PASSWORD=%{project}_secret
@@ -6,12 +6,6 @@ describe Kontena::Cli::Apps::ServiceGenerator do
6
6
  described_class.new({})
7
7
  end
8
8
 
9
- let(:env_file) do
10
- 'APIKEY=12345
11
- MYSQL_ROOT_PASSWORD=secret
12
- WP_ADMIN_PASSWORD=verysecret'.split(/\r?\n/)
13
- end
14
-
15
9
  describe '#parse_data' do
16
10
  context 'volumes' do
17
11
  it 'returns volumes if set' do
@@ -387,50 +381,5 @@ WP_ADMIN_PASSWORD=verysecret'.split(/\r?\n/)
387
381
  expect(result['secrets']).to be_nil
388
382
  end
389
383
  end
390
- end
391
-
392
- describe '#read_env_file' do
393
- before(:each) do
394
- allow(File).to receive(:readlines).with('.env').and_return(env_file)
395
- end
396
- it 'reads given file' do
397
- expect(File).to receive(:readlines).with('.env').and_return(env_file)
398
- subject.send(:read_env_file, '.env')
399
- end
400
-
401
- it 'returns array' do
402
- variables = subject.send(:read_env_file, '.env')
403
- expect(variables).to eq([
404
- 'APIKEY=12345',
405
- 'MYSQL_ROOT_PASSWORD=secret',
406
- 'WP_ADMIN_PASSWORD=verysecret'
407
- ])
408
- end
409
-
410
- it 'discards comment lines' do
411
- result = env_file
412
- result << "#COMMENTLINE"
413
- allow(File).to receive(:readlines).with('.env').and_return(result)
414
-
415
- variables = subject.send(:read_env_file, '.env')
416
- expect(variables).to eq([
417
- 'APIKEY=12345',
418
- 'MYSQL_ROOT_PASSWORD=secret',
419
- 'WP_ADMIN_PASSWORD=verysecret'
420
- ])
421
- end
422
-
423
- it 'discards empty lines' do
424
- result = env_file
425
- result << '
426
- '
427
- allow(File).to receive(:readlines).with('.env').and_return(result)
428
- variables = subject.send(:read_env_file, '.env')
429
- expect(variables).to eq([
430
- 'APIKEY=12345',
431
- 'MYSQL_ROOT_PASSWORD=secret',
432
- 'WP_ADMIN_PASSWORD=verysecret'
433
- ])
434
- end
435
- end
384
+ end
436
385
  end
@@ -16,6 +16,12 @@ describe Kontena::Cli::Apps::YAML::Reader do
16
16
  spy
17
17
  end
18
18
 
19
+ let(:env_file) do
20
+ ['APIKEY=12345
21
+ ', 'MYSQL_ROOT_PASSWORD=secret
22
+ ', 'WP_ADMIN_PASSWORD=verysecret']
23
+ end
24
+
19
25
  let(:valid_result) do
20
26
  {
21
27
  'wordpress' => {
@@ -232,6 +238,57 @@ describe Kontena::Cli::Apps::YAML::Reader do
232
238
  result = subject.execute[:services]
233
239
  expect(result['mysql']['environment']).to eq(['MYSQL_ROOT_PASSWORD=test_secret'])
234
240
  end
241
+
242
+ context 'when introduced env_file' do
243
+ before(:each) do
244
+ allow(File).to receive(:read)
245
+ .with(absolute_yaml_path('kontena.yml'))
246
+ .and_return(fixture('kontena-with-env-file.yml'))
247
+ allow(File).to receive(:readlines).with('.env').and_return(env_file)
248
+ end
249
+
250
+ it 'reads given file' do
251
+ expect(File).to receive(:readlines).with('.env').and_return(env_file)
252
+ subject.send(:read_env_file, '.env')
253
+ end
254
+
255
+ it 'discards comment lines' do
256
+ result = env_file
257
+ result << "#COMMENTLINE"
258
+ allow(File).to receive(:readlines).with('.env').and_return(result)
259
+
260
+ variables = subject.send(:read_env_file, '.env')
261
+ expect(variables).to eq([
262
+ 'APIKEY=12345',
263
+ 'MYSQL_ROOT_PASSWORD=secret',
264
+ 'WP_ADMIN_PASSWORD=verysecret'
265
+ ])
266
+ end
267
+
268
+ it 'discards empty lines' do
269
+ result = env_file
270
+ result << '
271
+ '
272
+ allow(File).to receive(:readlines).with('.env').and_return(result)
273
+ variables = subject.send(:read_env_file, '.env')
274
+ expect(variables).to eq([
275
+ 'APIKEY=12345',
276
+ 'MYSQL_ROOT_PASSWORD=secret',
277
+ 'WP_ADMIN_PASSWORD=verysecret'
278
+ ])
279
+ end
280
+
281
+ it 'merges variables' do
282
+ result = subject.execute[:services]
283
+ expect(result['wordpress']['environment']).to eq([
284
+ 'WORDPRESS_DB_PASSWORD=test_secret',
285
+ 'APIKEY=12345',
286
+ 'MYSQL_ROOT_PASSWORD=secret',
287
+ 'WP_ADMIN_PASSWORD=verysecret'
288
+ ])
289
+ end
290
+
291
+ end
235
292
  end
236
293
  it 'returns result hash' do
237
294
  outcome = subject.execute
@@ -246,4 +303,31 @@ describe Kontena::Cli::Apps::YAML::Reader do
246
303
  expect(outcome[:errors].size).to eq(1)
247
304
  end
248
305
  end
306
+
307
+ context 'when build option is string' do
308
+ it 'expands build option to absolute path' do
309
+ allow(File).to receive(:read)
310
+ .with(absolute_yaml_path('docker-compose.yml'))
311
+ .and_return(fixture('docker-compose.yml'))
312
+ allow(File).to receive(:read)
313
+ .with(absolute_yaml_path('kontena.yml'))
314
+ .and_return(fixture('kontena-build.yml'))
315
+ outcome = subject.execute
316
+
317
+ expect(outcome[:services]['wordpress']['build']).to eq(File.expand_path('.'))
318
+ end
319
+ end
320
+
321
+ context 'when build option is Hash' do
322
+ it 'expands build context to absolute path' do
323
+ allow(File).to receive(:read)
324
+ .with(absolute_yaml_path('docker-compose.yml'))
325
+ .and_return(fixture('docker-compose.yml'))
326
+ allow(File).to receive(:read)
327
+ .with(absolute_yaml_path('kontena.yml'))
328
+ .and_return(fixture('kontena_build_v2.yml'))
329
+ outcome = subject.execute
330
+ expect(outcome[:services]['webapp']['build']['context']).to eq(File.expand_path('.'))
331
+ end
332
+ end
249
333
  end
@@ -223,5 +223,41 @@ module Kontena::Cli::Services
223
223
  expect(subject.parse_build_args({'foo' => 'bar', 'baz' => nil})).to eq({'foo' => 'bar', 'baz' => 'baf'})
224
224
  end
225
225
  end
226
+
227
+ describe '#health_status' do
228
+ it 'returns :unknown by default' do
229
+ expect(subject.health_status({})).to eq(:unknown)
230
+ end
231
+
232
+ it 'returns :healthy if all instances are healthy' do
233
+ data = {
234
+ 'health_status' => {
235
+ 'healthy' => 3,
236
+ 'total' => 3
237
+ }
238
+ }
239
+ expect(subject.health_status(data)).to eq(:healthy)
240
+ end
241
+
242
+ it 'returns :partial if not all instances are healthy' do
243
+ data = {
244
+ 'health_status' => {
245
+ 'healthy' => 2,
246
+ 'total' => 3
247
+ }
248
+ }
249
+ expect(subject.health_status(data)).to eq(:partial)
250
+ end
251
+
252
+ it 'returns :unhealthy if all instances are down' do
253
+ data = {
254
+ 'health_status' => {
255
+ 'healthy' => 0,
256
+ 'total' => 3
257
+ }
258
+ }
259
+ expect(subject.health_status(data)).to eq(:unhealthy)
260
+ end
261
+ end
226
262
  end
227
263
  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: 0.15.0.rc1
4
+ version: 0.15.0.rc2
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-08-09 00:00:00.000000000 Z
11
+ date: 2016-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -339,9 +339,10 @@ files:
339
339
  - spec/fixtures/health.yml
340
340
  - spec/fixtures/kontena-build.yml
341
341
  - spec/fixtures/kontena-invalid.yml
342
+ - spec/fixtures/kontena-with-env-file.yml
342
343
  - spec/fixtures/kontena-with-variables.yml
343
344
  - spec/fixtures/kontena.yml
344
- - spec/fixtures/kontena_build_v2.yaml
345
+ - spec/fixtures/kontena_build_v2.yml
345
346
  - spec/fixtures/kontena_v2.yml
346
347
  - spec/fixtures/mysql.yml
347
348
  - spec/fixtures/wordpress-scaled.yml
@@ -419,9 +420,10 @@ test_files:
419
420
  - spec/fixtures/health.yml
420
421
  - spec/fixtures/kontena-build.yml
421
422
  - spec/fixtures/kontena-invalid.yml
423
+ - spec/fixtures/kontena-with-env-file.yml
422
424
  - spec/fixtures/kontena-with-variables.yml
423
425
  - spec/fixtures/kontena.yml
424
- - spec/fixtures/kontena_build_v2.yaml
426
+ - spec/fixtures/kontena_build_v2.yml
425
427
  - spec/fixtures/kontena_v2.yml
426
428
  - spec/fixtures/mysql.yml
427
429
  - spec/fixtures/wordpress-scaled.yml