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.
Files changed (293) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +212 -0
  3. data/LOGO +10 -0
  4. data/VERSION +1 -0
  5. data/bin/krates +23 -0
  6. data/lib/kontena/autoload_core.rb +19 -0
  7. data/lib/kontena/callback.rb +60 -0
  8. data/lib/kontena/callbacks/.gitkeep +0 -0
  9. data/lib/kontena/callbacks/auth/01_list_and_select_grid_after_master_auth.rb +26 -0
  10. data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +19 -0
  11. data/lib/kontena/callbacks/master/deploy/01_show_logo_before_deploy.rb +14 -0
  12. data/lib/kontena/callbacks/master/deploy/04_default_master_version.rb +18 -0
  13. data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +105 -0
  14. data/lib/kontena/callbacks/master/deploy/40_install_ssl_certificate_after_deploy.rb +32 -0
  15. data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +66 -0
  16. data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +21 -0
  17. data/lib/kontena/callbacks/master/deploy/56_set_server_provider_after_deploy.rb +24 -0
  18. data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +31 -0
  19. data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +95 -0
  20. data/lib/kontena/callbacks/master/deploy/90_proptip_after_deploy.rb +33 -0
  21. data/lib/kontena/cli/browser_launcher.rb +61 -0
  22. data/lib/kontena/cli/bytes_helper.rb +40 -0
  23. data/lib/kontena/cli/certificate/authorize_command.rb +107 -0
  24. data/lib/kontena/cli/certificate/common.rb +16 -0
  25. data/lib/kontena/cli/certificate/domain_authorization/list_command.rb +24 -0
  26. data/lib/kontena/cli/certificate/domain_authorization/remove_authorization_command.rb +25 -0
  27. data/lib/kontena/cli/certificate/domain_authorize_command.rb +7 -0
  28. data/lib/kontena/cli/certificate/export_command.rb +28 -0
  29. data/lib/kontena/cli/certificate/get_command.rb +33 -0
  30. data/lib/kontena/cli/certificate/import_command.rb +61 -0
  31. data/lib/kontena/cli/certificate/list_command.rb +75 -0
  32. data/lib/kontena/cli/certificate/register_command.rb +30 -0
  33. data/lib/kontena/cli/certificate/remove_command.rb +23 -0
  34. data/lib/kontena/cli/certificate/request_command.rb +20 -0
  35. data/lib/kontena/cli/certificate/show_command.rb +22 -0
  36. data/lib/kontena/cli/certificate_command.rb +18 -0
  37. data/lib/kontena/cli/cloud/login_command.rb +186 -0
  38. data/lib/kontena/cli/cloud/logout_command.rb +14 -0
  39. data/lib/kontena/cli/cloud/master/add_command.rb +156 -0
  40. data/lib/kontena/cli/cloud/master/list_command.rb +35 -0
  41. data/lib/kontena/cli/cloud/master/remove_command.rb +68 -0
  42. data/lib/kontena/cli/cloud/master/show_command.rb +21 -0
  43. data/lib/kontena/cli/cloud/master/update_command.rb +52 -0
  44. data/lib/kontena/cli/cloud/master_command.rb +14 -0
  45. data/lib/kontena/cli/cloud_command.rb +13 -0
  46. data/lib/kontena/cli/common.rb +360 -0
  47. data/lib/kontena/cli/config.rb +662 -0
  48. data/lib/kontena/cli/container_command.rb +10 -0
  49. data/lib/kontena/cli/containers/exec_command.rb +31 -0
  50. data/lib/kontena/cli/containers/inspect_command.rb +16 -0
  51. data/lib/kontena/cli/containers/list_command.rb +51 -0
  52. data/lib/kontena/cli/containers/logs_command.rb +19 -0
  53. data/lib/kontena/cli/etcd/common.rb +8 -0
  54. data/lib/kontena/cli/etcd/get_command.rb +26 -0
  55. data/lib/kontena/cli/etcd/health_command.rb +53 -0
  56. data/lib/kontena/cli/etcd/list_command.rb +36 -0
  57. data/lib/kontena/cli/etcd/mkdir_command.rb +23 -0
  58. data/lib/kontena/cli/etcd/remove_command.rb +29 -0
  59. data/lib/kontena/cli/etcd/set_command.rb +24 -0
  60. data/lib/kontena/cli/etcd_command.rb +12 -0
  61. data/lib/kontena/cli/external_registries/add_command.rb +25 -0
  62. data/lib/kontena/cli/external_registries/list_command.rb +23 -0
  63. data/lib/kontena/cli/external_registries/remove_command.rb +17 -0
  64. data/lib/kontena/cli/external_registry_command.rb +9 -0
  65. data/lib/kontena/cli/grid_command.rb +21 -0
  66. data/lib/kontena/cli/grid_options.rb +12 -0
  67. data/lib/kontena/cli/grids/audit_log_command.rb +22 -0
  68. data/lib/kontena/cli/grids/cloud_config_command.rb +53 -0
  69. data/lib/kontena/cli/grids/common.rb +182 -0
  70. data/lib/kontena/cli/grids/create_command.rb +48 -0
  71. data/lib/kontena/cli/grids/current_command.rb +25 -0
  72. data/lib/kontena/cli/grids/env_command.rb +32 -0
  73. data/lib/kontena/cli/grids/events_command.rb +50 -0
  74. data/lib/kontena/cli/grids/health_command.rb +69 -0
  75. data/lib/kontena/cli/grids/list_command.rb +59 -0
  76. data/lib/kontena/cli/grids/logs_command.rb +35 -0
  77. data/lib/kontena/cli/grids/remove_command.rb +31 -0
  78. data/lib/kontena/cli/grids/show_command.rb +25 -0
  79. data/lib/kontena/cli/grids/trusted_subnet_command.rb +10 -0
  80. data/lib/kontena/cli/grids/trusted_subnets/add_command.rb +18 -0
  81. data/lib/kontena/cli/grids/trusted_subnets/list_command.rb +18 -0
  82. data/lib/kontena/cli/grids/trusted_subnets/remove_command.rb +26 -0
  83. data/lib/kontena/cli/grids/update_command.rb +35 -0
  84. data/lib/kontena/cli/grids/use_command.rb +26 -0
  85. data/lib/kontena/cli/grids/user_command.rb +9 -0
  86. data/lib/kontena/cli/grids/users/add_command.rb +18 -0
  87. data/lib/kontena/cli/grids/users/list_command.rb +20 -0
  88. data/lib/kontena/cli/grids/users/remove_command.rb +20 -0
  89. data/lib/kontena/cli/helpers/exec_helper.rb +209 -0
  90. data/lib/kontena/cli/helpers/health_helper.rb +65 -0
  91. data/lib/kontena/cli/helpers/log_helper.rb +113 -0
  92. data/lib/kontena/cli/helpers/time_helper.rb +29 -0
  93. data/lib/kontena/cli/localhost_web_server.rb +113 -0
  94. data/lib/kontena/cli/log_formatters/compact.rb +65 -0
  95. data/lib/kontena/cli/log_formatters/strip_color.rb +13 -0
  96. data/lib/kontena/cli/logout_command.rb +10 -0
  97. data/lib/kontena/cli/master/audit_log_command.rb +19 -0
  98. data/lib/kontena/cli/master/config/export_command.rb +46 -0
  99. data/lib/kontena/cli/master/config/get_command.rb +26 -0
  100. data/lib/kontena/cli/master/config/import_command.rb +67 -0
  101. data/lib/kontena/cli/master/config/set_command.rb +19 -0
  102. data/lib/kontena/cli/master/config/unset_command.rb +20 -0
  103. data/lib/kontena/cli/master/config_command.rb +17 -0
  104. data/lib/kontena/cli/master/create_command.rb +74 -0
  105. data/lib/kontena/cli/master/current_command.rb +25 -0
  106. data/lib/kontena/cli/master/init_cloud_command.rb +45 -0
  107. data/lib/kontena/cli/master/join_command.rb +22 -0
  108. data/lib/kontena/cli/master/list_command.rb +24 -0
  109. data/lib/kontena/cli/master/login_command.rb +331 -0
  110. data/lib/kontena/cli/master/logout_command.rb +25 -0
  111. data/lib/kontena/cli/master/remove_command.rb +55 -0
  112. data/lib/kontena/cli/master/ssh_command.rb +72 -0
  113. data/lib/kontena/cli/master/token/common.rb +29 -0
  114. data/lib/kontena/cli/master/token/create_command.rb +50 -0
  115. data/lib/kontena/cli/master/token/current_command.rb +45 -0
  116. data/lib/kontena/cli/master/token/list_command.rb +39 -0
  117. data/lib/kontena/cli/master/token/remove_command.rb +19 -0
  118. data/lib/kontena/cli/master/token/show_command.rb +34 -0
  119. data/lib/kontena/cli/master/token_command.rb +13 -0
  120. data/lib/kontena/cli/master/use_command.rb +31 -0
  121. data/lib/kontena/cli/master/user/invite_command.rb +51 -0
  122. data/lib/kontena/cli/master/user/list_command.rb +29 -0
  123. data/lib/kontena/cli/master/user/remove_command.rb +24 -0
  124. data/lib/kontena/cli/master/user/role/add_command.rb +29 -0
  125. data/lib/kontena/cli/master/user/role/remove_command.rb +27 -0
  126. data/lib/kontena/cli/master/user/role_command.rb +6 -0
  127. data/lib/kontena/cli/master/user_command.rb +9 -0
  128. data/lib/kontena/cli/master_command.rb +21 -0
  129. data/lib/kontena/cli/node_command.rb +17 -0
  130. data/lib/kontena/cli/nodes/create_command.rb +25 -0
  131. data/lib/kontena/cli/nodes/env_command.rb +37 -0
  132. data/lib/kontena/cli/nodes/health_command.rb +47 -0
  133. data/lib/kontena/cli/nodes/label_command.rb +10 -0
  134. data/lib/kontena/cli/nodes/labels/add_command.rb +18 -0
  135. data/lib/kontena/cli/nodes/labels/list_command.rb +19 -0
  136. data/lib/kontena/cli/nodes/labels/remove_command.rb +32 -0
  137. data/lib/kontena/cli/nodes/list_command.rb +97 -0
  138. data/lib/kontena/cli/nodes/remove_command.rb +34 -0
  139. data/lib/kontena/cli/nodes/reset_token_command.rb +34 -0
  140. data/lib/kontena/cli/nodes/show_command.rb +56 -0
  141. data/lib/kontena/cli/nodes/ssh_command.rb +63 -0
  142. data/lib/kontena/cli/nodes/update_command.rb +31 -0
  143. data/lib/kontena/cli/plugin_command.rb +12 -0
  144. data/lib/kontena/cli/plugins/common.rb +8 -0
  145. data/lib/kontena/cli/plugins/install_command.rb +42 -0
  146. data/lib/kontena/cli/plugins/list_command.rb +31 -0
  147. data/lib/kontena/cli/plugins/search_command.rb +25 -0
  148. data/lib/kontena/cli/plugins/show_command.rb +17 -0
  149. data/lib/kontena/cli/plugins/uninstall_command.rb +31 -0
  150. data/lib/kontena/cli/plugins/upgrade_command.rb +60 -0
  151. data/lib/kontena/cli/registry/create_command.rb +151 -0
  152. data/lib/kontena/cli/registry/remove_command.rb +21 -0
  153. data/lib/kontena/cli/registry_command.rb +8 -0
  154. data/lib/kontena/cli/service_command.rb +28 -0
  155. data/lib/kontena/cli/services/container_command.rb +8 -0
  156. data/lib/kontena/cli/services/containers_command.rb +39 -0
  157. data/lib/kontena/cli/services/create_command.rb +105 -0
  158. data/lib/kontena/cli/services/deploy_command.rb +25 -0
  159. data/lib/kontena/cli/services/env_command.rb +9 -0
  160. data/lib/kontena/cli/services/envs/add_command.rb +21 -0
  161. data/lib/kontena/cli/services/envs/list_command.rb +22 -0
  162. data/lib/kontena/cli/services/envs/remove_command.rb +22 -0
  163. data/lib/kontena/cli/services/events_command.rb +36 -0
  164. data/lib/kontena/cli/services/exec_command.rb +107 -0
  165. data/lib/kontena/cli/services/link_command.rb +35 -0
  166. data/lib/kontena/cli/services/list_command.rb +66 -0
  167. data/lib/kontena/cli/services/logs_command.rb +33 -0
  168. data/lib/kontena/cli/services/monitor_command.rb +58 -0
  169. data/lib/kontena/cli/services/remove_command.rb +60 -0
  170. data/lib/kontena/cli/services/restart_command.rb +19 -0
  171. data/lib/kontena/cli/services/scale_command.rb +21 -0
  172. data/lib/kontena/cli/services/secret_command.rb +8 -0
  173. data/lib/kontena/cli/services/secrets/link_command.rb +26 -0
  174. data/lib/kontena/cli/services/secrets/unlink_command.rb +28 -0
  175. data/lib/kontena/cli/services/services_helper.rb +579 -0
  176. data/lib/kontena/cli/services/show_command.rb +26 -0
  177. data/lib/kontena/cli/services/start_command.rb +21 -0
  178. data/lib/kontena/cli/services/stats_command.rb +87 -0
  179. data/lib/kontena/cli/services/stop_command.rb +21 -0
  180. data/lib/kontena/cli/services/unlink_command.rb +30 -0
  181. data/lib/kontena/cli/services/update_command.rb +94 -0
  182. data/lib/kontena/cli/spinner.rb +205 -0
  183. data/lib/kontena/cli/stack_command.rb +21 -0
  184. data/lib/kontena/cli/stacks/build_command.rb +125 -0
  185. data/lib/kontena/cli/stacks/common.rb +209 -0
  186. data/lib/kontena/cli/stacks/deploy_command.rb +37 -0
  187. data/lib/kontena/cli/stacks/events_command.rb +33 -0
  188. data/lib/kontena/cli/stacks/inspect_command.rb +17 -0
  189. data/lib/kontena/cli/stacks/install_command.rb +95 -0
  190. data/lib/kontena/cli/stacks/label_command.rb +10 -0
  191. data/lib/kontena/cli/stacks/labels/add_command.rb +21 -0
  192. data/lib/kontena/cli/stacks/labels/common.rb +19 -0
  193. data/lib/kontena/cli/stacks/labels/list_command.rb +21 -0
  194. data/lib/kontena/cli/stacks/labels/remove_command.rb +21 -0
  195. data/lib/kontena/cli/stacks/list_command.rb +154 -0
  196. data/lib/kontena/cli/stacks/logs_command.rb +35 -0
  197. data/lib/kontena/cli/stacks/monitor_command.rb +93 -0
  198. data/lib/kontena/cli/stacks/registry/create_command.rb +24 -0
  199. data/lib/kontena/cli/stacks/registry/make_private_command.rb +24 -0
  200. data/lib/kontena/cli/stacks/registry/make_public_command.rb +24 -0
  201. data/lib/kontena/cli/stacks/registry/pull_command.rb +28 -0
  202. data/lib/kontena/cli/stacks/registry/push_command.rb +40 -0
  203. data/lib/kontena/cli/stacks/registry/remove_command.rb +30 -0
  204. data/lib/kontena/cli/stacks/registry/search_command.rb +42 -0
  205. data/lib/kontena/cli/stacks/registry/show_command.rb +65 -0
  206. data/lib/kontena/cli/stacks/registry_command.rb +12 -0
  207. data/lib/kontena/cli/stacks/remove_command.rb +80 -0
  208. data/lib/kontena/cli/stacks/restart_command.rb +24 -0
  209. data/lib/kontena/cli/stacks/service_generator.rb +131 -0
  210. data/lib/kontena/cli/stacks/service_generator_v2.rb +27 -0
  211. data/lib/kontena/cli/stacks/show_command.rb +168 -0
  212. data/lib/kontena/cli/stacks/stack_name.rb +71 -0
  213. data/lib/kontena/cli/stacks/stacks_helper.rb +83 -0
  214. data/lib/kontena/cli/stacks/stop_command.rb +24 -0
  215. data/lib/kontena/cli/stacks/upgrade_command.rb +264 -0
  216. data/lib/kontena/cli/stacks/validate_command.rb +75 -0
  217. data/lib/kontena/cli/stacks/yaml/custom_validators/affinities_validator.rb +19 -0
  218. data/lib/kontena/cli/stacks/yaml/custom_validators/build_validator.rb +22 -0
  219. data/lib/kontena/cli/stacks/yaml/custom_validators/certificates_validator.rb +22 -0
  220. data/lib/kontena/cli/stacks/yaml/custom_validators/extends_validator.rb +22 -0
  221. data/lib/kontena/cli/stacks/yaml/custom_validators/hooks_validator.rb +102 -0
  222. data/lib/kontena/cli/stacks/yaml/custom_validators/secrets_validator.rb +22 -0
  223. data/lib/kontena/cli/stacks/yaml/opto/certificates_resolver.rb +37 -0
  224. data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +78 -0
  225. data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +25 -0
  226. data/lib/kontena/cli/stacks/yaml/opto/service_link_resolver.rb +80 -0
  227. data/lib/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver.rb +39 -0
  228. data/lib/kontena/cli/stacks/yaml/opto/vault_resolver.rb +13 -0
  229. data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +12 -0
  230. data/lib/kontena/cli/stacks/yaml/opto.rb +16 -0
  231. data/lib/kontena/cli/stacks/yaml/reader.rb +525 -0
  232. data/lib/kontena/cli/stacks/yaml/service_extender.rb +65 -0
  233. data/lib/kontena/cli/stacks/yaml/stack_file_loader/file_loader.rb +41 -0
  234. data/lib/kontena/cli/stacks/yaml/stack_file_loader/registry_loader.rb +24 -0
  235. data/lib/kontena/cli/stacks/yaml/stack_file_loader/uri_loader.rb +23 -0
  236. data/lib/kontena/cli/stacks/yaml/stack_file_loader.rb +152 -0
  237. data/lib/kontena/cli/stacks/yaml/validations.rb +119 -0
  238. data/lib/kontena/cli/stacks/yaml/validator_v3.rb +164 -0
  239. data/lib/kontena/cli/subcommand_loader.rb +83 -0
  240. data/lib/kontena/cli/table_generator.rb +128 -0
  241. data/lib/kontena/cli/vault/export_command.rb +24 -0
  242. data/lib/kontena/cli/vault/import_command.rb +75 -0
  243. data/lib/kontena/cli/vault/list_command.rb +37 -0
  244. data/lib/kontena/cli/vault/read_command.rb +27 -0
  245. data/lib/kontena/cli/vault/remove_command.rb +23 -0
  246. data/lib/kontena/cli/vault/update_command.rb +24 -0
  247. data/lib/kontena/cli/vault/write_command.rb +23 -0
  248. data/lib/kontena/cli/vault_command.rb +13 -0
  249. data/lib/kontena/cli/version.rb +10 -0
  250. data/lib/kontena/cli/version_command.rb +20 -0
  251. data/lib/kontena/cli/volume_command.rb +9 -0
  252. data/lib/kontena/cli/volumes/create_command.rb +42 -0
  253. data/lib/kontena/cli/volumes/list_command.rb +29 -0
  254. data/lib/kontena/cli/volumes/remove_command.rb +29 -0
  255. data/lib/kontena/cli/volumes/show_command.rb +38 -0
  256. data/lib/kontena/cli/vpn/config_command.rb +27 -0
  257. data/lib/kontena/cli/vpn/create_command.rb +99 -0
  258. data/lib/kontena/cli/vpn/remove_command.rb +22 -0
  259. data/lib/kontena/cli/vpn_command.rb +9 -0
  260. data/lib/kontena/cli/whoami_command.rb +38 -0
  261. data/lib/kontena/client.rb +574 -0
  262. data/lib/kontena/command.rb +251 -0
  263. data/lib/kontena/debug_instrumentor.rb +80 -0
  264. data/lib/kontena/errors.rb +50 -0
  265. data/lib/kontena/light_prompt.rb +103 -0
  266. data/lib/kontena/machine/cert_helper.rb +43 -0
  267. data/lib/kontena/machine/cloud_config/cloudinit.yml +82 -0
  268. data/lib/kontena/machine/cloud_config/node_generator.rb +28 -0
  269. data/lib/kontena/machine/common.rb +17 -0
  270. data/lib/kontena/machine/random_name.rb +42 -0
  271. data/lib/kontena/main_command.rb +66 -0
  272. data/lib/kontena/plugin_manager/cleaner.rb +33 -0
  273. data/lib/kontena/plugin_manager/common.rb +89 -0
  274. data/lib/kontena/plugin_manager/installer.rb +78 -0
  275. data/lib/kontena/plugin_manager/loader.rb +93 -0
  276. data/lib/kontena/plugin_manager/rubygems_client.rb +59 -0
  277. data/lib/kontena/plugin_manager/uninstaller.rb +34 -0
  278. data/lib/kontena/plugin_manager.rb +26 -0
  279. data/lib/kontena/presets/github_auth_provider.yml +11 -0
  280. data/lib/kontena/presets/kontena_auth_provider.yml +11 -0
  281. data/lib/kontena/scripts/completer +9 -0
  282. data/lib/kontena/scripts/completer.rb +334 -0
  283. data/lib/kontena/scripts/init +18 -0
  284. data/lib/kontena/scripts/kontena.zsh +11 -0
  285. data/lib/kontena/scripts/krates.bash +8 -0
  286. data/lib/kontena/stacks/change_resolver.rb +118 -0
  287. data/lib/kontena/stacks/stack_data.rb +58 -0
  288. data/lib/kontena/stacks/stack_data_set.rb +51 -0
  289. data/lib/kontena/stacks_cache.rb +110 -0
  290. data/lib/kontena/stacks_client.rb +177 -0
  291. data/lib/kontena/util.rb +116 -0
  292. data/lib/kontena_cli.rb +190 -0
  293. 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