kontena-cli 0.10.3 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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