kontena-cli 0.15.5 → 0.16.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +0 -3
- data/Gemfile +3 -0
- data/LOGO +8 -0
- data/VERSION +1 -1
- data/kontena-cli.gemspec +2 -3
- data/lib/kontena/callback.rb +57 -0
- data/lib/kontena/callbacks/.gitkeep +0 -0
- data/lib/kontena/callbacks/auth/01_list_and_select_grid_after_master_auth.rb +27 -0
- data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +20 -0
- data/lib/kontena/callbacks/master/deploy/01_show_logo_before_deploy.rb +15 -0
- data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +124 -0
- data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +53 -0
- data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +32 -0
- data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +49 -0
- data/lib/kontena/callbacks/master/deploy/90_suggest_inviting_yourself_after_deploy.rb +24 -0
- data/lib/kontena/cli/app_command.rb +2 -1
- data/lib/kontena/cli/apps/build_command.rb +1 -1
- data/lib/kontena/cli/apps/common.rb +6 -1
- data/lib/kontena/cli/apps/config_command.rb +1 -1
- data/lib/kontena/cli/apps/deploy_command.rb +1 -1
- data/lib/kontena/cli/apps/init_command.rb +3 -5
- 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 +1 -1
- data/lib/kontena/cli/apps/remove_command.rb +2 -3
- 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/show_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/reader.rb +3 -13
- data/lib/kontena/cli/apps/yaml/validator.rb +0 -4
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +1 -5
- data/lib/kontena/cli/certificate/authorize_command.rb +1 -1
- data/lib/kontena/cli/certificate/get_command.rb +1 -1
- data/lib/kontena/cli/certificate/register_command.rb +1 -1
- data/lib/kontena/cli/certificate_command.rb +1 -1
- data/lib/kontena/cli/cloud/login_command.rb +128 -0
- data/lib/kontena/cli/cloud/master/add_command.rb +54 -0
- data/lib/kontena/cli/cloud/master/delete_command.rb +20 -0
- data/lib/kontena/cli/cloud/master/list_command.rb +29 -0
- data/lib/kontena/cli/cloud/master/show_command.rb +23 -0
- data/lib/kontena/cli/cloud/master/update_command.rb +58 -0
- data/lib/kontena/cli/cloud/master_command.rb +21 -0
- data/lib/kontena/cli/cloud_command.rb +10 -0
- data/lib/kontena/cli/common.rb +230 -88
- data/lib/kontena/cli/config.rb +537 -0
- data/lib/kontena/cli/container_command.rb +1 -1
- data/lib/kontena/cli/containers/exec_command.rb +1 -1
- data/lib/kontena/cli/containers/inspect_command.rb +1 -1
- data/lib/kontena/cli/etcd/get_command.rb +1 -1
- data/lib/kontena/cli/etcd/list_command.rb +1 -1
- data/lib/kontena/cli/etcd/mkdir_command.rb +1 -1
- data/lib/kontena/cli/etcd/remove_command.rb +1 -1
- data/lib/kontena/cli/etcd/set_command.rb +1 -1
- data/lib/kontena/cli/etcd_command.rb +1 -1
- data/lib/kontena/cli/external_registries/add_command.rb +1 -1
- data/lib/kontena/cli/external_registries/delete_command.rb +1 -1
- data/lib/kontena/cli/external_registries/list_command.rb +1 -1
- data/lib/kontena/cli/external_registries/remove_command.rb +1 -1
- data/lib/kontena/cli/external_registry_command.rb +1 -1
- data/lib/kontena/cli/grid_command.rb +1 -1
- data/lib/kontena/cli/grids/audit_log_command.rb +6 -5
- data/lib/kontena/cli/grids/cloud_config_command.rb +1 -1
- data/lib/kontena/cli/grids/common.rb +1 -1
- data/lib/kontena/cli/grids/create_command.rb +8 -4
- data/lib/kontena/cli/grids/current_command.rb +1 -1
- data/lib/kontena/cli/grids/env_command.rb +1 -1
- data/lib/kontena/cli/grids/list_command.rb +35 -10
- data/lib/kontena/cli/grids/logs_command.rb +1 -1
- data/lib/kontena/cli/grids/remove_command.rb +2 -2
- data/lib/kontena/cli/grids/show_command.rb +1 -1
- data/lib/kontena/cli/grids/trusted_subnet_command.rb +1 -1
- data/lib/kontena/cli/grids/trusted_subnets/add_command.rb +1 -1
- data/lib/kontena/cli/grids/trusted_subnets/list_command.rb +1 -1
- data/lib/kontena/cli/grids/trusted_subnets/remove_command.rb +1 -1
- data/lib/kontena/cli/grids/update_command.rb +1 -1
- data/lib/kontena/cli/grids/use_command.rb +11 -6
- data/lib/kontena/cli/grids/user_command.rb +1 -1
- data/lib/kontena/cli/grids/users/add_command.rb +1 -1
- data/lib/kontena/cli/grids/users/list_command.rb +1 -1
- data/lib/kontena/cli/grids/users/remove_command.rb +1 -1
- data/lib/kontena/cli/localhost_web_server.rb +93 -0
- data/lib/kontena/cli/login_command.rb +5 -118
- data/lib/kontena/cli/logout_command.rb +33 -2
- data/lib/kontena/cli/master/audit_log_command.rb +19 -0
- data/lib/kontena/cli/master/config/export_command.rb +47 -0
- data/lib/kontena/cli/master/config/get_command.rb +24 -0
- data/lib/kontena/cli/master/config/import_command.rb +69 -0
- data/lib/kontena/cli/master/config/set_command.rb +19 -0
- data/lib/kontena/cli/master/config/unset_command.rb +20 -0
- data/lib/kontena/cli/master/config_command.rb +24 -0
- data/lib/kontena/cli/master/create_command.rb +76 -0
- data/lib/kontena/cli/master/current_command.rb +10 -2
- data/lib/kontena/cli/master/join_command.rb +20 -0
- data/lib/kontena/cli/master/list_command.rb +4 -4
- data/lib/kontena/cli/master/login_command.rb +274 -0
- data/lib/kontena/cli/master/use_command.rb +8 -19
- data/lib/kontena/cli/master/users/invite_command.rb +33 -6
- data/lib/kontena/cli/master/users/list_command.rb +2 -2
- data/lib/kontena/cli/master/users/remove_command.rb +1 -1
- data/lib/kontena/cli/master/users/role_command.rb +1 -1
- data/lib/kontena/cli/master/users/roles/add_command.rb +18 -16
- data/lib/kontena/cli/master/users/roles/remove_command.rb +1 -1
- data/lib/kontena/cli/master/users_command.rb +1 -1
- data/lib/kontena/cli/master_command.rb +21 -1
- data/lib/kontena/cli/node_command.rb +1 -1
- data/lib/kontena/cli/nodes/label_command.rb +1 -1
- data/lib/kontena/cli/nodes/labels/add_command.rb +1 -1
- data/lib/kontena/cli/nodes/labels/remove_command.rb +1 -1
- data/lib/kontena/cli/nodes/list_command.rb +1 -1
- data/lib/kontena/cli/nodes/remove_command.rb +1 -1
- data/lib/kontena/cli/nodes/show_command.rb +1 -1
- data/lib/kontena/cli/nodes/ssh_command.rb +1 -1
- data/lib/kontena/cli/nodes/update_command.rb +1 -1
- data/lib/kontena/cli/plugin_command.rb +1 -1
- data/lib/kontena/cli/plugins/install_command.rb +2 -2
- data/lib/kontena/cli/plugins/list_command.rb +2 -2
- data/lib/kontena/cli/plugins/search_command.rb +1 -1
- data/lib/kontena/cli/plugins/uninstall_command.rb +2 -2
- data/lib/kontena/cli/registry/create_command.rb +2 -4
- data/lib/kontena/cli/registry/delete_command.rb +1 -1
- data/lib/kontena/cli/registry/remove_command.rb +1 -1
- data/lib/kontena/cli/registry_command.rb +1 -1
- data/lib/kontena/cli/service_command.rb +1 -1
- data/lib/kontena/cli/services/container_command.rb +1 -1
- data/lib/kontena/cli/services/containers_command.rb +1 -1
- data/lib/kontena/cli/services/create_command.rb +1 -1
- data/lib/kontena/cli/services/delete_command.rb +1 -1
- data/lib/kontena/cli/services/deploy_command.rb +1 -1
- data/lib/kontena/cli/services/env_command.rb +1 -1
- data/lib/kontena/cli/services/envs/add_command.rb +1 -1
- data/lib/kontena/cli/services/envs/list_command.rb +1 -1
- data/lib/kontena/cli/services/envs/remove_command.rb +1 -1
- data/lib/kontena/cli/services/link_command.rb +1 -1
- data/lib/kontena/cli/services/list_command.rb +1 -1
- data/lib/kontena/cli/services/logs_command.rb +1 -1
- data/lib/kontena/cli/services/monitor_command.rb +1 -1
- data/lib/kontena/cli/services/remove_command.rb +1 -1
- data/lib/kontena/cli/services/restart_command.rb +1 -1
- data/lib/kontena/cli/services/scale_command.rb +1 -1
- data/lib/kontena/cli/services/secret_command.rb +1 -1
- data/lib/kontena/cli/services/secrets/link_command.rb +1 -1
- data/lib/kontena/cli/services/secrets/unlink_command.rb +1 -1
- data/lib/kontena/cli/services/services_helper.rb +6 -3
- data/lib/kontena/cli/services/show_command.rb +1 -1
- data/lib/kontena/cli/services/start_command.rb +1 -1
- data/lib/kontena/cli/services/stats_command.rb +1 -1
- data/lib/kontena/cli/services/stop_command.rb +1 -1
- data/lib/kontena/cli/services/unlink_command.rb +1 -1
- data/lib/kontena/cli/services/update_command.rb +1 -1
- data/lib/kontena/cli/spinner.rb +122 -0
- data/lib/kontena/cli/stack_command.rb +1 -1
- data/lib/kontena/cli/stacks/create_command.rb +1 -1
- data/lib/kontena/cli/stacks/deploy_command.rb +1 -1
- data/lib/kontena/cli/stacks/list_command.rb +1 -1
- data/lib/kontena/cli/stacks/remove_command.rb +1 -1
- data/lib/kontena/cli/stacks/show_command.rb +1 -1
- data/lib/kontena/cli/stacks/update_command.rb +1 -1
- data/lib/kontena/cli/vault/list_command.rb +1 -1
- data/lib/kontena/cli/vault/read_command.rb +1 -1
- data/lib/kontena/cli/vault/remove_command.rb +1 -1
- data/lib/kontena/cli/vault/update_command.rb +1 -1
- data/lib/kontena/cli/vault/write_command.rb +1 -1
- data/lib/kontena/cli/vault_command.rb +1 -1
- data/lib/kontena/cli/version.rb +1 -1
- data/lib/kontena/cli/version_command.rb +1 -1
- data/lib/kontena/cli/vpn/config_command.rb +1 -1
- data/lib/kontena/cli/vpn/create_command.rb +2 -4
- data/lib/kontena/cli/vpn/delete_command.rb +1 -1
- data/lib/kontena/cli/vpn/remove_command.rb +1 -1
- data/lib/kontena/cli/vpn_command.rb +1 -1
- data/lib/kontena/cli/whoami_command.rb +16 -13
- data/lib/kontena/client.rb +410 -90
- data/lib/kontena/command.rb +172 -0
- data/lib/kontena/main_command.rb +7 -8
- data/lib/kontena/presets/github_auth_provider.yml +11 -0
- data/lib/kontena/presets/kontena_auth_provider.yml +11 -0
- data/lib/kontena_cli.rb +51 -1
- data/spec/kontena/cli/app/deploy_command_spec.rb +14 -44
- data/spec/kontena/cli/app/scale_spec.rb +1 -1
- data/spec/kontena/cli/app/yaml/reader_spec.rb +0 -48
- data/spec/kontena/cli/common_spec.rb +63 -59
- data/spec/kontena/cli/grids/use_command_spec.rb +43 -0
- data/spec/kontena/cli/master/current_command_spec.rb +3 -24
- data/spec/kontena/cli/master/use_command_spec.rb +2 -27
- data/spec/kontena/cli/master/users/invite_command_spec.rb +4 -18
- data/spec/kontena/cli/master/users/roles/add_command_spec.rb +2 -16
- data/spec/kontena/cli/master/users/roles/remove_command_spec.rb +2 -13
- data/spec/kontena/cli/services/restart_command_spec.rb +1 -1
- data/spec/kontena/cli/services/update_command_spec.rb +5 -5
- data/spec/kontena/client_spec.rb +104 -35
- data/spec/kontena/config_spec.rb +65 -0
- data/spec/spec_helper.rb +25 -3
- data/spec/support/client_helpers.rb +10 -3
- data/spec/support/requirements_helper.rb +32 -0
- metadata +61 -48
- data/lib/kontena/cli/register_command.rb +0 -23
- data/lib/kontena/cli/user/forgot_password_command.rb +0 -16
- data/lib/kontena/cli/user/reset_password_command.rb +0 -23
- data/lib/kontena/cli/user/verify_command.rb +0 -20
- data/lib/kontena/cli/user_command.rb +0 -13
- data/spec/fixtures/kontena-malformed-yaml.yml +0 -6
- data/spec/fixtures/kontena-not-hash-service-config.yml +0 -3
- data/spec/kontena/cli/login_command_spec.rb +0 -32
- data/spec/kontena/cli/register_command_spec.rb +0 -57
@@ -0,0 +1,20 @@
|
|
1
|
+
module Kontena::Cli::Master::Config
|
2
|
+
class UnsetCommand < Kontena::Command
|
3
|
+
|
4
|
+
include Kontena::Cli::Common
|
5
|
+
|
6
|
+
requires_current_master
|
7
|
+
requires_current_master_token
|
8
|
+
|
9
|
+
parameter "KEY ...", "Key(s) to unset", required: true
|
10
|
+
|
11
|
+
banner "Clears a configuration value from Master"
|
12
|
+
|
13
|
+
def execute
|
14
|
+
self.key_list.each do |key|
|
15
|
+
client.delete("config/#{key}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'config/set_command'
|
2
|
+
require_relative 'config/get_command'
|
3
|
+
require_relative 'config/unset_command'
|
4
|
+
require_relative 'config/export_command'
|
5
|
+
require_relative 'config/import_command'
|
6
|
+
|
7
|
+
module Kontena
|
8
|
+
module Cli
|
9
|
+
module Master
|
10
|
+
class ConfigCommand < Kontena::Command
|
11
|
+
|
12
|
+
subcommand "set", "Set a config value", Kontena::Cli::Master::Config::SetCommand
|
13
|
+
subcommand "get", "Get a config value", Kontena::Cli::Master::Config::GetCommand
|
14
|
+
subcommand "unset", "Clear a config value", Kontena::Cli::Master::Config::UnsetCommand
|
15
|
+
subcommand ["load", "import"], "Upload config to Master", Kontena::Cli::Master::Config::ImportCommand
|
16
|
+
subcommand ["dump", "export"], "Download config from Master", Kontena::Cli::Master::Config::ExportCommand
|
17
|
+
|
18
|
+
def execute
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Kontena::Cli::Master
|
2
|
+
class CreateCommand < Kontena::Command
|
3
|
+
include Kontena::Cli::Common
|
4
|
+
|
5
|
+
callback_matcher :master, :create_with_plugin_select
|
6
|
+
|
7
|
+
def subcommand_tree(command = nil)
|
8
|
+
command ||= Kontena::MainCommand
|
9
|
+
|
10
|
+
real_command = command.respond_to?(:subcommand_class) ? command.subcommand_class : command
|
11
|
+
|
12
|
+
tree = {}
|
13
|
+
real_command.recognised_subcommands.each do |sub_command|
|
14
|
+
sub_command.names.each do |command_name|
|
15
|
+
if sub_command.subcommand_class.has_subcommands?
|
16
|
+
tree[command_name] = subcommand_tree(sub_command)
|
17
|
+
else
|
18
|
+
tree[command_name] = sub_command.subcommand_class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
tree
|
23
|
+
end
|
24
|
+
|
25
|
+
def master_create_subcommands(tree)
|
26
|
+
creators = []
|
27
|
+
tree.each do |k,cmd|
|
28
|
+
if cmd.kind_of?(Hash)
|
29
|
+
creators += master_create_subcommands(cmd)
|
30
|
+
elsif cmd.respond_to?(:callback_matcher) && cmd.callback_matcher == [:master, :create]
|
31
|
+
creators << cmd
|
32
|
+
end
|
33
|
+
end
|
34
|
+
creators
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute
|
38
|
+
|
39
|
+
require 'shellwords'
|
40
|
+
|
41
|
+
tree = master_create_subcommands(subcommand_tree)
|
42
|
+
|
43
|
+
if tree.empty?
|
44
|
+
abort "Install platform plugins first, use: kontena plugin"
|
45
|
+
end
|
46
|
+
cmd_class = prompt.select("Choose target platform") do |menu|
|
47
|
+
tree.each do |cmd_class|
|
48
|
+
plugin_name = cmd_class.to_s[/Plugin::(.+?)::/, 1]
|
49
|
+
next unless plugin_name
|
50
|
+
menu.choice plugin_name, cmd_class
|
51
|
+
end
|
52
|
+
end
|
53
|
+
skip_options = ['--no-prompt', '--silent', '--help', '--version']
|
54
|
+
options = []
|
55
|
+
cmd_class.recognised_options.each do |option|
|
56
|
+
next if option.switches.any?{ |sw| skip_options.include?(sw) }
|
57
|
+
if option.type == :flag
|
58
|
+
answer = prompt.yes?(option.description)
|
59
|
+
if answer
|
60
|
+
options << option.switches.first
|
61
|
+
end
|
62
|
+
else
|
63
|
+
answer = prompt.ask("#{option.description}: ", required: option.required?, default: option.default_value)
|
64
|
+
if answer
|
65
|
+
options << "#{option.switches.first} #{answer.shellescape}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
command = cmd_class.to_s[/Plugin::(.+?)::/, 1].downcase
|
70
|
+
command << " master create #{options.join(" ")}"
|
71
|
+
Kontena.run(command)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
@@ -1,14 +1,22 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module Kontena::Cli::Master
|
2
|
-
class CurrentCommand <
|
4
|
+
class CurrentCommand < Kontena::Command
|
3
5
|
include Kontena::Cli::Common
|
4
6
|
|
5
7
|
option ["--name"], :flag, "Show name only", default: false
|
8
|
+
option ["--address"], :flag, "Show IP address or FQDN only", default: false
|
9
|
+
option ["--url"], :flag, "Show URL only", default: false
|
6
10
|
|
7
11
|
def execute
|
8
|
-
master =
|
12
|
+
master = require_current_master
|
9
13
|
|
10
14
|
if name?
|
11
15
|
puts master['name']
|
16
|
+
elsif address?
|
17
|
+
puts URI.parse(master['url']).host
|
18
|
+
elsif url?
|
19
|
+
puts master['url']
|
12
20
|
else
|
13
21
|
puts "#{master['name']} #{master['url']}"
|
14
22
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Kontena::Cli::Master
|
2
|
+
class JoinCommand < Kontena::Command
|
3
|
+
parameter "URL", "Kontena Master URL or name"
|
4
|
+
parameter "INVITE_CODE", "Invitation code"
|
5
|
+
|
6
|
+
option ['-n', '--name'], '[NAME]', 'Set server name'
|
7
|
+
option ['-r', '--remote'], :flag, 'Do not try to open a browser'
|
8
|
+
option ['-v', '--verbose'], :flag, 'Increase output verbosity'
|
9
|
+
|
10
|
+
def execute
|
11
|
+
params = []
|
12
|
+
params << "--join #{self.invite_code.shellescape}"
|
13
|
+
params << "--remote" if self.remote?
|
14
|
+
params << "--name #{self.name.shellescape}" if self.name
|
15
|
+
params << "--verbose" if self.verbose?
|
16
|
+
|
17
|
+
Kontena.run("master auth #{params.join(' ')} #{self.url.shellescape}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Kontena::Cli::Master
|
2
|
-
class ListCommand <
|
2
|
+
class ListCommand < Kontena::Command
|
3
3
|
include Kontena::Cli::Common
|
4
4
|
|
5
5
|
def execute
|
6
6
|
puts '%-24s %-30s' % ['Name', 'Url']
|
7
|
-
|
8
|
-
|
9
|
-
if server['name'] ==
|
7
|
+
current_master = config.current_master
|
8
|
+
config.servers.each do |server|
|
9
|
+
if server['name'] == current_master.name
|
10
10
|
name = "* #{server['name']}"
|
11
11
|
else
|
12
12
|
name = server['name']
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Kontena::Cli::Master
|
4
|
+
class LoginCommand < Kontena::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
|
7
|
+
parameter "[URL]", "Kontena Master URL or name"
|
8
|
+
option ['-j', '--join'], '[INVITE_CODE]', "Join master using an invitation code"
|
9
|
+
option ['-t', '--token'], '[TOKEN]', 'Use a pre-generated access token'
|
10
|
+
option ['-n', '--name'], '[NAME]', 'Set server name'
|
11
|
+
option ['-c', '--code'], '[CODE]', 'Use authorization code generated during master install'
|
12
|
+
option ['-r', '--remote'], :flag, 'Do not try to open a browser'
|
13
|
+
option ['-e', '--expires-in'], '[SECONDS]', 'Request token with expiration of X seconds. Use 0 to never expire', default: 7200
|
14
|
+
option ['-v', '--verbose'], :flag, 'Increase output verbosity'
|
15
|
+
option ['-f', '--force'], :flag, 'Force reauthentication'
|
16
|
+
option ['-s', '--silent'], :flag, 'Reduce output verbosity'
|
17
|
+
|
18
|
+
def execute
|
19
|
+
# rewrites self.url
|
20
|
+
use_current_master_if_available || use_master_by_name
|
21
|
+
|
22
|
+
# find server by url or create a new one
|
23
|
+
server = find_server_or_create_new(url)
|
24
|
+
|
25
|
+
# set server token from self.token or create a new one
|
26
|
+
set_server_token(server)
|
27
|
+
|
28
|
+
# set server token by exchanging code if --code given
|
29
|
+
use_authorization_code(server) if self.code
|
30
|
+
|
31
|
+
client = Kontena::Client.new(server.url, server.token)
|
32
|
+
|
33
|
+
# Unless an invitation code was supplied, check auth and exit
|
34
|
+
# if it works already.
|
35
|
+
unless self.join || self.force?
|
36
|
+
exit_if_auth_works(server)
|
37
|
+
end
|
38
|
+
|
39
|
+
# no local browser? tell user to launch an external one
|
40
|
+
if self.remote?
|
41
|
+
config.current_server = server.name
|
42
|
+
config.write
|
43
|
+
display_remote_message_and_exit(get_authorization_url)
|
44
|
+
end
|
45
|
+
|
46
|
+
# local web flow
|
47
|
+
response = response_from_web_flow
|
48
|
+
|
49
|
+
# If the master responds with a code, then exchange it to a token
|
50
|
+
if response['code']
|
51
|
+
vspinner "Exchanging authorization code for an access token from Kontena Master" do
|
52
|
+
response = client.exchange_code(response['code'])
|
53
|
+
unless response && response.kind_of?(Hash) && response['access_token']
|
54
|
+
abort "Code exchange failed"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if response['access_token']
|
60
|
+
update_server_token(server, response)
|
61
|
+
update_server_name(server, response)
|
62
|
+
config.current_server = server.name
|
63
|
+
config.write
|
64
|
+
display_login_info(only: :master) unless running_silent?
|
65
|
+
exit 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def master_account
|
70
|
+
@master_account ||= config.find_account('master')
|
71
|
+
end
|
72
|
+
|
73
|
+
def use_current_master_if_available
|
74
|
+
return nil if self.url
|
75
|
+
if config.current_master
|
76
|
+
self.url = config.current_master.url
|
77
|
+
true
|
78
|
+
else
|
79
|
+
abort "Current master is not set and URL was not provided."
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def use_master_by_name
|
84
|
+
return if self.url =~ /^(?:http|https):\/\//
|
85
|
+
server = config.find_server(self.url)
|
86
|
+
if server && server.url
|
87
|
+
self.url = server.url
|
88
|
+
true
|
89
|
+
else
|
90
|
+
abort "Server '#{self.url}' not found in configuration."
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def find_server_or_create_new(url)
|
95
|
+
existing_server = config.find_server_by(url: url)
|
96
|
+
if existing_server && (!self.name || existing_server.name == self.name)
|
97
|
+
config.current_server = existing_server.name
|
98
|
+
existing_server
|
99
|
+
else
|
100
|
+
new_server = Kontena::Cli::Config::Server.new(url: self.url, name: self.name)
|
101
|
+
config.servers << new_server
|
102
|
+
config.current_server = new_server.name
|
103
|
+
new_server
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_server_token(server)
|
108
|
+
if self.token
|
109
|
+
# Use supplied token
|
110
|
+
server.token = Kontena::Cli::Config::Token.new(access_token: self.token, parent_type: :master, parent_name: server.name)
|
111
|
+
elsif server.token.nil?
|
112
|
+
# Create new empty token if the server does not have one yet
|
113
|
+
server.token = Kontena::Cli::Config::Token.new(parent_type: :master, parent_name: server.name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def use_authorization_code(server)
|
118
|
+
vspinner "Exchanging authorization code for an access token from Master" do
|
119
|
+
client = Kontena::Client.new(server.url, server.token)
|
120
|
+
response = client.exchange_code(self.code)
|
121
|
+
|
122
|
+
if response && response.kind_of?(Hash) && !response.has_key?('error')
|
123
|
+
server.token.access_token = response['access_token']
|
124
|
+
server.token.refresh_token = response['refresh_token']
|
125
|
+
server.token.expires_at = response['expires_in'].to_i > 0 ? Time.now.utc.to_i + response['expires_in'].to_i : nil
|
126
|
+
server.token.username = response['user']['name'] || response['user']['email']
|
127
|
+
server.username = server.token.username
|
128
|
+
if response['server'] && response['server']['name']
|
129
|
+
server.name ||= response['server']['name']
|
130
|
+
config.current_server = server.name
|
131
|
+
end
|
132
|
+
else
|
133
|
+
raise Kontena::Errors::StandardError.new(500, 'Code exchange failed')
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def exit_if_auth_works(server)
|
139
|
+
if server && server.token && server.token.access_token
|
140
|
+
# See if the existing or supplied authentication works without reauthenticating
|
141
|
+
auth_ok = false
|
142
|
+
vspinner "Testing if authentication works using current access token" do
|
143
|
+
auth_ok = Kontena::Client.new(server.url, server.token).authentication_ok?(master_account.userinfo_endpoint)
|
144
|
+
end
|
145
|
+
if auth_ok
|
146
|
+
config.current_master = server.name
|
147
|
+
config.write
|
148
|
+
display_login_info(only: :master) unless running_silent?
|
149
|
+
exit 0
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def build_auth_url_path(port = nil)
|
155
|
+
auth_url_params = {}
|
156
|
+
if self.remote?
|
157
|
+
auth_url_params[:redirect_uri] = "/code"
|
158
|
+
else
|
159
|
+
auth_url_params[:redirect_uri] = "http://localhost:#{port}/cb"
|
160
|
+
end
|
161
|
+
auth_url_params[:invite_code] = self.join if self.join
|
162
|
+
auth_url_params[:expires_in] = self.expires_in if self.expires_in
|
163
|
+
"/authenticate?#{URI.encode_www_form(auth_url_params)}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def get_authorization_url(web_server_port = nil)
|
167
|
+
authorization_url = nil
|
168
|
+
vspinner "Sending authentication request to receive an authorization URL" do
|
169
|
+
client.request(
|
170
|
+
http_method: :get,
|
171
|
+
path: build_auth_url_path(web_server_port),
|
172
|
+
expects: [501, 400, 302, 403],
|
173
|
+
auth: false
|
174
|
+
)
|
175
|
+
|
176
|
+
case client.last_response.status
|
177
|
+
when 302
|
178
|
+
authorization_url = client.last_response.headers['Location']
|
179
|
+
when 501
|
180
|
+
abort "Authentication provider not configured"
|
181
|
+
when 403
|
182
|
+
abort "Invalid invitation code"
|
183
|
+
else
|
184
|
+
abort "Invalid response to authentication request"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
authorization_url
|
188
|
+
end
|
189
|
+
|
190
|
+
def display_remote_message_and_exit(url)
|
191
|
+
if running_silent?
|
192
|
+
sputs url
|
193
|
+
else
|
194
|
+
puts "Visit this URL in a browser:"
|
195
|
+
puts "<#{url}>"
|
196
|
+
puts
|
197
|
+
puts "Then complete the authentication by using:"
|
198
|
+
puts "kontena master login --code <CODE FROM BROWSER>"
|
199
|
+
# Using exit code 1 because the operation isn't complete,
|
200
|
+
# you can't do something like:
|
201
|
+
# kontena master login --remote && echo "yes"
|
202
|
+
end
|
203
|
+
exit 1
|
204
|
+
end
|
205
|
+
|
206
|
+
def response_from_web_flow
|
207
|
+
require_relative '../localhost_web_server'
|
208
|
+
require 'launchy'
|
209
|
+
|
210
|
+
web_server = Kontena::LocalhostWebServer.new
|
211
|
+
uri = URI.parse(get_authorization_url(web_server.port))
|
212
|
+
puts "Opening browser to #{uri.scheme}://#{uri.host}"
|
213
|
+
puts
|
214
|
+
puts "If you are running this command over an ssh connection or it's"
|
215
|
+
puts "otherwise not possible to open a browser from this terminal"
|
216
|
+
puts "then you must use the --remote flag or use a pregenerated"
|
217
|
+
puts "access token using the --token option."
|
218
|
+
puts
|
219
|
+
puts "Once the authentication is complete you can close the browser"
|
220
|
+
puts "window or tab and return to this window to continue."
|
221
|
+
puts
|
222
|
+
|
223
|
+
any_key_to_continue
|
224
|
+
|
225
|
+
puts "If the browser does not open, try visiting this URL manually:"
|
226
|
+
puts "<#{uri.to_s}>"
|
227
|
+
puts
|
228
|
+
|
229
|
+
server_thread = Thread.new { Thread.main['response'] = web_server.serve_one }
|
230
|
+
browser_thread = Thread.new { Launchy.open(uri.to_s) }
|
231
|
+
|
232
|
+
vspinner "Waiting for browser authorization response" do
|
233
|
+
server_thread.join
|
234
|
+
end
|
235
|
+
browser_thread.join
|
236
|
+
|
237
|
+
Thread.main['response']
|
238
|
+
end
|
239
|
+
|
240
|
+
def in_to_at(expires_in)
|
241
|
+
if expires_in.to_i > 0
|
242
|
+
Time.now.utc.to_i + expires_in.to_i
|
243
|
+
else
|
244
|
+
nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def update_server_token(server, response)
|
249
|
+
server.token = Kontena::Cli::Config::Token.new
|
250
|
+
server.token.access_token = response['access_token']
|
251
|
+
server.token.refresh_token = response['refresh_token']
|
252
|
+
server.token.expires_at = in_to_at(response['expires_in'])
|
253
|
+
server.token.username = response.fetch('user', {}).fetch('name', nil) || response.fetch('user', {}).fetch('email', nil)
|
254
|
+
server.username = server.token.username
|
255
|
+
end
|
256
|
+
|
257
|
+
def update_server_name(server, response)
|
258
|
+
return unless server.name.nil?
|
259
|
+
|
260
|
+
if self.name
|
261
|
+
server.name = self.name
|
262
|
+
elsif response['server'] && response['server']['name']
|
263
|
+
server.name = response['server']['name']
|
264
|
+
elsif config.find_server('default')
|
265
|
+
server.name = "default-#{SecureRandom.hex(2)}"
|
266
|
+
else
|
267
|
+
server.name = "default"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|