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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7d0e10fa18b709c1354a89d324619c7f68acaf9
4
- data.tar.gz: 5915e5df8a85eae8626a0b7531b0ce0db76a1dbf
3
+ metadata.gz: 960104670b01a01904b3c6c59b8a34a54fa00d4b
4
+ data.tar.gz: 818f20aebc2d1774d80b631ccc8cb6c30e393a5e
5
5
  SHA512:
6
- metadata.gz: 131645acde33c5569b7a44dd5471a1de4afd19fedc71dd3f689dded6a05dc2163d50394ce7d1cbde1a0c426d2265cc27990b06f709c005a7672cb0152f9f0bee
7
- data.tar.gz: 3032517244f0ab4333cfa289aa736f46837829059a972c92b8debf75d5552cdfccc13a1ae72330bc132822d48e259810c828a1b6470ba20636fc0ef7382bcaa6
6
+ metadata.gz: 9f23afc68dbe36ff30c0f0f7e93ae6880f2dbd455ba357a9b7d58b3ca55683a325704fc650e0a984055ab2f4fff1a5bbc7268074c3e0c48de169974decf0c86e
7
+ data.tar.gz: f30c99956e19d094efa2e718c9607c8e58245211e1ad2f12d369e4ecf5f41e6f6dceeac651ef81174b64fc5d7b5290fdef5beeae0f64a0d9b0e1b7641aaabd9e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.3
1
+ 0.11.0
data/bin/kontena CHANGED
@@ -33,6 +33,7 @@ require 'kontena/cli/external_registry_command'
33
33
  require 'kontena/cli/deploy_command'
34
34
  require 'kontena/cli/app_command'
35
35
  require 'kontena/cli/etcd_command'
36
+ require 'kontena/cli/vault_command'
36
37
  require 'kontena/cli/version_command'
37
38
 
38
39
  class MainCommand < Clamp::Command
@@ -40,6 +41,7 @@ class MainCommand < Clamp::Command
40
41
  subcommand "grid", "Grid specific commands", Kontena::Cli::GridCommand
41
42
  subcommand "app", "App specific commands", Kontena::Cli::AppCommand
42
43
  subcommand "service", "Service specific commands", Kontena::Cli::ServiceCommand
44
+ subcommand "vault", "Vault specific commands", Kontena::Cli::VaultCommand
43
45
  subcommand "deploy", "Create and deploy multiple services from YAML file", Kontena::Cli::DeployCommand
44
46
  subcommand "node", "Node specific commands", Kontena::Cli::NodeCommand
45
47
  subcommand "master", "Master specific commands", Kontena::Cli::MasterCommand
data/kontena-cli.gemspec CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.required_ruby_version = ">= 2.0.0"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.7"
22
24
  spec.add_development_dependency "rake", "~> 10.0"
23
25
  spec.add_runtime_dependency 'excon'
@@ -9,7 +9,8 @@ module Kontena::Cli::Apps
9
9
 
10
10
  option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
11
11
  option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
12
- parameter "[SERVICE] ...", "Services to start"
12
+ option ['--no-cache'], :flag, 'Do not use cache when building the image', default: false
13
+ parameter "[SERVICE] ...", "Services to build"
13
14
 
14
15
  attr_reader :services, :service_prefix
15
16
 
@@ -17,7 +18,7 @@ module Kontena::Cli::Apps
17
18
  require_config_file(filename)
18
19
  @service_prefix = project_name || current_dir
19
20
  @services = load_services(filename, service_list, service_prefix)
20
- process_docker_images(services, true)
21
+ process_docker_images(services, true, no_cache?)
21
22
  end
22
23
  end
23
24
  end
@@ -11,6 +11,7 @@ module Kontena::Cli::Apps
11
11
  option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
12
12
  option ['--no-build'], :flag, 'Don\'t build an image, even if it\'s missing', default: false
13
13
  option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
14
+ option '--async', :flag, 'Run deploys async/parallel'
14
15
 
15
16
  parameter "[SERVICE] ...", "Services to start"
16
17
 
@@ -38,7 +39,14 @@ module Kontena::Cli::Apps
38
39
 
39
40
  def deploy_services(queue)
40
41
  queue.each do |service|
41
- deploy_service(token, service['id'].split('/').last, {})
42
+ name = service['id'].split('/').last
43
+ deploy_service(token, name, {})
44
+ print "deploying #{name.sub("#{service_prefix}-", '').colorize(:cyan)}"
45
+ unless async?
46
+ wait_for_deploy_to_finish(token, service['id'])
47
+ else
48
+ puts ''
49
+ end
42
50
  end
43
51
  end
44
52
 
@@ -150,6 +158,7 @@ module Kontena::Cli::Apps
150
158
  end
151
159
 
152
160
  data[:hooks] = options['hooks'] || {}
161
+ data[:secrets] = options['secrets'] if options['secrets']
153
162
 
154
163
  data
155
164
  end
@@ -1,14 +1,19 @@
1
1
  module Kontena::Cli::Apps
2
2
  module DockerHelper
3
3
 
4
- def process_docker_images(services, force_build = false)
4
+ def process_docker_images(services, force_build = false, no_cache=false)
5
+ if services.none?{|name, service| service['build']}
6
+ puts "Not found any service with build option"
7
+ return
8
+ end
9
+
5
10
  services.each do |name, service|
6
11
  if service['build'] && (!image_exist?(service['image']) || force_build)
12
+ dockerfile = service['dockerfile'] || 'Dockerfile'
7
13
  abort("'#{service['image']}' is not valid Docker image name") unless validate_image_name(service['image'])
8
- abort("'#{service['build']}' does not have Dockerfile") unless dockerfile_exist?(service['build'])
14
+ abort("'#{service['build']}' does not have #{dockerfile}") unless dockerfile_exist?(service['build'], dockerfile)
9
15
  puts "Building image #{service['image'].colorize(:cyan)}"
10
- build_docker_image(service['image'], service['build'])
11
-
16
+ build_docker_image(service['image'], service['build'], dockerfile, no_cache)
12
17
  puts "Pushing image #{service['image'].colorize(:cyan)} to registry"
13
18
  push_docker_image(service['image'])
14
19
  end
@@ -20,8 +25,12 @@ module Kontena::Cli::Apps
20
25
  end
21
26
 
22
27
 
23
- def build_docker_image(name, path)
24
- ret = system("docker build -t #{name} #{path}")
28
+ def build_docker_image(name, path, dockerfile, no_cache=false)
29
+ if no_cache
30
+ ret = system("docker build -t #{name} -f #{dockerfile} --no-cache #{path}")
31
+ else
32
+ ret = system("docker build -t #{name} -f #{dockerfile} #{path}")
33
+ end
25
34
  abort("Failed to build image #{name.colorize(:cyan)}") unless ret
26
35
  ret
27
36
  end
@@ -36,8 +45,8 @@ module Kontena::Cli::Apps
36
45
  `docker history #{image} 2>&1` ; $?.success?
37
46
  end
38
47
 
39
- def dockerfile_exist?(path)
40
- file = File.join(File.expand_path(path), 'Dockerfile')
48
+ def dockerfile_exist?(path, dockerfile)
49
+ file = File.join(File.expand_path(path), dockerfile)
41
50
  File.exist?(file)
42
51
  end
43
52
  end
@@ -36,7 +36,7 @@ module Kontena::Cli::Apps
36
36
  services.each do |service_name, opts|
37
37
  service = get_service(token, prefixed_name(service_name)) rescue false
38
38
  result = client(token).get("services/#{service['id']}/container_logs?#{query_params.join('&')}") if service
39
- logs = logs + result['logs']
39
+ logs = logs + result['logs'] if result && result['logs']
40
40
  end
41
41
  logs.sort!{|x,y| DateTime.parse(x['created_at']) <=> DateTime.parse(y['created_at'])}
42
42
  logs.each do |log|
@@ -9,7 +9,7 @@ module Kontena
9
9
  end
10
10
 
11
11
  def require_token
12
- token = ENV['KONTENA_TOKEN'] || settings['server']['token']
12
+ token = ENV['KONTENA_TOKEN'] || current_master['token']
13
13
  unless token
14
14
  raise ArgumentError.new("Please login first using: kontena login")
15
15
  end
@@ -25,7 +25,6 @@ module Kontena
25
25
 
26
26
  @client = Kontena::Client.new(api_url, headers)
27
27
  end
28
-
29
28
  @client
30
29
  end
31
30
 
@@ -41,15 +40,27 @@ module Kontena
41
40
  if @settings.nil?
42
41
  if File.exists?(settings_filename)
43
42
  @settings = JSON.parse(File.read(settings_filename))
43
+ unless @settings['current_server']
44
+ # Let's migrate the old settings model to new
45
+ @settings['server']['name'] = 'default'
46
+ @settings = {
47
+ 'current_server' => 'default',
48
+ 'servers' => [ @settings['server']]
49
+ }
50
+ save_settings
51
+ end
44
52
  else
45
- @settings = {'server' => {}}
53
+ @settings = {
54
+ 'current_server' => 'default',
55
+ 'servers' => [{}]
56
+ }
46
57
  end
47
58
  end
48
59
  @settings
49
60
  end
50
61
 
51
62
  def api_url
52
- url = ENV['KONTENA_URL'] || settings['server']['url']
63
+ url = ENV['KONTENA_URL'] || current_master['url']
53
64
  unless url
54
65
  raise ArgumentError.new("It seem's that you are not logged into Kontena master, please login with: kontena login")
55
66
  end
@@ -68,7 +79,7 @@ module Kontena
68
79
  end
69
80
 
70
81
  def current_grid=(grid)
71
- settings['server']['grid'] = grid['id']
82
+ settings['servers'][current_master_index]['grid'] = grid['id']
72
83
  save_settings
73
84
  end
74
85
 
@@ -79,12 +90,52 @@ module Kontena
79
90
  end
80
91
 
81
92
  def clear_current_grid
82
- settings['server'].delete('grid')
93
+ settings['servers'][current_master_index].delete('grid')
83
94
  save_settings
84
95
  end
85
96
 
86
97
  def current_grid
87
- ENV['KONTENA_GRID'] || settings['server']['grid']
98
+ ENV['KONTENA_GRID'] || current_master['grid']
99
+ end
100
+
101
+ def current_master_index
102
+ current_server = settings['current_server'] || 'default'
103
+ settings['servers'].find_index{|m| m['name'] == current_server}
104
+ end
105
+
106
+ def current_master
107
+ index = current_master_index
108
+ unless index
109
+ raise ArgumentError.new("It seem's that you are not logged into ANY Kontena master, please login with: kontena login")
110
+ end
111
+ settings['servers'][index]
112
+ end
113
+
114
+ def current_master=(master_alias)
115
+ settings['current_server'] = master_alias
116
+ save_settings
117
+ end
118
+
119
+ def api_url=(api_url)
120
+ settings['servers'][current_master_index]['url'] = api_url
121
+ save_settings
122
+ end
123
+
124
+ def access_token=(token)
125
+ settings['servers'][current_master_index]['token'] = token
126
+ save_settings
127
+ end
128
+
129
+ def add_master(server_name, master_info)
130
+ server_name = server_name || 'default'
131
+ index = settings['servers'].find_index{|m| m['name'] == server_name}
132
+ if index
133
+ settings['servers'][index] = master_info
134
+ else
135
+ settings['servers'] << master_info
136
+ end
137
+ settings['current_server'] = server_name
138
+ save_settings
88
139
  end
89
140
 
90
141
  def save_settings
@@ -2,19 +2,17 @@ module Kontena::Cli::ExternalRegistries
2
2
  class AddCommand < Clamp::Command
3
3
  include Kontena::Cli::Common
4
4
 
5
- def execute
6
- require 'highline/import'
5
+ parameter '[URL]', 'Docker Registry url', default: 'https://index.docker.io/v2/'
6
+
7
+ option ['-u', '--username'], 'USERNAME', 'Username', required: true
8
+ option ['-e', '--email'], 'EMAIL', 'Email', required: true
9
+ option ['-p', '--password'], 'PASSWORD', 'Password', required: true
7
10
 
8
- default_url = 'https://index.docker.io/v2/'
11
+ def execute
9
12
  require_api_url
10
13
  require_current_grid
11
14
  token = require_token
12
15
 
13
- username = ask("Username: ")
14
- password = ask("Password: ") { |q| q.echo = "*" }
15
- email = ask("Email: ")
16
- url = ask("URL [#{default_url}]: ")
17
- url = default_url if url.strip == ''
18
16
  data = { username: username, password: password, email: email, url: url }
19
17
  client(token).post("grids/#{current_grid}/external_registries", data)
20
18
  end
@@ -18,7 +18,7 @@ class Kontena::Cli::GridCommand < Clamp::Command
18
18
  subcommand "use", "Switch to use specific grid", Kontena::Cli::Grids::UseCommand
19
19
  subcommand "show", "Show grid details", Kontena::Cli::Grids::ShowCommand
20
20
  subcommand "logs", "Show logs from grid containers", Kontena::Cli::Grids::LogsCommand
21
- subcommand "remove", "Remove a grid", Kontena::Cli::Grids::RemoveCommand
21
+ subcommand ["remove","rm"], "Remove a grid", Kontena::Cli::Grids::RemoveCommand
22
22
  subcommand "current", "Show current grid details", Kontena::Cli::Grids::CurrentCommand
23
23
  subcommand "env", "Show the current grid environment details", Kontena::Cli::Grids::EnvCommand
24
24
  subcommand "audit-log", "Show audit log of the current grid", Kontena::Cli::Grids::AuditLogCommand
@@ -5,7 +5,7 @@ module Kontena::Cli::Grids
5
5
  # @param [Hash] grid
6
6
  def print_grid(grid)
7
7
  puts "#{grid['name']}:"
8
- puts " uri: #{settings['server']['url'].sub('http', 'ws')}"
8
+ puts " uri: #{self.current_master['url'].sub('http', 'ws')}"
9
9
  puts " token: #{grid['token']}"
10
10
  puts " users: #{grid['user_count']}"
11
11
  puts " nodes: #{grid['node_count']}"
@@ -5,13 +5,20 @@ module Kontena::Cli::Grids
5
5
  include Kontena::Cli::Common
6
6
  include Common
7
7
 
8
+ option ["--name"], :flag, "Show name only", default: false
9
+
8
10
  def execute
9
11
  require_api_url
10
12
  if current_grid.nil?
11
13
  abort 'No grid selected. To select grid, please run: kontena grid use <grid name>'
12
14
  else
15
+
13
16
  grid = client(require_token).get("grids/#{current_grid}")
14
- print_grid(grid)
17
+ if name?
18
+ puts "#{grid['name']}"
19
+ else
20
+ print_grid(grid)
21
+ end
15
22
  end
16
23
  end
17
24
  end
@@ -5,14 +5,20 @@ module Kontena::Cli::Grids
5
5
  include Kontena::Cli::Common
6
6
  include Common
7
7
 
8
+ parameter "[NAME]", "Grid name"
8
9
  option ["-e", "--export"], :flag, "Add export", default: false
9
10
 
10
11
  def execute
11
12
  require_api_url
12
- if current_grid.nil?
13
- abort 'No grid selected. To select grid, please run: kontena grid use <grid name>'
13
+
14
+ name_or_current = name.nil? ? current_grid : name
15
+
16
+ if name_or_current.nil?
17
+ abort "No grid selected. Use: kontena grid env <name>, or select a grid with: kontena grid use <name>"
14
18
  else
15
- grid = client(require_token).get("grids/#{current_grid}")
19
+ grid = find_grid_by_name(name_or_current)
20
+ abort("Grid not found".colorize(:red)) unless grid
21
+
16
22
  prefix = export? ? 'export ' : ''
17
23
  puts "#{prefix}KONTENA_URI=#{settings['server']['url'].sub('http', 'ws')}"
18
24
  puts "#{prefix}KONTENA_TOKEN=#{grid['token']}"
@@ -3,13 +3,16 @@ class Kontena::Cli::LoginCommand < Clamp::Command
3
3
 
4
4
  parameter "URL", "Kontena Master URI"
5
5
 
6
+ option ['-n', '--name'], 'NAME', 'Local alias name for the master. Default default'
7
+
6
8
  def execute
7
9
  require 'highline/import'
8
10
 
9
11
  until !url.nil? && !url.empty?
10
12
  api_url = ask('Kontena Master Node URL: ')
11
13
  end
12
- update_api_url(url)
14
+
15
+ @api_url = url
13
16
 
14
17
  unless request_server_info
15
18
  puts 'Could not connect to server'.colorize(:red)
@@ -21,7 +24,7 @@ class Kontena::Cli::LoginCommand < Clamp::Command
21
24
  response = do_login(email, password)
22
25
 
23
26
  if response
24
- update_access_token(response['access_token'])
27
+ update_master_info(name, url, response['access_token'])
25
28
  display_logo
26
29
  puts ''
27
30
  puts "Logged in as #{response['user']['name'].green}"
@@ -52,6 +55,14 @@ class Kontena::Cli::LoginCommand < Clamp::Command
52
55
  end
53
56
  end
54
57
 
58
+
59
+ def login_client
60
+ if @login_client.nil?
61
+ @login_client = Kontena::Client.new(@api_url)
62
+ end
63
+ @login_client
64
+ end
65
+
55
66
  def do_login(email, password)
56
67
  params = {
57
68
  username: email,
@@ -59,13 +70,13 @@ class Kontena::Cli::LoginCommand < Clamp::Command
59
70
  grant_type: 'password',
60
71
  scope: 'user'
61
72
  }
62
- client.post('auth', params)
73
+ login_client.post('auth', params)
63
74
  end
64
75
 
65
76
  def request_server_info
66
77
  valid = true
67
78
  begin
68
- client.get('ping') # test server connection
79
+ login_client.get('ping') # test server connection
69
80
  rescue Excon::Errors::SocketError => exc
70
81
  if exc.message.include?('Unable to verify certificate')
71
82
  puts "The server uses a certificate signed by an unknown authority.".colorize(:red)
@@ -81,21 +92,20 @@ class Kontena::Cli::LoginCommand < Clamp::Command
81
92
  end
82
93
 
83
94
  ##
84
- # Store api_url to config file
85
95
  #
86
- # @param [String] api_url
87
- def update_api_url(api_url)
88
- settings['server']['url'] = api_url
89
- save_settings
90
- end
91
-
92
- ##
93
- # Store access token to config file
96
+ # @param [String] name
97
+ # @param [String] url
98
+ # @param [String] token
94
99
  #
95
- # @param [String] access_token
96
- def update_access_token(access_token)
97
- settings['server']['token'] = access_token
98
- save_settings
100
+ def update_master_info(name, url, token)
101
+ name = name || 'default'
102
+ master = {
103
+ 'name' => name,
104
+ 'url' => url,
105
+ 'token' => token
106
+ }
107
+
108
+ self.add_master(name, master)
99
109
  end
100
110
 
101
111
  def display_logo