kontena-cli 0.10.3 → 0.11.0

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/kontena +2 -0
  4. data/kontena-cli.gemspec +2 -0
  5. data/lib/kontena/cli/apps/build_command.rb +3 -2
  6. data/lib/kontena/cli/apps/deploy_command.rb +10 -1
  7. data/lib/kontena/cli/apps/docker_helper.rb +17 -8
  8. data/lib/kontena/cli/apps/logs_command.rb +1 -1
  9. data/lib/kontena/cli/common.rb +58 -7
  10. data/lib/kontena/cli/external_registries/add_command.rb +6 -8
  11. data/lib/kontena/cli/grid_command.rb +1 -1
  12. data/lib/kontena/cli/grids/common.rb +1 -1
  13. data/lib/kontena/cli/grids/current_command.rb +8 -1
  14. data/lib/kontena/cli/grids/env_command.rb +9 -3
  15. data/lib/kontena/cli/login_command.rb +27 -17
  16. data/lib/kontena/cli/logout_command.rb +1 -2
  17. data/lib/kontena/cli/master/aws/create_command.rb +7 -1
  18. data/lib/kontena/cli/master/azure/create_command.rb +7 -1
  19. data/lib/kontena/cli/master/digital_ocean/create_command.rb +6 -0
  20. data/lib/kontena/cli/master/list_command.rb +18 -0
  21. data/lib/kontena/cli/master/use_command.rb +26 -0
  22. data/lib/kontena/cli/master/vagrant/create_command.rb +7 -1
  23. data/lib/kontena/cli/master_command.rb +4 -0
  24. data/lib/kontena/cli/node_command.rb +1 -1
  25. data/lib/kontena/cli/nodes/list_command.rb +40 -14
  26. data/lib/kontena/cli/services/create_command.rb +2 -0
  27. data/lib/kontena/cli/services/services_helper.rb +31 -0
  28. data/lib/kontena/cli/services/update_command.rb +2 -1
  29. data/lib/kontena/cli/vault/list_command.rb +15 -0
  30. data/lib/kontena/cli/vault/read_command.rb +16 -0
  31. data/lib/kontena/cli/vault/remove_command.rb +13 -0
  32. data/lib/kontena/cli/vault/write_command.rb +23 -0
  33. data/lib/kontena/cli/vault_command.rb +15 -0
  34. data/lib/kontena/cli/whoami_command.rb +6 -3
  35. data/lib/kontena/machine/aws/cloudinit.yml +5 -0
  36. data/lib/kontena/machine/aws/cloudinit_master.yml +3 -0
  37. data/lib/kontena/machine/aws/master_provisioner.rb +3 -1
  38. data/lib/kontena/machine/azure/cloudinit.yml +5 -0
  39. data/lib/kontena/machine/azure/cloudinit_master.yml +3 -0
  40. data/lib/kontena/machine/azure/master_provisioner.rb +3 -1
  41. data/lib/kontena/machine/digital_ocean/cloudinit.yml +5 -0
  42. data/lib/kontena/machine/digital_ocean/cloudinit_master.yml +3 -0
  43. data/lib/kontena/machine/digital_ocean/master_provisioner.rb +4 -2
  44. data/lib/kontena/machine/vagrant/Vagrantfile.master.rb.erb +3 -0
  45. data/lib/kontena/machine/vagrant/cloudinit.yml +5 -0
  46. data/lib/kontena/machine/vagrant/master_provisioner.rb +4 -0
  47. data/lib/kontena/scripts/completer +14 -2
  48. data/spec/kontena/cli/app/deploy_command_spec.rb +37 -4
  49. data/spec/kontena/cli/app/scale_spec.rb +6 -1
  50. data/spec/kontena/cli/common_spec.rb +69 -1
  51. data/spec/kontena/cli/deploy_command_spec.rb +29 -3
  52. data/spec/kontena/cli/login_command_spec.rb +10 -0
  53. data/spec/kontena/cli/master/use_command_spec.rb +29 -0
  54. metadata +13 -4
@@ -2,7 +2,6 @@ class Kontena::Cli::LogoutCommand < Clamp::Command
2
2
  include Kontena::Cli::Common
3
3
 
4
4
  def execute
5
- settings['server'].delete('token')
6
- save_settings
5
+ self.access_token = nil
7
6
  end
8
7
  end
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Kontena::Cli::Master::Aws
2
4
  class CreateCommand < Clamp::Command
3
5
  include Kontena::Cli::Common
@@ -12,6 +14,8 @@ module Kontena::Cli::Master::Aws
12
14
  option "--subnet-id", "SUBNET ID", "VPC option to specify subnet to launch instance into"
13
15
  option "--type", "SIZE", "Instance type", default: 't2.small'
14
16
  option "--storage", "STORAGE", "Storage size (GiB)", default: '30'
17
+ option "--vault-secret", "VAULT_SECRET", "Secret key for Vault"
18
+ option "--vault-iv", "VAULT_IV", "Initialization vector for Vault"
15
19
  option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
16
20
  option "--auth-provider-url", "AUTH_PROVIDER_URL", "Define authentication provider url"
17
21
 
@@ -28,7 +32,9 @@ module Kontena::Cli::Master::Aws
28
32
  storage: storage,
29
33
  version: version,
30
34
  key_pair: key_pair,
31
- auth_server: auth_provider_url
35
+ auth_server: auth_provider_url,
36
+ vault_secret: vault_secret || SecureRandom.hex(24),
37
+ vault_iv: vault_iv || SecureRandom.hex(24)
32
38
  )
33
39
  end
34
40
  end
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Kontena::Cli::Master::Azure
2
4
  class CreateCommand < Clamp::Command
3
5
  include Kontena::Cli::Common
@@ -11,6 +13,8 @@ module Kontena::Cli::Master::Azure
11
13
  option "--password", "PASSWORD", "Password"
12
14
  option "--location", "LOCATION", "Location", default: 'West Europe'
13
15
  option "--ssl-cert", "SSL CERT", "SSL certificate file"
16
+ option "--vault-secret", "VAULT_SECRET", "Secret key for Vault"
17
+ option "--vault-iv", "VAULT_IV", "Initialization vector for Vault"
14
18
  option "--auth-provider-url", "AUTH_PROVIDER_URL", "Define authentication provider url"
15
19
  option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
16
20
 
@@ -26,7 +30,9 @@ module Kontena::Cli::Master::Azure
26
30
  subnet: subnet,
27
31
  location: location,
28
32
  auth_server: auth_provider_url,
29
- version: version
33
+ version: version,
34
+ vault_secret: vault_secret || SecureRandom.hex(24),
35
+ vault_iv: vault_iv || SecureRandom.hex(24)
30
36
  )
31
37
  end
32
38
  end
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Kontena::Cli::Master::DigitalOcean
2
4
  class CreateCommand < Clamp::Command
3
5
  include Kontena::Cli::Common
@@ -7,6 +9,8 @@ module Kontena::Cli::Master::DigitalOcean
7
9
  option "--ssl-cert", "SSL CERT", "SSL certificate file"
8
10
  option "--size", "SIZE", "Droplet size", default: '1gb'
9
11
  option "--region", "REGION", "Region", default: 'ams2'
12
+ option "--vault-secret", "VAULT_SECRET", "Secret key for Vault"
13
+ option "--vault-iv", "VAULT_IV", "Initialization vector for Vault"
10
14
  option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
11
15
  option "--auth-provider-url", "AUTH_PROVIDER_URL", "Define authentication provider url"
12
16
 
@@ -23,6 +27,8 @@ module Kontena::Cli::Master::DigitalOcean
23
27
  region: region,
24
28
  version: version,
25
29
  auth_server: auth_provider_url,
30
+ vault_secret: vault_secret || SecureRandom.hex(24),
31
+ vault_iv: vault_iv || SecureRandom.hex(24)
26
32
  )
27
33
  end
28
34
 
@@ -0,0 +1,18 @@
1
+ module Kontena::Cli::Master
2
+ class ListCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ def execute
6
+ puts '%-24s %-30s' % ['Name', 'Url']
7
+ current_server = settings['current_server']
8
+ settings['servers'].each do |server|
9
+ if server['name'] == current_server
10
+ name = "* #{server['name']}"
11
+ else
12
+ name = server['name']
13
+ end
14
+ puts '%-24s %-30s' % [name, server['url']]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ module Kontena::Cli::Master
2
+ class UseCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ parameter "NAME", "Master name to use"
6
+
7
+ def execute
8
+ master = find_master_by_name(name)
9
+ if !master.nil?
10
+ self.current_master = master['name']
11
+ puts "Using master: #{master['name'].cyan}"
12
+ puts "URL: #{master['url'].cyan}"
13
+ else
14
+ abort "Could not resolve master by name [#{name}]. For a list of known masters please run: kontena master list".colorize(:red)
15
+ end
16
+ end
17
+
18
+ def find_master_by_name(name)
19
+ settings['servers'].each do |server|
20
+ return server if server['name'] == name
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Kontena::Cli::Master::Vagrant
2
4
  class CreateCommand < Clamp::Command
3
5
  include Kontena::Cli::Common
@@ -5,6 +7,8 @@ module Kontena::Cli::Master::Vagrant
5
7
  option "--memory", "MEMORY", "How much memory node has", default: '512'
6
8
  option "--version", "VERSION", "Define installed Kontena version", default: 'latest'
7
9
  option "--auth-provider-url", "AUTH_PROVIDER_URL", "Define authentication provider url"
10
+ option "--vault-secret", "VAULT_SECRET", "Secret key for Vault"
11
+ option "--vault-iv", "VAULT_IV", "Initialization vector for Vault"
8
12
 
9
13
  def execute
10
14
  require 'kontena/machine/vagrant'
@@ -12,7 +16,9 @@ module Kontena::Cli::Master::Vagrant
12
16
  provisioner.run!(
13
17
  memory: memory,
14
18
  version: version,
15
- auth_server: auth_provider_url
19
+ auth_server: auth_provider_url,
20
+ vault_secret: vault_secret || SecureRandom.hex(24),
21
+ vault_iv: vault_iv || SecureRandom.hex(24)
16
22
  )
17
23
  end
18
24
  end
@@ -2,6 +2,8 @@ require_relative 'master/vagrant_command'
2
2
  require_relative 'master/aws_command'
3
3
  require_relative 'master/digital_ocean_command'
4
4
  require_relative 'master/azure_command'
5
+ require_relative 'master/use_command'
6
+ require_relative 'master/list_command'
5
7
 
6
8
  class Kontena::Cli::MasterCommand < Clamp::Command
7
9
 
@@ -9,6 +11,8 @@ class Kontena::Cli::MasterCommand < Clamp::Command
9
11
  subcommand "aws", "AWS specific commands", Kontena::Cli::Master::AwsCommand
10
12
  subcommand "digitalocean", "DigitalOcean specific commands", Kontena::Cli::Master::DigitalOceanCommand
11
13
  subcommand "azure", "Azure specific commands", Kontena::Cli::Master::AzureCommand
14
+ subcommand ["list", "ls"], "List masters where client has logged in", Kontena::Cli::Master::ListCommand
15
+ subcommand "use", "Switch to use selected master", Kontena::Cli::Master::UseCommand
12
16
 
13
17
  def execute
14
18
  end
@@ -15,7 +15,7 @@ class Kontena::Cli::NodeCommand < Clamp::Command
15
15
  subcommand "show", "Show node", Kontena::Cli::Nodes::ShowCommand
16
16
  subcommand "ssh", "Ssh into node", Kontena::Cli::Nodes::SshCommand
17
17
  subcommand "update", "Update node", Kontena::Cli::Nodes::UpdateCommand
18
- subcommand "remove", "Remove node", Kontena::Cli::Nodes::RemoveCommand
18
+ subcommand ["remove","rm"], "Remove node", Kontena::Cli::Nodes::RemoveCommand
19
19
 
20
20
  subcommand "vagrant", "Vagrant specific commands", Kontena::Cli::Nodes::VagrantCommand
21
21
  subcommand "digitalocean", "DigitalOcean specific commands", Kontena::Cli::Nodes::DigitalOceanCommand
@@ -2,26 +2,52 @@ module Kontena::Cli::Nodes
2
2
  class ListCommand < Clamp::Command
3
3
  include Kontena::Cli::Common
4
4
 
5
+ option ["--all"], :flag, "List nodes for all grids", default: false
6
+
5
7
  def execute
6
8
  require_api_url
7
9
  require_current_grid
8
10
  token = require_token
9
11
 
10
- grids = client(token).get("grids/#{current_grid}/nodes")
11
- puts "%-30s %-40s %-15s %-30s %-10s" % ['Name', 'OS', 'Driver', 'Labels', 'Status']
12
- grids['nodes'].each do |node|
13
- if node['connected']
14
- status = 'online'
15
- else
16
- status = 'offline'
12
+ if all?
13
+ grids = client(token).get("grids")
14
+ puts "%-30s %-30s %-40s %-15s %-30s %-10s" % [ 'Grid', 'Name', 'OS', 'Driver', 'Labels', 'Status']
15
+
16
+ grids['grids'].each do |grid|
17
+ nodes = client(token).get("grids/#{grid['name']}/nodes")
18
+ nodes['nodes'].each do |node|
19
+ if node['connected']
20
+ status = 'online'
21
+ else
22
+ status = 'offline'
23
+ end
24
+ puts "%-30.30s %-30.30s %-40.40s %-15s %-30.30s %-10s" % [
25
+ grid['name'],
26
+ node['name'],
27
+ "#{node['os']} (#{node['kernel_version']})",
28
+ node['driver'],
29
+ (node['labels'] || ['-']).join(","),
30
+ status
31
+ ]
32
+ end
33
+ end
34
+ else
35
+ nodes = client(token).get("grids/#{current_grid}/nodes")
36
+ puts "%-30s %-40s %-15s %-30s %-10s" % ['Name', 'OS', 'Driver', 'Labels', 'Status']
37
+ nodes['nodes'].each do |node|
38
+ if node['connected']
39
+ status = 'online'
40
+ else
41
+ status = 'offline'
42
+ end
43
+ puts "%-30.30s %-40.40s %-15s %-30.30s %-10s" % [
44
+ node['name'],
45
+ "#{node['os']} (#{node['kernel_version']})",
46
+ node['driver'],
47
+ (node['labels'] || ['-']).join(","),
48
+ status
49
+ ]
17
50
  end
18
- puts "%-30.30s %-40.40s %-15s %-30.30s %-10s" % [
19
- node['name'],
20
- "#{node['os']} (#{node['kernel_version']})",
21
- node['driver'],
22
- (node['labels'] || ['-']).join(","),
23
- status
24
- ]
25
51
  end
26
52
  end
27
53
  end
@@ -31,6 +31,7 @@ module Kontena::Cli::Services
31
31
  option "--deploy-wait-for-port", "PORT", "Wait for port to respond when deploying"
32
32
  option "--deploy-min-health", "FLOAT", "The minimum percentage (0.0 - 1.0) of healthy instances that do not sacrifice overall service availability while deploying"
33
33
  option "--pid", "PID", "Pid namespace to use"
34
+ option "--secret", "SECRET", "Import secret from Vault", multivalued: true
34
35
 
35
36
  def execute
36
37
  require_api_url
@@ -58,6 +59,7 @@ module Kontena::Cli::Services
58
59
  data[:cpu_shares] = cpu_shares if cpu_shares
59
60
  data[:affinity] = affinity_list unless affinity_list.empty?
60
61
  data[:env] = env_list unless env_list.empty?
62
+ data[:secrets] = parse_secrets(secret_list)
61
63
  data[:container_count] = instances if instances
62
64
  data[:cmd] = cmd.split(" ") if cmd
63
65
  data[:user] = user if user
@@ -1,5 +1,6 @@
1
1
  require 'kontena/client'
2
2
  require_relative '../common'
3
+ require 'shell-spinner'
3
4
 
4
5
  module Kontena
5
6
  module Cli
@@ -78,6 +79,15 @@ module Kontena
78
79
  end
79
80
  end
80
81
 
82
+ if service['secrets'].to_a.size > 0
83
+ puts " secrets: "
84
+ service['secrets'].to_a.each do |s|
85
+ puts " - secret: #{s['secret']}"
86
+ puts " secret: #{s['name']}"
87
+ puts " type: #{s['type']}"
88
+ end
89
+ end
90
+
81
91
  if service['env'].to_a.size > 0
82
92
  puts " env: "
83
93
  service['env'].to_a.each do |e|
@@ -175,6 +185,16 @@ module Kontena
175
185
  client(token).post("services/#{param}/deploy", data)
176
186
  end
177
187
 
188
+ # @param [String] token
189
+ # @param [String] name
190
+ def wait_for_deploy_to_finish(token, name)
191
+ ShellSpinner " " do
192
+ until client(token).get("services/#{name}")['state'] != 'deploying' do
193
+ sleep 1
194
+ end
195
+ end
196
+ end
197
+
178
198
  # @param [String] token
179
199
  # @param [String] service_id
180
200
  def start_service(token, service_id)
@@ -272,6 +292,17 @@ module Kontena
272
292
  end
273
293
  opts
274
294
  end
295
+
296
+ # @param [Array<String>] secret_opts
297
+ # @return [Array<Hash>]
298
+ def parse_secrets(secret_opts)
299
+ secrets = []
300
+ secret_opts.each do |s|
301
+ secret, name, type = s.split(':')
302
+ secrets << {secret: secret, name: name, type: type}
303
+ end
304
+ secrets
305
+ end
275
306
  end
276
307
  end
277
308
  end
@@ -28,7 +28,7 @@ module Kontena::Cli::Services
28
28
  option "--deploy-wait-for-port", "PORT", "Wait for port to respond when deploying"
29
29
  option "--deploy-min-health", "FLOAT", "The minimum percentage (0.0 - 1.0) of healthy instances that do not sacrifice overall service availability while deploying"
30
30
  option "--pid", "PID", "Pid namespace to use"
31
-
31
+ option "--secret", "SECRET", "Import secret from Vault", multivalued: true
32
32
 
33
33
  def execute
34
34
  require_api_url
@@ -51,6 +51,7 @@ module Kontena::Cli::Services
51
51
  data[:cpu_shares] = cpu_shares if cpu_shares
52
52
  data[:affinity] = affinity_list unless affinity_list.empty?
53
53
  data[:env] = env_list unless env_list.empty?
54
+ data[:secrets] = parse_secrets(secret_list)
54
55
  data[:container_count] = instances if instances
55
56
  data[:cmd] = cmd.split(" ") if cmd
56
57
  data[:user] = user if user
@@ -0,0 +1,15 @@
1
+ module Kontena::Cli::Vault
2
+ class ListCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ def execute
6
+ require_api_url
7
+ token = require_token
8
+ result = client(token).get("grids/#{current_grid}/secrets")
9
+ puts '%-30.30s %-30.30s' % ['NAME', 'CREATED AT']
10
+ result['secrets'].each do |secret|
11
+ puts '%-30.30s %-30.30s' % [secret['name'], secret['created_at']]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Kontena::Cli::Vault
2
+ class ReadCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ parameter "NAME", "Secret name"
6
+
7
+ def execute
8
+ require_api_url
9
+ token = require_token
10
+ result = client(token).get("secrets/#{current_grid}/#{name}")
11
+ puts "#{result['name']}:"
12
+ puts " created_at: #{result['created_at']}"
13
+ puts " value: #{result['value']}"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Kontena::Cli::Vault
2
+ class RemoveCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ parameter "NAME", "Secret name"
6
+
7
+ def execute
8
+ require_api_url
9
+ token = require_token
10
+ client(token).delete("secrets/#{current_grid}/#{name}")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Kontena::Cli::Vault
2
+ class WriteCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ parameter 'NAME', 'Secret name'
6
+ parameter '[VALUE]', 'Secret value'
7
+
8
+ def execute
9
+ require_api_url
10
+ token = require_token
11
+ secret = value
12
+ if secret.to_s == ''
13
+ secret = STDIN.read
14
+ end
15
+ abort('No value provided') if secret.to_s == ''
16
+ data = {
17
+ name: name,
18
+ value: secret
19
+ }
20
+ client(token).post("grids/#{current_grid}/secrets", data)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'vault/write_command'
2
+ require_relative 'vault/list_command'
3
+ require_relative 'vault/read_command'
4
+ require_relative 'vault/remove_command'
5
+
6
+ class Kontena::Cli::VaultCommand < Clamp::Command
7
+
8
+ subcommand "write", "Write a secret", Kontena::Cli::Vault::WriteCommand
9
+ subcommand ["list", "ls"], "List secrets", Kontena::Cli::Vault::ListCommand
10
+ subcommand "read", "Read secret", Kontena::Cli::Vault::ReadCommand
11
+ subcommand ["remove", "rm"], "Remove secret", Kontena::Cli::Vault::RemoveCommand
12
+
13
+ def execute
14
+ end
15
+ end
@@ -10,10 +10,13 @@ class Kontena::Cli::WhoamiCommand < Clamp::Command
10
10
  end
11
11
 
12
12
  require_api_url
13
- puts "Master: #{settings['server']['url']}"
13
+ puts "Master: #{self.current_master['name']}"
14
+ puts "URL: #{api_url}"
15
+ puts "Grid: #{current_grid}"
14
16
  token = require_token
15
- response = client(token).get('user')
16
- puts "User: #{response['email']}"
17
+ user = client(token).get('user')
18
+ puts "User: #{user['email']}"
19
+
17
20
  end
18
21
 
19
22
  end