kontena-cli 0.16.0.pre7 → 0.16.0.pre8
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/kontena-cli.gemspec +2 -5
- data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +15 -21
- data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +5 -3
- data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +3 -1
- data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +7 -18
- data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +93 -0
- data/lib/kontena/callbacks/master/deploy/90_proptip_after_deploy.rb +33 -0
- data/lib/kontena/cli/apps/build_command.rb +2 -1
- data/lib/kontena/cli/apps/common.rb +3 -2
- data/lib/kontena/cli/apps/config_command.rb +3 -3
- data/lib/kontena/cli/apps/deploy_command.rb +1 -0
- data/lib/kontena/cli/apps/docker_helper.rb +7 -7
- data/lib/kontena/cli/apps/list_command.rb +1 -1
- data/lib/kontena/cli/apps/logs_command.rb +1 -1
- data/lib/kontena/cli/apps/monitor_command.rb +2 -2
- data/lib/kontena/cli/apps/remove_command.rb +1 -1
- data/lib/kontena/cli/apps/restart_command.rb +1 -1
- data/lib/kontena/cli/apps/scale_command.rb +1 -1
- data/lib/kontena/cli/apps/start_command.rb +1 -1
- data/lib/kontena/cli/apps/stop_command.rb +1 -1
- data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +19 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +22 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +21 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +54 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +22 -0
- data/lib/kontena/cli/apps/yaml/validations.rb +60 -90
- data/lib/kontena/cli/apps/yaml/validator.rb +9 -31
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +13 -40
- data/lib/kontena/cli/cloud/login_command.rb +2 -2
- data/lib/kontena/cli/cloud/master/add_command.rb +114 -34
- data/lib/kontena/cli/cloud/master/list_command.rb +5 -2
- data/lib/kontena/cli/cloud/master/remove_command.rb +69 -0
- data/lib/kontena/cli/cloud/master_command.rb +2 -2
- data/lib/kontena/cli/common.rb +10 -17
- data/lib/kontena/cli/config.rb +4 -3
- data/lib/kontena/cli/grids/env_command.rb +5 -5
- data/lib/kontena/cli/localhost_web_server.rb +1 -1
- data/lib/kontena/cli/master/init_cloud_command.rb +21 -0
- data/lib/kontena/cli/master/login_command.rb +28 -29
- data/lib/kontena/cli/master/remove_command.rb +58 -0
- data/lib/kontena/cli/master/token/create_command.rb +6 -0
- data/lib/kontena/cli/master/users/invite_command.rb +4 -1
- data/lib/kontena/cli/master/users/roles/add_command.rb +4 -3
- data/lib/kontena/cli/master_command.rb +8 -9
- data/lib/kontena/cli/services/list_command.rb +3 -2
- data/lib/kontena/cli/services/services_helper.rb +1 -1
- data/lib/kontena/client.rb +31 -47
- data/lib/kontena/presets/kontena_auth_provider.yml +2 -2
- data/lib/kontena_cli.rb +12 -0
- data/spec/kontena/cli/app/docker_helper_spec.rb +9 -4
- data/spec/kontena/cli/app/yaml/validator_spec.rb +66 -71
- data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +51 -58
- metadata +25 -59
- data/lib/kontena/callbacks/master/deploy/90_suggest_inviting_yourself_after_deploy.rb +0 -24
- data/lib/kontena/cli/cloud/master/delete_command.rb +0 -20
@@ -79,7 +79,7 @@ module Kontena::Cli::Cloud
|
|
79
79
|
any_key_to_continue(10)
|
80
80
|
|
81
81
|
puts "If the browser does not open, try visiting this URL manually:"
|
82
|
-
puts "
|
82
|
+
puts "#{uri.to_s}"
|
83
83
|
puts
|
84
84
|
|
85
85
|
server_thread = Thread.new { Thread.main['response'] = web_server.serve_one }
|
@@ -116,7 +116,7 @@ module Kontena::Cli::Cloud
|
|
116
116
|
|
117
117
|
response = client.get(path) rescue nil
|
118
118
|
if response && response.kind_of?(Hash)
|
119
|
-
kontena_account.username = response['username']
|
119
|
+
kontena_account.username = response['data']['attributes']['username']
|
120
120
|
config.write
|
121
121
|
display_logo
|
122
122
|
display_login_info(only: :account)
|
@@ -7,47 +7,127 @@ module Kontena::Cli::Cloud::Master
|
|
7
7
|
|
8
8
|
requires_current_account_token
|
9
9
|
|
10
|
-
parameter "NAME", "Master name"
|
10
|
+
parameter "[NAME]", "Master name"
|
11
11
|
|
12
|
-
option ['--redirect-uri'], '[URL]', '
|
13
|
-
option ['--url'], '[URL]', '
|
14
|
-
option ['--provider'], '[NAME]', '
|
15
|
-
option ['--name'], '[NAME]', '
|
16
|
-
option ['--version'], '[VERSION]', '
|
17
|
-
option ['--owner'], '[NAME]', '
|
12
|
+
option ['--redirect-uri'], '[URL]', 'Set master redirect URL'
|
13
|
+
option ['--url'], '[URL]', 'Set master URL'
|
14
|
+
option ['--provider'], '[NAME]', 'Set master provider'
|
15
|
+
option ['--name'], '[NAME]', 'Set master name', hidden: true
|
16
|
+
option ['--version'], '[VERSION]', 'Set master version', hidden: true
|
17
|
+
option ['--owner'], '[NAME]', 'Set master owner', hidden: true
|
18
18
|
|
19
|
-
option ['--id'],
|
20
|
-
option ['--
|
19
|
+
option ['--id'], :flag, 'Just output the ID'
|
20
|
+
option ['--return'], :flag, 'Return the ID', hidden: true
|
21
|
+
option ['--force'], :flag, "Don't ask questions"
|
21
22
|
|
22
|
-
option ['--
|
23
|
+
option ['--cloud-master-id'], '[ID]', "Use existing cloud master ID", hidden: true
|
24
|
+
option ['--current'], :flag, 'Register and configure current master', hidden: true
|
23
25
|
|
24
|
-
def
|
25
|
-
attributes = {
|
26
|
-
attributes['
|
27
|
-
attributes['
|
28
|
-
attributes['
|
29
|
-
attributes['
|
30
|
-
attributes['
|
26
|
+
def register(name, url = nil, provider = nil, redirect_uri = nil, version = nil, owner = nil)
|
27
|
+
attributes = {}
|
28
|
+
attributes['name'] = name
|
29
|
+
attributes['url'] = url if url
|
30
|
+
attributes['provider'] = provider if provider
|
31
|
+
attributes['redirect-uri'] = redirect_uri if redirect_uri
|
32
|
+
attributes['version'] = version if version
|
33
|
+
attributes['owner'] = owner if owner
|
31
34
|
|
32
35
|
response = cloud_client.post('user/masters', { data: { attributes: attributes } })
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
36
|
+
exit_with_error "Failed (invalid response)" unless response.kind_of?(Hash)
|
37
|
+
exit_with_error "Failed (no data)" unless response['data']
|
38
|
+
exit_with_error "Failed: #{response['error']}" if response['error']
|
39
|
+
response
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_existing(id)
|
43
|
+
cloud_client.get("user/masters/#{id}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def cloud_masters
|
47
|
+
masters = []
|
48
|
+
spinner "Retrieving a list of your registered Kontena Masters in Kontena Cloud" do |spin|
|
49
|
+
begin
|
50
|
+
masters = Kontena.run("cloud master list --return", returning: :result)
|
51
|
+
rescue SystemExit
|
52
|
+
spin.fail
|
53
|
+
end
|
54
|
+
end
|
55
|
+
masters
|
56
|
+
end
|
57
|
+
|
58
|
+
def new_cloud_master_name(master_name)
|
59
|
+
masters = cloud_masters
|
60
|
+
return master_name if masters.empty?
|
61
|
+
|
62
|
+
existing_master = masters.find { |m| m['attributes']['name'] == master_name }
|
63
|
+
return master_name unless existing_master
|
64
|
+
|
65
|
+
new_name = "#{master_name}-2"
|
66
|
+
new_name.succ! until masters.find { |m| m['attributes']['name'] == new_name }.nil?
|
67
|
+
new_name
|
68
|
+
end
|
69
|
+
|
70
|
+
def register_current
|
71
|
+
require_api_url
|
72
|
+
require_token
|
73
|
+
|
74
|
+
unless self.force?
|
75
|
+
puts "Proceeding will:"
|
76
|
+
puts " * Register the Kontena Master #{current_master.name} to Kontena Cloud"
|
77
|
+
puts " * Configure the Kontena Master to use Kontena Cloud as the"
|
78
|
+
puts " authentication provider"
|
79
|
+
puts
|
80
|
+
puts "After this:"
|
81
|
+
puts " * Users will not be able to reauthenticate without authorizing the"
|
82
|
+
puts " Master to access their Kontena Cloud user information"
|
83
|
+
puts " * Users that have registered a different email address to Kontena"
|
84
|
+
puts " Cloud than the one they currently have as their username in the"
|
85
|
+
puts " master will not be able to authenticate before an administrator"
|
86
|
+
puts " of the Kontena Master creates an invitation code for them"
|
87
|
+
puts " (kontena master users invite old@email.example.com)"
|
88
|
+
exit_with_error "Aborted" unless prompt.yes?("Proceed?")
|
89
|
+
end
|
90
|
+
|
91
|
+
new_name = new_cloud_master_name(current_master.name)
|
92
|
+
|
93
|
+
if self.cloud_master_id
|
94
|
+
response = spinner "Retrieving Master information from Kontena Cloud using id" do
|
95
|
+
get_existing(self.cloud_master_id)
|
50
96
|
end
|
97
|
+
else
|
98
|
+
response = spinner "Registering current Kontena Master '#{current_master.name}' #{" as '#{new_name}' " unless new_name == current_master.name}to Kontena Cloud" do
|
99
|
+
register(new_name, current_master.url)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
spinner "Loading Kontena Cloud auth provider base configuration to Kontena Master" do
|
104
|
+
Kontena.run('master config import --force --preset kontena_auth_provider')
|
105
|
+
end
|
106
|
+
|
107
|
+
spinner "Updating OAuth2 client-id and client-secret to Kontena Master" do
|
108
|
+
Kontena.run("master config set oauth2.client_id=#{response['data']['attributes']['client-id'].shellescape} oauth2.client_secret=#{response['data']['attributes']['client-secret'].shellescape} server.root_url=#{current_master.url.shellescape} server.name=#{current_master.name.shellescape} cloud.provider_is_kontena=true")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def execute
|
113
|
+
unless cloud_client.authentication_ok?(kontena_account.userinfo_endpoint)
|
114
|
+
Kontena.run('cloud login')
|
115
|
+
end
|
116
|
+
|
117
|
+
return register_current if self.current?
|
118
|
+
|
119
|
+
exit_with_error 'Master name is required' unless self.name
|
120
|
+
|
121
|
+
response = register(self.name, self.url, self.provider, self.redirect_uri, self.version, self.owner)
|
122
|
+
if self.return?
|
123
|
+
return response['data']['id']
|
124
|
+
elsif self.id?
|
125
|
+
puts response['data']['id']
|
126
|
+
else
|
127
|
+
puts "Registered master.".colorize(:green)
|
128
|
+
puts "ID: #{response['data']['id']}"
|
129
|
+
puts "Client ID: #{response['data']['attributes']['client-id']}"
|
130
|
+
puts "Client Secret: #{response['data']['attributes']['client-secret']}"
|
51
131
|
end
|
52
132
|
end
|
53
133
|
end
|
@@ -5,18 +5,21 @@ module Kontena::Cli::Cloud::Master
|
|
5
5
|
|
6
6
|
callback_matcher 'cloud-master', 'list'
|
7
7
|
|
8
|
+
option '--return', :flag, 'Return the list', hidden: true
|
9
|
+
|
8
10
|
requires_current_account_token
|
9
11
|
|
10
12
|
def execute
|
11
13
|
response = cloud_client.get('user/masters')
|
12
14
|
unless response && response.kind_of?(Hash) && response['data'].kind_of?(Array)
|
13
|
-
|
14
|
-
exit 1
|
15
|
+
abort "Listing masters failed".colorize(:red)
|
15
16
|
end
|
16
17
|
|
17
18
|
if response['data'].empty?
|
19
|
+
return [] if self.return?
|
18
20
|
puts "No masters registered"
|
19
21
|
else
|
22
|
+
return response['data'] if self.return?
|
20
23
|
puts '%-26.26s %-24s %-12s %s' % ['ID', 'NAME', 'OWNER', 'URL']
|
21
24
|
response['data'].each do |data|
|
22
25
|
attr = data['attributes']
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Kontena::Cli::Cloud::Master
|
2
|
+
class RemoveCommand < Kontena::Command
|
3
|
+
|
4
|
+
include Kontena::Cli::Common
|
5
|
+
|
6
|
+
callback_matcher 'cloud-master', 'delete'
|
7
|
+
|
8
|
+
requires_current_account_token
|
9
|
+
|
10
|
+
parameter "[MASTER_ID]", "Master ID"
|
11
|
+
|
12
|
+
option ['-f', '--force'], :flag, "Don't ask for confirmation"
|
13
|
+
|
14
|
+
def delete_server(id)
|
15
|
+
spinner "Deleting server #{id} from Kontena Cloud" do |spin|
|
16
|
+
begin
|
17
|
+
cloud_client.delete("user/masters/#{id}")
|
18
|
+
rescue
|
19
|
+
spin.fail
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_interactive
|
25
|
+
response = nil
|
26
|
+
spinner "Retrieving a list of registered masters on Kontena Cloud" do
|
27
|
+
response = cloud_client.get('user/masters')
|
28
|
+
unless response && response.kind_of?(Hash) && response['data'].kind_of?(Array)
|
29
|
+
abort 'Listing masters failed'.colorize(:red)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if response['data'].empty?
|
34
|
+
puts "No registered masters"
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
servers_to_delete = prompt.multi_select("Select registered master(s) to delete:") do |menu|
|
39
|
+
response['data'].each do |server|
|
40
|
+
menu.choice "#{server['attributes']['name']} (#{server['attributes']['url'] || "?"})", server['id']
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if servers_to_delete.empty?
|
45
|
+
puts "No masters selected"
|
46
|
+
else
|
47
|
+
puts "About to delete servers from Kontena Cloud:"
|
48
|
+
servers_to_delete.each do |id|
|
49
|
+
puts " * #{id}"
|
50
|
+
end
|
51
|
+
confirm unless self.force?
|
52
|
+
servers_to_delete.each do |id|
|
53
|
+
delete_server(id)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute
|
59
|
+
if self.master_id.nil?
|
60
|
+
run_interactive
|
61
|
+
else
|
62
|
+
confirm unless self.force?
|
63
|
+
delete_server(self.master_id)
|
64
|
+
end
|
65
|
+
exit 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'master/add_command'
|
2
2
|
require_relative 'master/list_command'
|
3
|
-
require_relative 'master/
|
3
|
+
require_relative 'master/remove_command'
|
4
4
|
require_relative 'master/update_command'
|
5
5
|
require_relative 'master/show_command'
|
6
6
|
|
@@ -9,8 +9,8 @@ module Kontena::Cli::Cloud
|
|
9
9
|
include Kontena::Cli::Common
|
10
10
|
|
11
11
|
subcommand ['list', 'ls'], "List masters in Kontena Cloud", Kontena::Cli::Cloud::Master::ListCommand
|
12
|
+
subcommand ['remove', 'rm'], "Remove a master registration from Kontena Cloud", Kontena::Cli::Cloud::Master::RemoveCommand
|
12
13
|
subcommand "add", "Register a master in Kontena Cloud", Kontena::Cli::Cloud::Master::AddCommand
|
13
|
-
subcommand "delete", "Delete a master in Kontena Cloud", Kontena::Cli::Cloud::Master::DeleteCommand
|
14
14
|
subcommand "show", "Show master settings in Kontena Cloud", Kontena::Cli::Cloud::Master::ShowCommand
|
15
15
|
subcommand "update", "Update master settings in Kontena Cloud", Kontena::Cli::Cloud::Master::UpdateCommand
|
16
16
|
|
data/lib/kontena/cli/common.rb
CHANGED
@@ -151,7 +151,7 @@ module Kontena
|
|
151
151
|
@kontena_account ||= config.find_account(ENV['KONTENA_ACCOUNT'] || 'kontena')
|
152
152
|
end
|
153
153
|
|
154
|
-
def
|
154
|
+
def cloud_auth?
|
155
155
|
return false unless kontena_account
|
156
156
|
return false unless kontena_account.token
|
157
157
|
return false unless kontena_account.token.access_token
|
@@ -289,33 +289,26 @@ module Kontena
|
|
289
289
|
return nil unless $stdout.tty?
|
290
290
|
start_time = Time.now.to_i
|
291
291
|
end_time = start_time + timeout
|
292
|
-
Thread.main['any_key.stop_prompt'] = false
|
293
292
|
Thread.main['any_key.timed_out'] = false
|
294
293
|
msg = "Press any key to continue or ctrl-c to cancel.. (Automatically continuing in ? seconds)"
|
294
|
+
|
295
|
+
reader_thread = Thread.new do
|
296
|
+
Thread.main['any_key.char'] = STDIN.getch
|
297
|
+
end
|
298
|
+
|
295
299
|
countdown_thread = Thread.new do
|
296
300
|
time_left = timeout
|
297
|
-
while time_left > 0 &&
|
298
|
-
print "\r#{pastel.
|
301
|
+
while time_left > 0 && Thread.main['any_key.char'].nil?
|
302
|
+
print "\r#{pastel.bright_white("#{msg.sub("?", time_left.to_s)}")} "
|
299
303
|
time_left = end_time - Time.now.to_i
|
300
304
|
sleep 0.1
|
301
305
|
end
|
302
306
|
print "\r#{' ' * msg.length} \r"
|
303
|
-
|
304
|
-
end
|
305
|
-
|
306
|
-
reader_thread = Thread.new do
|
307
|
-
Thread.main['any_key.char'] = STDIN.getch
|
308
|
-
Thread.main['any_key.stop_prompt'] = true
|
307
|
+
reader_thread.kill if reader_thread.alive?
|
309
308
|
end
|
310
309
|
|
311
310
|
countdown_thread.join
|
312
311
|
|
313
|
-
until Thread.main['any_key.char'] || Thread.main['any_key.timed_out']
|
314
|
-
sleep 0.1
|
315
|
-
end
|
316
|
-
|
317
|
-
reader_thread.kill if reader_thread.alive?
|
318
|
-
|
319
312
|
if Thread.main['any_key.char'] == "\u0003"
|
320
313
|
error "Canceled"
|
321
314
|
end
|
@@ -355,7 +348,7 @@ module Kontena
|
|
355
348
|
def display_master_login_info
|
356
349
|
server = config.current_master
|
357
350
|
if server
|
358
|
-
if server.token && server.access_token
|
351
|
+
if server.token && server.token.access_token
|
359
352
|
puts [
|
360
353
|
pastel.green('Authenticated to Kontena Master'),
|
361
354
|
pastel.yellow(server.name),
|
data/lib/kontena/cli/config.rb
CHANGED
@@ -25,6 +25,7 @@ module Kontena
|
|
25
25
|
class TokenExpiredError < StandardError; end
|
26
26
|
|
27
27
|
def initialize
|
28
|
+
super
|
28
29
|
@logger = Logger.new(STDOUT)
|
29
30
|
@logger.level = ENV["DEBUG"].nil? ? Logger::INFO : Logger::DEBUG
|
30
31
|
@logger.progname = 'CONFIG'
|
@@ -116,9 +117,9 @@ module Kontena
|
|
116
117
|
{
|
117
118
|
name: 'kontena',
|
118
119
|
url: 'https://cloud-api.kontena.io',
|
119
|
-
token_endpoint: 'https://
|
120
|
-
authorization_endpoint: 'https://cloud
|
121
|
-
userinfo_endpoint: 'https://
|
120
|
+
token_endpoint: 'https://cloud-api.kontena.io/oauth2/token',
|
121
|
+
authorization_endpoint: 'https://cloud.kontena.io/login/oauth/authorize',
|
122
|
+
userinfo_endpoint: 'https://cloud-api.kontena.io/user',
|
122
123
|
token_post_content_type: 'application/x-www-form-urlencoded',
|
123
124
|
code_requires_basic_auth: false,
|
124
125
|
token_method: 'post',
|
@@ -19,13 +19,13 @@ module Kontena::Cli::Grids
|
|
19
19
|
grid = find_grid_by_name(name_or_current)
|
20
20
|
exit_with_error("Grid not found") unless grid
|
21
21
|
|
22
|
+
grid_uri = self.current_master['url'].sub('http', 'ws')
|
23
|
+
|
24
|
+
|
22
25
|
prefix = export? ? 'export ' : ''
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
puts "#{prefix}KONTENA_URI=#{server['url'].sub('http', 'ws')}"
|
27
|
-
puts "#{prefix}KONTENA_TOKEN=#{server['token']}"
|
28
|
-
end
|
27
|
+
puts "#{prefix}KONTENA_URI=#{grid_uri}"
|
28
|
+
puts "#{prefix}KONTENA_TOKEN=#{grid['token']}"
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -17,7 +17,7 @@ module Kontena
|
|
17
17
|
attr_accessor :server, :success_response, :error_response, :port
|
18
18
|
|
19
19
|
DEFAULT_ERROR_MESSAGE = "Bad request"
|
20
|
-
SUCCESS_URL = "https://cloud
|
20
|
+
SUCCESS_URL = "https://cloud.kontena.io/terminal-success"
|
21
21
|
|
22
22
|
# Get new server instance
|
23
23
|
#
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Kontena::Cli::Master
|
2
|
+
class InitCloudCommand < Kontena::Command
|
3
|
+
|
4
|
+
include Kontena::Cli::Common
|
5
|
+
|
6
|
+
banner "Configures the current Kontena Master to use Kontena Cloud services and authentication"
|
7
|
+
|
8
|
+
option '--force', :flag, "Don't ask questions"
|
9
|
+
option '--cloud-master-id', '[ID]', "Use existing cloud master ID"
|
10
|
+
|
11
|
+
requires_current_master
|
12
|
+
requires_current_account_token
|
13
|
+
|
14
|
+
def execute
|
15
|
+
args = ["--current"]
|
16
|
+
args << "--force" if self.force?
|
17
|
+
args << "--cloud-master-id #{self.cloud_master_id}" if self.cloud_master_id
|
18
|
+
Kontena.run("cloud master add #{args.map(&:shellescape).join(' ')}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -17,6 +17,8 @@ module Kontena::Cli::Master
|
|
17
17
|
option ['-f', '--force'], :flag, 'Force reauthentication'
|
18
18
|
option ['-s', '--silent'], :flag, 'Reduce output verbosity'
|
19
19
|
|
20
|
+
option ['--no-login-info'], :flag, "Don't show login info", hidden: true
|
21
|
+
|
20
22
|
def execute
|
21
23
|
# rewrites self.url
|
22
24
|
use_current_master_if_available || use_master_by_name
|
@@ -28,14 +30,18 @@ module Kontena::Cli::Master
|
|
28
30
|
set_server_token(server)
|
29
31
|
|
30
32
|
# set server token by exchanging code if --code given
|
31
|
-
use_authorization_code(server) if self.code
|
33
|
+
use_authorization_code(server, self.code) if self.code
|
32
34
|
|
33
35
|
client = Kontena::Client.new(server.url, server.token)
|
34
36
|
|
35
37
|
# Unless an invitation code was supplied, check auth and exit
|
36
38
|
# if it works already.
|
37
39
|
unless self.join || self.force?
|
38
|
-
|
40
|
+
if auth_works?(server)
|
41
|
+
config.write
|
42
|
+
display_login_info(only: :master) unless self.no_login_info?
|
43
|
+
exit 0
|
44
|
+
end
|
39
45
|
end
|
40
46
|
|
41
47
|
# no local browser? tell user to launch an external one
|
@@ -50,22 +56,14 @@ module Kontena::Cli::Master
|
|
50
56
|
|
51
57
|
# If the master responds with a code, then exchange it to a token
|
52
58
|
if response['code']
|
53
|
-
|
54
|
-
|
55
|
-
unless response && response.kind_of?(Hash) && response['access_token']
|
56
|
-
exit_with_error "Code exchange failed"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
if response['access_token']
|
59
|
+
use_authorization_code(server, response['code'])
|
60
|
+
elsif response['access_token']
|
62
61
|
update_server_token(server, response)
|
63
62
|
update_server_name(server, response)
|
64
63
|
config.current_server = server.name
|
65
|
-
config.write
|
66
|
-
display_login_info(only: :master) unless running_silent?
|
67
|
-
exit 0
|
68
64
|
end
|
65
|
+
config.write
|
66
|
+
display_login_info(only: :master) unless (running_silent? || self.no_login_info?)
|
69
67
|
end
|
70
68
|
|
71
69
|
def master_account
|
@@ -116,40 +114,41 @@ module Kontena::Cli::Master
|
|
116
114
|
end
|
117
115
|
end
|
118
116
|
|
119
|
-
def use_authorization_code(server)
|
120
|
-
vspinner "Exchanging authorization code for an access token from Master" do
|
117
|
+
def use_authorization_code(server, code)
|
118
|
+
vspinner "Exchanging authorization code for an access token from Kontena Master" do
|
121
119
|
client = Kontena::Client.new(server.url, server.token)
|
122
|
-
response = client.exchange_code(
|
120
|
+
response = client.exchange_code(code) rescue nil
|
123
121
|
|
124
122
|
if response && response.kind_of?(Hash) && !response.has_key?('error')
|
125
|
-
server.token.access_token = response['access_token']
|
126
|
-
server.token.refresh_token = response['refresh_token']
|
127
|
-
server.token.expires_at = response['expires_in'].to_i > 0 ? Time.now.utc.to_i + response['expires_in'].to_i : nil
|
128
|
-
server.token.username = response['user']['name'] || response['user']['email']
|
129
|
-
server.username = server.token.username
|
130
123
|
if response['server'] && response['server']['name']
|
131
124
|
server.name ||= response['server']['name']
|
125
|
+
server.username = response['user']['name'] || response['user']['email']
|
132
126
|
config.current_server = server.name
|
133
127
|
end
|
128
|
+
|
129
|
+
server.token = Kontena::Cli::Config::Token.new(
|
130
|
+
access_token: response['access_token'],
|
131
|
+
refresh_token: response['refresh_token'],
|
132
|
+
expires_at: response['expires_in'].to_i > 0 ? Time.now.utc.to_i + response['expires_in'].to_i : nil,
|
133
|
+
)
|
134
134
|
else
|
135
135
|
raise Kontena::Errors::StandardError.new(500, 'Code exchange failed')
|
136
136
|
end
|
137
137
|
end
|
138
|
+
true
|
138
139
|
end
|
139
140
|
|
140
|
-
def
|
141
|
+
def auth_works?(server)
|
141
142
|
if server && server.token && server.token.access_token
|
142
143
|
# See if the existing or supplied authentication works without reauthenticating
|
143
144
|
auth_ok = false
|
144
145
|
vspinner "Testing if authentication works using current access token" do
|
145
146
|
auth_ok = Kontena::Client.new(server.url, server.token).authentication_ok?(master_account.userinfo_endpoint)
|
146
|
-
end
|
147
|
-
if auth_ok
|
148
147
|
config.current_master = server.name
|
149
|
-
config.write
|
150
|
-
display_login_info(only: :master) unless running_silent?
|
151
|
-
exit 0
|
152
148
|
end
|
149
|
+
auth_ok
|
150
|
+
else
|
151
|
+
false
|
153
152
|
end
|
154
153
|
end
|
155
154
|
|
@@ -225,7 +224,7 @@ module Kontena::Cli::Master
|
|
225
224
|
any_key_to_continue(10)
|
226
225
|
|
227
226
|
puts "If the browser does not open, try visiting this URL manually:"
|
228
|
-
puts "
|
227
|
+
puts "#{uri.to_s}"
|
229
228
|
puts
|
230
229
|
|
231
230
|
server_thread = Thread.new { Thread.main['response'] = web_server.serve_one }
|