krates 1.6.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.
- checksums.yaml +7 -0
- data/LICENSE +212 -0
- data/LOGO +10 -0
- data/VERSION +1 -0
- data/bin/krates +23 -0
- data/lib/kontena/autoload_core.rb +19 -0
- data/lib/kontena/callback.rb +60 -0
- data/lib/kontena/callbacks/.gitkeep +0 -0
- data/lib/kontena/callbacks/auth/01_list_and_select_grid_after_master_auth.rb +26 -0
- data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +19 -0
- data/lib/kontena/callbacks/master/deploy/01_show_logo_before_deploy.rb +14 -0
- data/lib/kontena/callbacks/master/deploy/04_default_master_version.rb +18 -0
- data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +105 -0
- data/lib/kontena/callbacks/master/deploy/40_install_ssl_certificate_after_deploy.rb +32 -0
- data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +66 -0
- data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +21 -0
- data/lib/kontena/callbacks/master/deploy/56_set_server_provider_after_deploy.rb +24 -0
- data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +31 -0
- data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +95 -0
- data/lib/kontena/callbacks/master/deploy/90_proptip_after_deploy.rb +33 -0
- data/lib/kontena/cli/browser_launcher.rb +61 -0
- data/lib/kontena/cli/bytes_helper.rb +40 -0
- data/lib/kontena/cli/certificate/authorize_command.rb +107 -0
- data/lib/kontena/cli/certificate/common.rb +16 -0
- data/lib/kontena/cli/certificate/domain_authorization/list_command.rb +24 -0
- data/lib/kontena/cli/certificate/domain_authorization/remove_authorization_command.rb +25 -0
- data/lib/kontena/cli/certificate/domain_authorize_command.rb +7 -0
- data/lib/kontena/cli/certificate/export_command.rb +28 -0
- data/lib/kontena/cli/certificate/get_command.rb +33 -0
- data/lib/kontena/cli/certificate/import_command.rb +61 -0
- data/lib/kontena/cli/certificate/list_command.rb +75 -0
- data/lib/kontena/cli/certificate/register_command.rb +30 -0
- data/lib/kontena/cli/certificate/remove_command.rb +23 -0
- data/lib/kontena/cli/certificate/request_command.rb +20 -0
- data/lib/kontena/cli/certificate/show_command.rb +22 -0
- data/lib/kontena/cli/certificate_command.rb +18 -0
- data/lib/kontena/cli/cloud/login_command.rb +186 -0
- data/lib/kontena/cli/cloud/logout_command.rb +14 -0
- data/lib/kontena/cli/cloud/master/add_command.rb +156 -0
- data/lib/kontena/cli/cloud/master/list_command.rb +35 -0
- data/lib/kontena/cli/cloud/master/remove_command.rb +68 -0
- data/lib/kontena/cli/cloud/master/show_command.rb +21 -0
- data/lib/kontena/cli/cloud/master/update_command.rb +52 -0
- data/lib/kontena/cli/cloud/master_command.rb +14 -0
- data/lib/kontena/cli/cloud_command.rb +13 -0
- data/lib/kontena/cli/common.rb +360 -0
- data/lib/kontena/cli/config.rb +662 -0
- data/lib/kontena/cli/container_command.rb +10 -0
- data/lib/kontena/cli/containers/exec_command.rb +31 -0
- data/lib/kontena/cli/containers/inspect_command.rb +16 -0
- data/lib/kontena/cli/containers/list_command.rb +51 -0
- data/lib/kontena/cli/containers/logs_command.rb +19 -0
- data/lib/kontena/cli/etcd/common.rb +8 -0
- data/lib/kontena/cli/etcd/get_command.rb +26 -0
- data/lib/kontena/cli/etcd/health_command.rb +53 -0
- data/lib/kontena/cli/etcd/list_command.rb +36 -0
- data/lib/kontena/cli/etcd/mkdir_command.rb +23 -0
- data/lib/kontena/cli/etcd/remove_command.rb +29 -0
- data/lib/kontena/cli/etcd/set_command.rb +24 -0
- data/lib/kontena/cli/etcd_command.rb +12 -0
- data/lib/kontena/cli/external_registries/add_command.rb +25 -0
- data/lib/kontena/cli/external_registries/list_command.rb +23 -0
- data/lib/kontena/cli/external_registries/remove_command.rb +17 -0
- data/lib/kontena/cli/external_registry_command.rb +9 -0
- data/lib/kontena/cli/grid_command.rb +21 -0
- data/lib/kontena/cli/grid_options.rb +12 -0
- data/lib/kontena/cli/grids/audit_log_command.rb +22 -0
- data/lib/kontena/cli/grids/cloud_config_command.rb +53 -0
- data/lib/kontena/cli/grids/common.rb +182 -0
- data/lib/kontena/cli/grids/create_command.rb +48 -0
- data/lib/kontena/cli/grids/current_command.rb +25 -0
- data/lib/kontena/cli/grids/env_command.rb +32 -0
- data/lib/kontena/cli/grids/events_command.rb +50 -0
- data/lib/kontena/cli/grids/health_command.rb +69 -0
- data/lib/kontena/cli/grids/list_command.rb +59 -0
- data/lib/kontena/cli/grids/logs_command.rb +35 -0
- data/lib/kontena/cli/grids/remove_command.rb +31 -0
- data/lib/kontena/cli/grids/show_command.rb +25 -0
- data/lib/kontena/cli/grids/trusted_subnet_command.rb +10 -0
- data/lib/kontena/cli/grids/trusted_subnets/add_command.rb +18 -0
- data/lib/kontena/cli/grids/trusted_subnets/list_command.rb +18 -0
- data/lib/kontena/cli/grids/trusted_subnets/remove_command.rb +26 -0
- data/lib/kontena/cli/grids/update_command.rb +35 -0
- data/lib/kontena/cli/grids/use_command.rb +26 -0
- data/lib/kontena/cli/grids/user_command.rb +9 -0
- data/lib/kontena/cli/grids/users/add_command.rb +18 -0
- data/lib/kontena/cli/grids/users/list_command.rb +20 -0
- data/lib/kontena/cli/grids/users/remove_command.rb +20 -0
- data/lib/kontena/cli/helpers/exec_helper.rb +209 -0
- data/lib/kontena/cli/helpers/health_helper.rb +65 -0
- data/lib/kontena/cli/helpers/log_helper.rb +113 -0
- data/lib/kontena/cli/helpers/time_helper.rb +29 -0
- data/lib/kontena/cli/localhost_web_server.rb +113 -0
- data/lib/kontena/cli/log_formatters/compact.rb +65 -0
- data/lib/kontena/cli/log_formatters/strip_color.rb +13 -0
- data/lib/kontena/cli/logout_command.rb +10 -0
- data/lib/kontena/cli/master/audit_log_command.rb +19 -0
- data/lib/kontena/cli/master/config/export_command.rb +46 -0
- data/lib/kontena/cli/master/config/get_command.rb +26 -0
- data/lib/kontena/cli/master/config/import_command.rb +67 -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 +17 -0
- data/lib/kontena/cli/master/create_command.rb +74 -0
- data/lib/kontena/cli/master/current_command.rb +25 -0
- data/lib/kontena/cli/master/init_cloud_command.rb +45 -0
- data/lib/kontena/cli/master/join_command.rb +22 -0
- data/lib/kontena/cli/master/list_command.rb +24 -0
- data/lib/kontena/cli/master/login_command.rb +331 -0
- data/lib/kontena/cli/master/logout_command.rb +25 -0
- data/lib/kontena/cli/master/remove_command.rb +55 -0
- data/lib/kontena/cli/master/ssh_command.rb +72 -0
- data/lib/kontena/cli/master/token/common.rb +29 -0
- data/lib/kontena/cli/master/token/create_command.rb +50 -0
- data/lib/kontena/cli/master/token/current_command.rb +45 -0
- data/lib/kontena/cli/master/token/list_command.rb +39 -0
- data/lib/kontena/cli/master/token/remove_command.rb +19 -0
- data/lib/kontena/cli/master/token/show_command.rb +34 -0
- data/lib/kontena/cli/master/token_command.rb +13 -0
- data/lib/kontena/cli/master/use_command.rb +31 -0
- data/lib/kontena/cli/master/user/invite_command.rb +51 -0
- data/lib/kontena/cli/master/user/list_command.rb +29 -0
- data/lib/kontena/cli/master/user/remove_command.rb +24 -0
- data/lib/kontena/cli/master/user/role/add_command.rb +29 -0
- data/lib/kontena/cli/master/user/role/remove_command.rb +27 -0
- data/lib/kontena/cli/master/user/role_command.rb +6 -0
- data/lib/kontena/cli/master/user_command.rb +9 -0
- data/lib/kontena/cli/master_command.rb +21 -0
- data/lib/kontena/cli/node_command.rb +17 -0
- data/lib/kontena/cli/nodes/create_command.rb +25 -0
- data/lib/kontena/cli/nodes/env_command.rb +37 -0
- data/lib/kontena/cli/nodes/health_command.rb +47 -0
- data/lib/kontena/cli/nodes/label_command.rb +10 -0
- data/lib/kontena/cli/nodes/labels/add_command.rb +18 -0
- data/lib/kontena/cli/nodes/labels/list_command.rb +19 -0
- data/lib/kontena/cli/nodes/labels/remove_command.rb +32 -0
- data/lib/kontena/cli/nodes/list_command.rb +97 -0
- data/lib/kontena/cli/nodes/remove_command.rb +34 -0
- data/lib/kontena/cli/nodes/reset_token_command.rb +34 -0
- data/lib/kontena/cli/nodes/show_command.rb +56 -0
- data/lib/kontena/cli/nodes/ssh_command.rb +63 -0
- data/lib/kontena/cli/nodes/update_command.rb +31 -0
- data/lib/kontena/cli/plugin_command.rb +12 -0
- data/lib/kontena/cli/plugins/common.rb +8 -0
- data/lib/kontena/cli/plugins/install_command.rb +42 -0
- data/lib/kontena/cli/plugins/list_command.rb +31 -0
- data/lib/kontena/cli/plugins/search_command.rb +25 -0
- data/lib/kontena/cli/plugins/show_command.rb +17 -0
- data/lib/kontena/cli/plugins/uninstall_command.rb +31 -0
- data/lib/kontena/cli/plugins/upgrade_command.rb +60 -0
- data/lib/kontena/cli/registry/create_command.rb +151 -0
- data/lib/kontena/cli/registry/remove_command.rb +21 -0
- data/lib/kontena/cli/registry_command.rb +8 -0
- data/lib/kontena/cli/service_command.rb +28 -0
- data/lib/kontena/cli/services/container_command.rb +8 -0
- data/lib/kontena/cli/services/containers_command.rb +39 -0
- data/lib/kontena/cli/services/create_command.rb +105 -0
- data/lib/kontena/cli/services/deploy_command.rb +25 -0
- data/lib/kontena/cli/services/env_command.rb +9 -0
- data/lib/kontena/cli/services/envs/add_command.rb +21 -0
- data/lib/kontena/cli/services/envs/list_command.rb +22 -0
- data/lib/kontena/cli/services/envs/remove_command.rb +22 -0
- data/lib/kontena/cli/services/events_command.rb +36 -0
- data/lib/kontena/cli/services/exec_command.rb +107 -0
- data/lib/kontena/cli/services/link_command.rb +35 -0
- data/lib/kontena/cli/services/list_command.rb +66 -0
- data/lib/kontena/cli/services/logs_command.rb +33 -0
- data/lib/kontena/cli/services/monitor_command.rb +58 -0
- data/lib/kontena/cli/services/remove_command.rb +60 -0
- data/lib/kontena/cli/services/restart_command.rb +19 -0
- data/lib/kontena/cli/services/scale_command.rb +21 -0
- data/lib/kontena/cli/services/secret_command.rb +8 -0
- data/lib/kontena/cli/services/secrets/link_command.rb +26 -0
- data/lib/kontena/cli/services/secrets/unlink_command.rb +28 -0
- data/lib/kontena/cli/services/services_helper.rb +579 -0
- data/lib/kontena/cli/services/show_command.rb +26 -0
- data/lib/kontena/cli/services/start_command.rb +21 -0
- data/lib/kontena/cli/services/stats_command.rb +87 -0
- data/lib/kontena/cli/services/stop_command.rb +21 -0
- data/lib/kontena/cli/services/unlink_command.rb +30 -0
- data/lib/kontena/cli/services/update_command.rb +94 -0
- data/lib/kontena/cli/spinner.rb +205 -0
- data/lib/kontena/cli/stack_command.rb +21 -0
- data/lib/kontena/cli/stacks/build_command.rb +125 -0
- data/lib/kontena/cli/stacks/common.rb +209 -0
- data/lib/kontena/cli/stacks/deploy_command.rb +37 -0
- data/lib/kontena/cli/stacks/events_command.rb +33 -0
- data/lib/kontena/cli/stacks/inspect_command.rb +17 -0
- data/lib/kontena/cli/stacks/install_command.rb +95 -0
- data/lib/kontena/cli/stacks/label_command.rb +10 -0
- data/lib/kontena/cli/stacks/labels/add_command.rb +21 -0
- data/lib/kontena/cli/stacks/labels/common.rb +19 -0
- data/lib/kontena/cli/stacks/labels/list_command.rb +21 -0
- data/lib/kontena/cli/stacks/labels/remove_command.rb +21 -0
- data/lib/kontena/cli/stacks/list_command.rb +154 -0
- data/lib/kontena/cli/stacks/logs_command.rb +35 -0
- data/lib/kontena/cli/stacks/monitor_command.rb +93 -0
- data/lib/kontena/cli/stacks/registry/create_command.rb +24 -0
- data/lib/kontena/cli/stacks/registry/make_private_command.rb +24 -0
- data/lib/kontena/cli/stacks/registry/make_public_command.rb +24 -0
- data/lib/kontena/cli/stacks/registry/pull_command.rb +28 -0
- data/lib/kontena/cli/stacks/registry/push_command.rb +40 -0
- data/lib/kontena/cli/stacks/registry/remove_command.rb +30 -0
- data/lib/kontena/cli/stacks/registry/search_command.rb +42 -0
- data/lib/kontena/cli/stacks/registry/show_command.rb +65 -0
- data/lib/kontena/cli/stacks/registry_command.rb +12 -0
- data/lib/kontena/cli/stacks/remove_command.rb +80 -0
- data/lib/kontena/cli/stacks/restart_command.rb +24 -0
- data/lib/kontena/cli/stacks/service_generator.rb +131 -0
- data/lib/kontena/cli/stacks/service_generator_v2.rb +27 -0
- data/lib/kontena/cli/stacks/show_command.rb +168 -0
- data/lib/kontena/cli/stacks/stack_name.rb +71 -0
- data/lib/kontena/cli/stacks/stacks_helper.rb +83 -0
- data/lib/kontena/cli/stacks/stop_command.rb +24 -0
- data/lib/kontena/cli/stacks/upgrade_command.rb +264 -0
- data/lib/kontena/cli/stacks/validate_command.rb +75 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/affinities_validator.rb +19 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/build_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/certificates_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/extends_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/hooks_validator.rb +102 -0
- data/lib/kontena/cli/stacks/yaml/custom_validators/secrets_validator.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/opto/certificates_resolver.rb +37 -0
- data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +78 -0
- data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +25 -0
- data/lib/kontena/cli/stacks/yaml/opto/service_link_resolver.rb +80 -0
- data/lib/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver.rb +39 -0
- data/lib/kontena/cli/stacks/yaml/opto/vault_resolver.rb +13 -0
- data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +12 -0
- data/lib/kontena/cli/stacks/yaml/opto.rb +16 -0
- data/lib/kontena/cli/stacks/yaml/reader.rb +525 -0
- data/lib/kontena/cli/stacks/yaml/service_extender.rb +65 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader/file_loader.rb +41 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader/registry_loader.rb +24 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader/uri_loader.rb +23 -0
- data/lib/kontena/cli/stacks/yaml/stack_file_loader.rb +152 -0
- data/lib/kontena/cli/stacks/yaml/validations.rb +119 -0
- data/lib/kontena/cli/stacks/yaml/validator_v3.rb +164 -0
- data/lib/kontena/cli/subcommand_loader.rb +83 -0
- data/lib/kontena/cli/table_generator.rb +128 -0
- data/lib/kontena/cli/vault/export_command.rb +24 -0
- data/lib/kontena/cli/vault/import_command.rb +75 -0
- data/lib/kontena/cli/vault/list_command.rb +37 -0
- data/lib/kontena/cli/vault/read_command.rb +27 -0
- data/lib/kontena/cli/vault/remove_command.rb +23 -0
- data/lib/kontena/cli/vault/update_command.rb +24 -0
- data/lib/kontena/cli/vault/write_command.rb +23 -0
- data/lib/kontena/cli/vault_command.rb +13 -0
- data/lib/kontena/cli/version.rb +10 -0
- data/lib/kontena/cli/version_command.rb +20 -0
- data/lib/kontena/cli/volume_command.rb +9 -0
- data/lib/kontena/cli/volumes/create_command.rb +42 -0
- data/lib/kontena/cli/volumes/list_command.rb +29 -0
- data/lib/kontena/cli/volumes/remove_command.rb +29 -0
- data/lib/kontena/cli/volumes/show_command.rb +38 -0
- data/lib/kontena/cli/vpn/config_command.rb +27 -0
- data/lib/kontena/cli/vpn/create_command.rb +99 -0
- data/lib/kontena/cli/vpn/remove_command.rb +22 -0
- data/lib/kontena/cli/vpn_command.rb +9 -0
- data/lib/kontena/cli/whoami_command.rb +38 -0
- data/lib/kontena/client.rb +574 -0
- data/lib/kontena/command.rb +251 -0
- data/lib/kontena/debug_instrumentor.rb +80 -0
- data/lib/kontena/errors.rb +50 -0
- data/lib/kontena/light_prompt.rb +103 -0
- data/lib/kontena/machine/cert_helper.rb +43 -0
- data/lib/kontena/machine/cloud_config/cloudinit.yml +82 -0
- data/lib/kontena/machine/cloud_config/node_generator.rb +28 -0
- data/lib/kontena/machine/common.rb +17 -0
- data/lib/kontena/machine/random_name.rb +42 -0
- data/lib/kontena/main_command.rb +66 -0
- data/lib/kontena/plugin_manager/cleaner.rb +33 -0
- data/lib/kontena/plugin_manager/common.rb +89 -0
- data/lib/kontena/plugin_manager/installer.rb +78 -0
- data/lib/kontena/plugin_manager/loader.rb +93 -0
- data/lib/kontena/plugin_manager/rubygems_client.rb +59 -0
- data/lib/kontena/plugin_manager/uninstaller.rb +34 -0
- data/lib/kontena/plugin_manager.rb +26 -0
- data/lib/kontena/presets/github_auth_provider.yml +11 -0
- data/lib/kontena/presets/kontena_auth_provider.yml +11 -0
- data/lib/kontena/scripts/completer +9 -0
- data/lib/kontena/scripts/completer.rb +334 -0
- data/lib/kontena/scripts/init +18 -0
- data/lib/kontena/scripts/kontena.zsh +11 -0
- data/lib/kontena/scripts/krates.bash +8 -0
- data/lib/kontena/stacks/change_resolver.rb +118 -0
- data/lib/kontena/stacks/stack_data.rb +58 -0
- data/lib/kontena/stacks/stack_data_set.rb +51 -0
- data/lib/kontena/stacks_cache.rb +110 -0
- data/lib/kontena/stacks_client.rb +177 -0
- data/lib/kontena/util.rb +116 -0
- data/lib/kontena_cli.rb +190 -0
- metadata +518 -0
@@ -0,0 +1,574 @@
|
|
1
|
+
module Kontena
|
2
|
+
class Client
|
3
|
+
|
4
|
+
CLIENT_ID = ENV['KONTENA_CLIENT_ID'] || '15faec8a7a9b4f1e8b7daebb1307f1d8'.freeze
|
5
|
+
CLIENT_SECRET = ENV['KONTENA_CLIENT_SECRET'] || 'fb8942ae00da4c7b8d5a1898effc742f'.freeze
|
6
|
+
|
7
|
+
CONTENT_URLENCODED = 'application/x-www-form-urlencoded'.freeze
|
8
|
+
CONTENT_JSON = 'application/json'.freeze
|
9
|
+
JSON_REGEX = /application\/(.+?\+)?json/.freeze
|
10
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
11
|
+
X_KONTENA_VERSION = 'X-Kontena-Version'.freeze
|
12
|
+
ACCEPT = 'Accept'.freeze
|
13
|
+
AUTHORIZATION = 'Authorization'.freeze
|
14
|
+
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
15
|
+
GZIP = 'gzip'.freeze
|
16
|
+
|
17
|
+
attr_accessor :default_headers
|
18
|
+
attr_accessor :path_prefix
|
19
|
+
attr_reader :http_client
|
20
|
+
attr_reader :last_response
|
21
|
+
attr_reader :options
|
22
|
+
attr_reader :token
|
23
|
+
attr_reader :logger
|
24
|
+
attr_reader :api_url
|
25
|
+
attr_reader :host
|
26
|
+
|
27
|
+
# Initialize api client
|
28
|
+
#
|
29
|
+
# @param [String] api_url
|
30
|
+
# @param [Kontena::Cli::Config::Token,Hash] access_token
|
31
|
+
# @param [Hash] options
|
32
|
+
def initialize(api_url, token = nil, options = {})
|
33
|
+
require 'json'
|
34
|
+
require 'excon'
|
35
|
+
require 'uri'
|
36
|
+
require 'base64'
|
37
|
+
require 'socket'
|
38
|
+
require 'openssl'
|
39
|
+
require 'uri'
|
40
|
+
require 'time'
|
41
|
+
require 'kontena/errors'
|
42
|
+
require 'kontena/cli/version'
|
43
|
+
require 'kontena/cli/config'
|
44
|
+
|
45
|
+
@api_url, @token, @options = api_url, token, options
|
46
|
+
uri = URI.parse(@api_url)
|
47
|
+
@host = uri.host
|
48
|
+
|
49
|
+
@logger = Kontena.logger
|
50
|
+
|
51
|
+
@options[:default_headers] ||= {}
|
52
|
+
|
53
|
+
excon_opts = {
|
54
|
+
omit_default_port: true,
|
55
|
+
connect_timeout: ENV["EXCON_CONNECT_TIMEOUT"] ? ENV["EXCON_CONNECT_TIMEOUT"].to_i : 10,
|
56
|
+
read_timeout: ENV["EXCON_READ_TIMEOUT"] ? ENV["EXCON_READ_TIMEOUT"].to_i : 30,
|
57
|
+
write_timeout: ENV["EXCON_WRITE_TIMEOUT"] ? ENV["EXCON_WRITE_TIMEOUT"].to_i : 10,
|
58
|
+
ssl_verify_peer: ignore_ssl_errors? ? false : true,
|
59
|
+
middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::Decompress]
|
60
|
+
}
|
61
|
+
if Kontena.debug?
|
62
|
+
require 'kontena/debug_instrumentor'
|
63
|
+
excon_opts[:instrumentor] = Kontena::DebugInstrumentor
|
64
|
+
end
|
65
|
+
excon_opts[:ssl_ca_file] = @options[:ssl_cert_path]
|
66
|
+
excon_opts[:ssl_verify_peer_host] = @options[:ssl_subject_cn]
|
67
|
+
|
68
|
+
debug { "Excon opts: #{excon_opts.inspect}" }
|
69
|
+
|
70
|
+
@http_client = Excon.new(api_url, excon_opts)
|
71
|
+
|
72
|
+
@default_headers = {
|
73
|
+
ACCEPT => CONTENT_JSON,
|
74
|
+
CONTENT_TYPE => CONTENT_JSON,
|
75
|
+
'User-Agent' => "kontena-cli/#{Kontena::Cli::VERSION}"
|
76
|
+
}.merge(options[:default_headers])
|
77
|
+
|
78
|
+
if token
|
79
|
+
if token.kind_of?(String)
|
80
|
+
@token = { 'access_token' => token }
|
81
|
+
else
|
82
|
+
@token = token
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
@api_url = api_url
|
87
|
+
@path_prefix = options[:prefix] || '/v1/'
|
88
|
+
end
|
89
|
+
|
90
|
+
def debug(&block)
|
91
|
+
logger.debug("CLIENT", &block)
|
92
|
+
end
|
93
|
+
|
94
|
+
def error(&block)
|
95
|
+
logger.error("CLIENT", &block)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Generates a header hash for HTTP basic authentication.
|
99
|
+
# Defaults to using client_id and client_secret as user/pass
|
100
|
+
#
|
101
|
+
# @param [String] username
|
102
|
+
# @param [String] password
|
103
|
+
# @return [Hash] auth_header_hash
|
104
|
+
def basic_auth_header(user = nil, pass = nil)
|
105
|
+
user ||= client_id
|
106
|
+
pass ||= client_secret
|
107
|
+
{
|
108
|
+
AUTHORIZATION =>
|
109
|
+
"Basic #{Base64.encode64([user, pass].join(':')).gsub(/[\r\n]/, '')}"
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Generates a bearer token authentication header hash if a token object is
|
114
|
+
# available. Otherwise returns an empty hash.
|
115
|
+
#
|
116
|
+
# @return [Hash] authentication_header
|
117
|
+
def bearer_authorization_header
|
118
|
+
if token && token['access_token']
|
119
|
+
{AUTHORIZATION => "Bearer #{token['access_token']}"}
|
120
|
+
else
|
121
|
+
{}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Requests path supplied as argument and returns true if the request was a success.
|
126
|
+
# For checking if the current authentication is valid.
|
127
|
+
#
|
128
|
+
# @param [String] token_verify_path a path that requires authentication
|
129
|
+
# @return [Boolean]
|
130
|
+
def authentication_ok?(token_verify_path)
|
131
|
+
return false unless token
|
132
|
+
return false unless token['access_token']
|
133
|
+
return false unless token_verify_path
|
134
|
+
|
135
|
+
final_path = token_verify_path.gsub(/\:access\_token/, token['access_token'])
|
136
|
+
debug { "Requesting user info from #{final_path}" }
|
137
|
+
request(path: final_path)
|
138
|
+
true
|
139
|
+
rescue => ex
|
140
|
+
error { "Authentication verification exception" }
|
141
|
+
error { ex }
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
# Calls the code exchange endpoint in token's config to exchange an authorization_code
|
146
|
+
# to a access_token
|
147
|
+
def exchange_code(code)
|
148
|
+
return nil unless token_account
|
149
|
+
return nil unless token_account['token_endpoint']
|
150
|
+
|
151
|
+
response = request(
|
152
|
+
http_method: token_account['token_method'].downcase.to_sym,
|
153
|
+
path: token_account['token_endpoint'],
|
154
|
+
headers: { CONTENT_TYPE => token_account['token_post_content_type'] },
|
155
|
+
body: {
|
156
|
+
'grant_type' => 'authorization_code',
|
157
|
+
'code' => code,
|
158
|
+
'client_id' => Kontena::Client::CLIENT_ID,
|
159
|
+
'client_secret' => Kontena::Client::CLIENT_SECRET
|
160
|
+
},
|
161
|
+
expects: [200,201],
|
162
|
+
auth: false
|
163
|
+
)
|
164
|
+
response['expires_at'] ||= in_to_at(response['expires_in'])
|
165
|
+
response
|
166
|
+
end
|
167
|
+
|
168
|
+
# Return server version from a Kontena master by requesting '/'
|
169
|
+
#
|
170
|
+
# @return [String] version_string
|
171
|
+
def server_version
|
172
|
+
request(auth: false, expects: 200)['version']
|
173
|
+
rescue => ex
|
174
|
+
error { "Server version exception" }
|
175
|
+
error { ex }
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# OAuth2 client_id from ENV KONTENA_CLIENT_ID or client CLIENT_ID constant
|
180
|
+
#
|
181
|
+
# @return [String]
|
182
|
+
def client_id
|
183
|
+
ENV['KONTENA_CLIENT_ID'] || CLIENT_ID
|
184
|
+
end
|
185
|
+
|
186
|
+
# OAuth2 client_secret from ENV KONTENA_CLIENT_SECRET or client CLIENT_SECRET constant
|
187
|
+
#
|
188
|
+
# @return [String]
|
189
|
+
def client_secret
|
190
|
+
ENV['KONTENA_CLIENT_SECRET'] || CLIENT_SECRET
|
191
|
+
end
|
192
|
+
|
193
|
+
# Get request
|
194
|
+
#
|
195
|
+
# @param [String] path
|
196
|
+
# @param [Hash,NilClass] params
|
197
|
+
# @param [Hash] headers
|
198
|
+
# @return [Hash]
|
199
|
+
def get(path, params = nil, headers = {}, auth = true)
|
200
|
+
request(path: path, query: params, headers: headers, auth: auth)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Post request
|
204
|
+
#
|
205
|
+
# @param [String] path
|
206
|
+
# @param [Object] obj
|
207
|
+
# @param [Hash] params
|
208
|
+
# @param [Hash] headers
|
209
|
+
# @return [Hash]
|
210
|
+
def post(path, obj, params = {}, headers = {}, auth = true)
|
211
|
+
request(http_method: :post, path: path, body: obj, query: params, headers: headers, auth: auth)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Put request
|
215
|
+
#
|
216
|
+
# @param [String] path
|
217
|
+
# @param [Object] obj
|
218
|
+
# @param [Hash] params
|
219
|
+
# @param [Hash] headers
|
220
|
+
# @return [Hash]
|
221
|
+
def put(path, obj, params = {}, headers = {}, auth = true)
|
222
|
+
request(http_method: :put, path: path, body: obj, query: params, headers: headers, auth: auth)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Patch request
|
226
|
+
#
|
227
|
+
# @param [String] path
|
228
|
+
# @param [Object] obj
|
229
|
+
# @param [Hash] params
|
230
|
+
# @param [Hash] headers
|
231
|
+
# @return [Hash]
|
232
|
+
def patch(path, obj, params = {}, headers = {}, auth = true)
|
233
|
+
request(http_method: :patch, path: path, body: obj, query: params, headers: headers, auth: auth)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Delete request
|
237
|
+
#
|
238
|
+
# @param [String] path
|
239
|
+
# @param [Hash,String] body
|
240
|
+
# @param [Hash] params
|
241
|
+
# @param [Hash] headers
|
242
|
+
# @return [Hash]
|
243
|
+
def delete(path, body = nil, params = {}, headers = {}, auth = true)
|
244
|
+
request(http_method: :delete, path: path, body: body, query: params, headers: headers, auth: auth)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Get stream request
|
248
|
+
#
|
249
|
+
# @param [String] path
|
250
|
+
# @param [Lambda] response_block
|
251
|
+
# @param [Hash,NilClass] params
|
252
|
+
# @param [Hash] headers
|
253
|
+
def get_stream(path, response_block, params = nil, headers = {}, auth = true)
|
254
|
+
request(path: path, query: params, headers: headers, response_block: response_block, auth: auth, gzip: false)
|
255
|
+
end
|
256
|
+
|
257
|
+
def token_expired?
|
258
|
+
return false unless token
|
259
|
+
if token.respond_to?(:expired?)
|
260
|
+
token.expired?
|
261
|
+
elsif token['expires_at'].to_i > 0
|
262
|
+
token['expires_at'].to_i < Time.now.utc.to_i
|
263
|
+
else
|
264
|
+
false
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Perform a HTTP request. Will try to refresh the access token and retry if it's
|
269
|
+
# expired or if the server responds with HTTP 401.
|
270
|
+
#
|
271
|
+
# Automatically parses a JSON response into a hash.
|
272
|
+
#
|
273
|
+
# After the request has been performed, the response can be inspected using
|
274
|
+
# client.last_response.
|
275
|
+
#
|
276
|
+
# @param http_method [Symbol] :get, :post, etc
|
277
|
+
# @param path [String] if it starts with / then prefix won't be used.
|
278
|
+
# @param body [Hash, String] will be encoded using #encode_body
|
279
|
+
# @param query [Hash] url query parameters
|
280
|
+
# @param headers [Hash] extra headers for request.
|
281
|
+
# @param response_block [Proc] for streaming requests, must respond to #call
|
282
|
+
# @param expects [Array] raises unless response status code matches this list.
|
283
|
+
# @param auth [Boolean] use token authentication default = true
|
284
|
+
# @return [Hash, String] response parsed response object
|
285
|
+
def request(http_method: :get, path:'/', body: nil, query: {}, headers: {}, response_block: nil, expects: [200, 201, 204], host: nil, port: nil, auth: true, gzip: true)
|
286
|
+
|
287
|
+
retried ||= false
|
288
|
+
|
289
|
+
if auth && token_expired?
|
290
|
+
raise Excon::Error::Unauthorized, "Token expired or not valid, you need to login again, use: kontena #{token_is_for_master? ? "master" : "cloud"} login"
|
291
|
+
end
|
292
|
+
|
293
|
+
request_headers = request_headers(headers, auth: auth, gzip: gzip)
|
294
|
+
|
295
|
+
if body.nil?
|
296
|
+
body_content = ''
|
297
|
+
request_headers.delete(CONTENT_TYPE)
|
298
|
+
else
|
299
|
+
body_content = encode_body(body, request_headers[CONTENT_TYPE])
|
300
|
+
request_headers.merge!('Content-Length' => body_content.bytesize)
|
301
|
+
end
|
302
|
+
|
303
|
+
uri = URI.parse(path)
|
304
|
+
host_options = {}
|
305
|
+
|
306
|
+
if uri.host
|
307
|
+
host_options[:host] = uri.host
|
308
|
+
host_options[:port] = uri.port
|
309
|
+
host_options[:scheme] = uri.scheme
|
310
|
+
path = uri.request_uri
|
311
|
+
else
|
312
|
+
host_options[:host] = host if host
|
313
|
+
host_options[:port] = port if port
|
314
|
+
end
|
315
|
+
|
316
|
+
request_options = {
|
317
|
+
method: http_method,
|
318
|
+
expects: Array(expects),
|
319
|
+
path: path_with_prefix(path),
|
320
|
+
headers: request_headers,
|
321
|
+
body: body_content,
|
322
|
+
query: query
|
323
|
+
}.merge(host_options)
|
324
|
+
|
325
|
+
request_options.merge!(response_block: response_block) if response_block
|
326
|
+
|
327
|
+
# Store the response into client.last_response
|
328
|
+
@last_response = http_client.request(request_options)
|
329
|
+
|
330
|
+
parse_response(@last_response)
|
331
|
+
rescue Excon::Error::Unauthorized
|
332
|
+
if token
|
333
|
+
debug { 'Server reports access token expired' }
|
334
|
+
|
335
|
+
if retried || !token || !token['refresh_token']
|
336
|
+
raise Kontena::Errors::StandardError.new(401, 'The access token has expired and needs to be refreshed')
|
337
|
+
end
|
338
|
+
|
339
|
+
retried = true
|
340
|
+
retry if refresh_token
|
341
|
+
end
|
342
|
+
raise Kontena::Errors::StandardError.new(401, 'Unauthorized')
|
343
|
+
rescue Excon::Error::HTTPStatus => error
|
344
|
+
if error.response.headers['Content-Encoding'] == 'gzip'
|
345
|
+
error.response.body = Zlib::GzipReader.new(StringIO.new(error.response.body)).read
|
346
|
+
end
|
347
|
+
|
348
|
+
debug { "Request #{error.request[:method].upcase} #{error.request[:path]}: #{error.response.status} #{error.response.reason_phrase}: #{error.response.body}" }
|
349
|
+
|
350
|
+
handle_error_response(error.response)
|
351
|
+
end
|
352
|
+
|
353
|
+
# Build a token refresh request param hash
|
354
|
+
#
|
355
|
+
# @return [Hash]
|
356
|
+
def refresh_request_params
|
357
|
+
{
|
358
|
+
refresh_token: token['refresh_token'],
|
359
|
+
grant_type: 'refresh_token',
|
360
|
+
client_id: client_id,
|
361
|
+
client_secret: client_secret
|
362
|
+
}
|
363
|
+
end
|
364
|
+
|
365
|
+
# Accessor to token's account settings
|
366
|
+
def token_account
|
367
|
+
return {} unless token
|
368
|
+
if token.respond_to?(:account)
|
369
|
+
token.account
|
370
|
+
elsif token.kind_of?(Hash) && token['account'].kind_of?(String)
|
371
|
+
config.find_account(token['account'])
|
372
|
+
else
|
373
|
+
{}
|
374
|
+
end
|
375
|
+
rescue => ex
|
376
|
+
error { "Access token refresh exception" }
|
377
|
+
error { ex }
|
378
|
+
false
|
379
|
+
end
|
380
|
+
|
381
|
+
# Perform refresh token request to auth provider.
|
382
|
+
# Updates the client's Token object and writes changes to
|
383
|
+
# configuration.
|
384
|
+
#
|
385
|
+
# @param [Boolean] use_basic_auth? When true, use basic auth authentication header
|
386
|
+
# @return [Boolean] success?
|
387
|
+
def refresh_token
|
388
|
+
debug { "Performing token refresh" }
|
389
|
+
return false if token.nil?
|
390
|
+
return false if token['refresh_token'].nil?
|
391
|
+
uri = URI.parse(token_account['token_endpoint'])
|
392
|
+
endpoint_data = { path: uri.path }
|
393
|
+
endpoint_data[:host] = uri.host if uri.host
|
394
|
+
endpoint_data[:port] = uri.port if uri.port
|
395
|
+
|
396
|
+
debug { "Token refresh endpoint: #{endpoint_data.inspect}" }
|
397
|
+
|
398
|
+
return false unless endpoint_data[:path]
|
399
|
+
|
400
|
+
response = request(
|
401
|
+
{
|
402
|
+
http_method: token_account['token_method'].downcase.to_sym,
|
403
|
+
body: refresh_request_params,
|
404
|
+
headers: {
|
405
|
+
CONTENT_TYPE => token_account['token_post_content_type']
|
406
|
+
}.merge(
|
407
|
+
token_account['code_requires_basic_auth'] ? basic_auth_header : {}
|
408
|
+
),
|
409
|
+
expects: [200, 201, 400, 401, 403],
|
410
|
+
auth: false
|
411
|
+
}.merge(endpoint_data)
|
412
|
+
)
|
413
|
+
|
414
|
+
if response && response['access_token']
|
415
|
+
debug { "Got response to refresh request" }
|
416
|
+
token['access_token'] = response['access_token']
|
417
|
+
token['refresh_token'] = response['refresh_token']
|
418
|
+
token['expires_at'] = in_to_at(response['expires_in'])
|
419
|
+
token.config.write if token.respond_to?(:config)
|
420
|
+
true
|
421
|
+
else
|
422
|
+
debug { "Got null or bad response to refresh request: #{last_response.inspect}" }
|
423
|
+
false
|
424
|
+
end
|
425
|
+
rescue => ex
|
426
|
+
error { "Access token refresh exception" }
|
427
|
+
error { ex }
|
428
|
+
false
|
429
|
+
end
|
430
|
+
|
431
|
+
private
|
432
|
+
|
433
|
+
# Returns true if the token object belongs to a master
|
434
|
+
#
|
435
|
+
# @return [Boolean]
|
436
|
+
def token_is_for_master?
|
437
|
+
token_account['name'] == 'master'
|
438
|
+
end
|
439
|
+
|
440
|
+
|
441
|
+
# Get prefixed request path unless path starts with /
|
442
|
+
#
|
443
|
+
# @param [String] path
|
444
|
+
# @return [String]
|
445
|
+
def path_with_prefix(path)
|
446
|
+
path.to_s.start_with?('/') ? path : "#{path_prefix}#{path}"
|
447
|
+
end
|
448
|
+
|
449
|
+
|
450
|
+
##
|
451
|
+
# Build request headers. Removes empty headers.
|
452
|
+
# @example
|
453
|
+
# request_headers('Authorization' => nil)
|
454
|
+
#
|
455
|
+
# @param [Hash] headers
|
456
|
+
# @return [Hash]
|
457
|
+
def request_headers(headers = {}, auth: true, gzip: true)
|
458
|
+
headers = default_headers.merge(headers)
|
459
|
+
headers.merge!(bearer_authorization_header) if auth
|
460
|
+
headers[ACCEPT_ENCODING] = GZIP if gzip
|
461
|
+
headers.reject{|_,v| v.nil? || (v.respond_to?(:empty?) && v.empty?)}
|
462
|
+
end
|
463
|
+
|
464
|
+
##
|
465
|
+
# Encode body based on content type.
|
466
|
+
#
|
467
|
+
# @param [Object] body
|
468
|
+
# @param [String] content_type
|
469
|
+
# @return [String] encoded_content
|
470
|
+
def encode_body(body, content_type)
|
471
|
+
if content_type =~ JSON_REGEX # vnd.api+json should pass as json
|
472
|
+
dump_json(body)
|
473
|
+
elsif content_type == CONTENT_URLENCODED && body.kind_of?(Hash)
|
474
|
+
URI.encode_www_form(body)
|
475
|
+
else
|
476
|
+
body
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
##
|
481
|
+
# Parse response. If the respons is JSON, returns a Hash representation.
|
482
|
+
# Otherwise returns the raw body.
|
483
|
+
#
|
484
|
+
# @param [Excon::Response]
|
485
|
+
# @return [Hash,String]
|
486
|
+
def parse_response(response)
|
487
|
+
check_version_and_warn(response.headers[X_KONTENA_VERSION])
|
488
|
+
|
489
|
+
if response.headers[CONTENT_TYPE] =~ JSON_REGEX
|
490
|
+
parse_json(response)
|
491
|
+
else
|
492
|
+
response.body
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
def check_version_and_warn(server_version)
|
497
|
+
return nil if $VERSION_WARNING_ADDED
|
498
|
+
return nil unless server_version.to_s =~ /^\d+\.\d+\.\d+/
|
499
|
+
|
500
|
+
unless server_version[/^(\d+\.\d+)/, 1] == Kontena::Cli::VERSION[/^(\d+\.\d+)/, 1] # Just compare x.y
|
501
|
+
add_version_warning(server_version)
|
502
|
+
$VERSION_WARNING_ADDED = true
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def add_version_warning(server_version)
|
507
|
+
at_exit do
|
508
|
+
warn Kontena.pastel.yellow("Warning: Server version is #{server_version}. You are using CLI version #{Kontena::Cli::VERSION}.")
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
# Parse json
|
513
|
+
#
|
514
|
+
# @param response [Excon::Response]
|
515
|
+
# @return [Hash,Object,NilClass]
|
516
|
+
def parse_json(response)
|
517
|
+
return nil if response.body.empty?
|
518
|
+
|
519
|
+
JSON.parse(response.body)
|
520
|
+
rescue => ex
|
521
|
+
raise Kontena::Errors::StandardError.new(520, "Invalid response JSON from server for #{response.path}: #{ex.class.name}: #{ex.message}")
|
522
|
+
end
|
523
|
+
|
524
|
+
# Dump json
|
525
|
+
#
|
526
|
+
# @param [Object] obj
|
527
|
+
# @return [String]
|
528
|
+
def dump_json(obj)
|
529
|
+
JSON.dump(obj)
|
530
|
+
end
|
531
|
+
|
532
|
+
# @return [Boolean]
|
533
|
+
def ignore_ssl_errors?
|
534
|
+
ENV['SSL_IGNORE_ERRORS'] == 'true' || options[:ignore_ssl_errors]
|
535
|
+
end
|
536
|
+
|
537
|
+
# @param [Excon::Response] response
|
538
|
+
def handle_error_response(response)
|
539
|
+
data = parse_response(response)
|
540
|
+
|
541
|
+
request_path = " (#{response.path})"
|
542
|
+
|
543
|
+
if data.is_a?(Hash) && data.has_key?('error') && data['error'].is_a?(Hash)
|
544
|
+
raise Kontena::Errors::StandardErrorHash.new(response.status, response.reason_phrase, data['error'])
|
545
|
+
elsif data.is_a?(Hash) && data.has_key?('errors') && data['errors'].is_a?(Array) && data['errors'].all? { |e| e.is_a?(Hash) }
|
546
|
+
error_with_status = data['errors'].find { |error| error.key?('status') }
|
547
|
+
if error_with_status
|
548
|
+
status = error_with_status['status']
|
549
|
+
else
|
550
|
+
status = response.status
|
551
|
+
end
|
552
|
+
raise Kontena::Errors::StandardErrorHash.new(status, response.reason_phrase, data)
|
553
|
+
elsif data.is_a?(Hash) && data.has_key?('error')
|
554
|
+
raise Kontena::Errors::StandardError.new(response.status, data['error'] + request_path)
|
555
|
+
elsif data.is_a?(String) && !data.empty?
|
556
|
+
raise Kontena::Errors::StandardError.new(response.status, data + request_path)
|
557
|
+
else
|
558
|
+
raise Kontena::Errors::StandardError.new(response.status, response.reason_phrase + request_path)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
# Convert expires_in into expires_at
|
563
|
+
#
|
564
|
+
# @param [Fixnum] seconds_till_expiration
|
565
|
+
# @return [Fixnum] expires_at_unix_timestamp
|
566
|
+
def in_to_at(expires_in)
|
567
|
+
if expires_in.to_i < 1
|
568
|
+
0
|
569
|
+
else
|
570
|
+
Time.now.utc.to_i + expires_in.to_i
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|