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,152 @@
1
+ require_relative '../stack_name'
2
+ require 'yaml'
3
+ require_relative 'reader'
4
+
5
+ module Kontena::Cli::Stacks
6
+ module YAML
7
+ class StackFileLoader
8
+ # A base class for loading stack files. You can define more loaders by
9
+ # inheriting from this class.
10
+ #
11
+ # The purpose of StackFileLoader is to provide a generic interface for
12
+ # loading stack YAML's from different sources, such as local files,
13
+ # stack registry or URLs
14
+
15
+ def self.inherited(where)
16
+ loaders << where
17
+ end
18
+
19
+ def self.loaders
20
+ @loaders ||= []
21
+ end
22
+
23
+ # The main interface for getting a new loader
24
+ #
25
+ # @param source [String] stack file source string (filename, url, ..)
26
+ # @param parent [StackFileLoader] define a parent for recursion
27
+ # @return [StackFileLoader]
28
+ def self.for(source, parent = nil)
29
+ loader = loaders.find { |l| l.match?(source, parent) }
30
+ if loader.nil?
31
+ raise RuntimeError, "Not found: no such file #{source} or invalid uri scheme"
32
+ end
33
+ loader.new(source, parent)
34
+ end
35
+
36
+ attr_reader :source, :parent
37
+
38
+ # @param source [String] stack file source string (filename, url, ..)
39
+ # @param parent [StackFileLoader] define a parent for recursion
40
+ # @return [StackFileLoader]
41
+ def initialize(source, parent = nil)
42
+ @source = source
43
+ @parent = parent
44
+ end
45
+
46
+ # @return [String] a stripped down version of inspect without all the yaml source
47
+ def inspect
48
+ "#<#{self.class.name}:#{object_id} @source=#{source.inspect} @parent=#{parent.nil? ? 'nil' : parent.source}>"
49
+ end
50
+
51
+ # @return [Hash] a hash parsed from the YAML content
52
+ def yaml
53
+ @yaml ||= ::YAML.safe_load(content, [], [], true, source)
54
+ end
55
+
56
+ # @return [String] raw file content
57
+ def content
58
+ @content ||= read_content
59
+ end
60
+
61
+ def read_content
62
+ raise "Implement in inheriting class"
63
+ end
64
+
65
+ # @return [StackName] an accessor to StackName for the target file
66
+ def stack_name
67
+ @stack_name = Kontena::Cli::Stacks::StackName.new(yaml['stack'], yaml['version'])
68
+ end
69
+
70
+ # @return [Reader] an accessor to YAML::Reader for the target file
71
+ def reader(*args)
72
+ @reader ||= Reader.new(self, *args)
73
+ end
74
+
75
+ # Builds an array of hashes that represent the dependency tree starting
76
+ # from the target file. Unless recurse is set to false, the tree will
77
+ # contain also nested dependencies from any child stacks.
78
+ #
79
+ # @param recurse [TrueClass,FalseClass] recurse child dependencies?
80
+ # @return [Array<Hash>] an array of hashes ('name', 'stack', 'variables', and 'depends')
81
+ def dependencies(recurse: true)
82
+ return @dependencies if @dependencies
83
+ if depends.nil? || depends.empty?
84
+ @dependencies = nil
85
+ else
86
+ @dependencies = depends.map do |name, dependency|
87
+ loader = StackFileLoader.for(dependency['stack'], self)
88
+ deps = { 'name' => name, 'stack' => loader.source, 'variables' => dependency.fetch('variables', Hash.new) }
89
+ if recurse
90
+ child_deps = loader.dependencies
91
+ deps['depends'] = child_deps unless child_deps.nil?
92
+ end
93
+ deps
94
+ end
95
+ end
96
+ end
97
+
98
+ def to_h
99
+ {
100
+ 'stack' => stack_name.stack_name,
101
+ :loader => self,
102
+ }
103
+ end
104
+
105
+ # Returns a non nested hash of all dependencies.
106
+ # Processes :variables hash and moves the related variables to children
107
+ #
108
+ # @param basename [String] installed stack name
109
+ # @param opts [Hash] extra data such as variable lists
110
+ # @return [Hash] { installation_name => { 'name' => installation-name, 'stack' => stack_name, :loader => self }, child_install_name => { ... } }
111
+ def flat_dependencies(basename, opts = {})
112
+ opt_variables = opts[:variables] || {}
113
+
114
+ result = {
115
+ basename => self.to_h.merge(opts).merge(
116
+ name: basename,
117
+ variables: opt_variables.reject { |k, _| k.include?('.') }
118
+ )
119
+ }
120
+
121
+ depends.each do |as_name, data|
122
+ variables = {}
123
+
124
+ opt_variables.select { |k, _| k.start_with?(as_name + '.') }.each do |k,v|
125
+ variables[k.split('.', 2).last] = v
126
+ end
127
+
128
+ data['variables'] ||= {}
129
+
130
+ loader = StackFileLoader.for(data['stack'], self)
131
+ result.merge!(
132
+ loader.flat_dependencies(
133
+ basename + '-' + as_name,
134
+ variables: data['variables'].merge(variables),
135
+ parent_name: basename
136
+ )
137
+ )
138
+ end
139
+
140
+ result
141
+ end
142
+
143
+ private
144
+
145
+ def depends
146
+ yaml['depends'] || {}
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ Dir[File.expand_path('../stack_file_loader/*.rb', __FILE__)].each { |f| require f }
@@ -0,0 +1,119 @@
1
+ module Kontena::Cli::Stacks::YAML
2
+ module Validations
3
+ module CustomValidators
4
+ require_relative 'custom_validators/affinities_validator'
5
+ require_relative 'custom_validators/build_validator'
6
+ require_relative 'custom_validators/extends_validator'
7
+ require_relative 'custom_validators/hooks_validator'
8
+ require_relative 'custom_validators/secrets_validator'
9
+ require_relative 'custom_validators/certificates_validator'
10
+
11
+ def self.load
12
+ return if @loaded
13
+ HashValidator.append_validator(AffinitiesValidator.new)
14
+ HashValidator.append_validator(BuildValidator.new)
15
+ HashValidator.append_validator(ExtendsValidator.new)
16
+ HashValidator.append_validator(SecretsValidator.new)
17
+ HashValidator.append_validator(CertificatesValidator.new)
18
+ HashValidator.append_validator(HooksValidator.new)
19
+ @loaded = true
20
+ end
21
+ end
22
+
23
+ def common_validations
24
+ {
25
+ 'image' => optional('string'), # it's optional because some base yml file might contain image option
26
+ 'extends' => optional('stacks_valid_extends'),
27
+ 'stateful' => optional('boolean'),
28
+ 'affinity' => optional('stacks_valid_affinities'),
29
+ 'cap_add' => optional('array'),
30
+ 'cap_drop' => optional('array'),
31
+ 'command' => optional('string'),
32
+ 'cpus' => optional('float'),
33
+ 'cpu_shares' => optional('integer'),
34
+ 'external_links' => optional('array'),
35
+ 'mem_limit' => optional('string'),
36
+ 'mem_swaplimit' => optional('string'),
37
+ 'shm_size' => optional('string'),
38
+ 'environment' => optional(-> (value) {
39
+ if value.is_a?(Hash)
40
+ value.all? do |k,v|
41
+ k.kind_of?(String) && (
42
+ v.kind_of?(String) ||
43
+ v.kind_of?(Integer) ||
44
+ v.kind_of?(TrueClass) ||
45
+ v.kind_of?(FalseClass) ||
46
+ v.nil?
47
+ )
48
+ end
49
+ elsif value.is_a?(Array)
50
+ value.all? { |v| v.kind_of?(String) && v =~ /\A[^=]+=/ }
51
+ else
52
+ false
53
+ end
54
+ }),
55
+ 'env_file' => optional(-> (value) { value.is_a?(String) || value.is_a?(Array) }),
56
+ 'instances' => optional('integer'),
57
+ 'links' => optional(-> (value) { value.is_a?(Array) || value.nil? }),
58
+ 'ports' => optional(-> (value) { value.is_a?(Array) && value.all? { |v| v.is_a?(String) && v.match(/\A(\d+\.\d+\.\d+\.\d+)?:?(\d+)\:(\d+)\/?(\w+)?\z/) } }),
59
+ 'pid' => optional('string'),
60
+ 'privileged' => optional('boolean'),
61
+ 'user' => optional('string'),
62
+ 'volumes' => optional('array'),
63
+ 'volumes_from' => optional('array'),
64
+ 'secrets' => optional('stacks_valid_secrets'),
65
+ 'certificates' => optional('stacks_valid_certificates'),
66
+ 'hooks' => optional('stacks_valid_hooks'),
67
+ 'only_if' => optional(-> (value) { value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array) }),
68
+ 'skip_if' => optional(-> (value) { value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array) }),
69
+ 'deploy' => optional({
70
+ 'strategy' => optional(%w(ha daemon random)),
71
+ 'wait_for_port' => optional('integer'),
72
+ 'min_health' => optional('float'),
73
+ 'interval' => optional(/^\d+(min|h|d|)$/)
74
+ }),
75
+ 'health_check' => optional({
76
+ 'protocol' => /^(http|tcp)$/,
77
+ 'port' => 'integer',
78
+ 'uri' => optional(/\/[\S]*/),
79
+ 'timeout' => optional('integer'),
80
+ 'interval' => optional('integer'),
81
+ 'initial_delay' => optional('integer')
82
+ }),
83
+ 'stop_signal' => optional('string'),
84
+ 'stop_grace_period' => optional(/(\d+(?:\.\d+)?)([hms])/),
85
+ 'read_only' => optional('boolean'),
86
+ 'entrypoint' => optional('string')
87
+ }
88
+ end
89
+
90
+ def optional(type)
91
+ HashValidator.optional(type)
92
+ end
93
+
94
+ def validate_options(service_config)
95
+ HashValidator.validate(service_config, @schema, true)
96
+ end
97
+
98
+ def validate_volume_options(volume_config)
99
+ HashValidator.validate(volume_config, volume_schema, true)
100
+ end
101
+
102
+ def validate_dependencies(dependency_config)
103
+ HashValidator.validate(dependency_config, dependency_schema, true)
104
+ end
105
+
106
+ def volume_schema
107
+ {
108
+ 'external' => optional(-> (value) { value.is_a?(TrueClass) || (value.is_a?(Hash) && value['name'].is_a?(String)) })
109
+ }
110
+ end
111
+
112
+ def dependency_schema
113
+ {
114
+ 'stack' => optional('string'),
115
+ 'variables' => optional(-> (value) { value.is_a?(Hash) })
116
+ }
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,164 @@
1
+ require 'hash_validator'
2
+
3
+ module Kontena::Cli::Stacks
4
+ module YAML
5
+ class ValidatorV3
6
+ require_relative 'validations'
7
+ include Validations
8
+
9
+ KNOWN_TOP_LEVEL_KEYS = %i(
10
+ services
11
+ errors
12
+ volumes
13
+ networks
14
+ variables
15
+ stack
16
+ version
17
+ data
18
+ description
19
+ expose
20
+ depends
21
+ labels
22
+ )
23
+
24
+ def initialize
25
+ @schema = common_validations
26
+ @schema['build'] = optional('stacks_valid_build')
27
+ @schema['depends_on'] = optional('array')
28
+ @schema['network_mode'] = optional(%w(host bridge))
29
+ @schema['logging'] = optional({
30
+ 'driver' => optional('string'),
31
+ 'options' => optional(-> (value) { value.kind_of?(Hash) })
32
+ })
33
+ Validations::CustomValidators.load
34
+ end
35
+
36
+ # borrowed from server/app/helpers/volumes_helpers.rb
37
+ def parse_volume(vol)
38
+ elements = vol.split(':')
39
+ if elements.size >= 2 # Bind mount or volume used
40
+ if elements[0].start_with?('/') && elements[1] && elements[1].start_with?('/') # Bind mount
41
+ {bind_mount: elements[0], path: elements[1], flags: elements[2..-1].join(',')}
42
+ elsif !elements[0].start_with?('/') && elements[1].start_with?('/') # Real volume
43
+ {volume: elements[0], path: elements[1], flags: elements[2..-1].join(',')}
44
+ else
45
+ {error: "volume definition not in right format: #{vol}" }
46
+ end
47
+ elsif elements.size == 1 && elements[0].start_with?('/') # anon volume
48
+ {bind_mount: nil, path: elements[0], flags: nil} # anon vols do not support flags
49
+ else
50
+ {error: "volume definition not in right format: #{vol}" }
51
+ end
52
+ end
53
+
54
+ ##
55
+ # @param [Hash] yaml
56
+ # @param [TrueClass|FalseClass] strict
57
+ # @return [Array] validation_errors
58
+ def validate(yaml)
59
+ result = {
60
+ errors: [],
61
+ notifications: []
62
+ }
63
+
64
+ yaml.keys.each do |key|
65
+ unless KNOWN_TOP_LEVEL_KEYS.include?(key) || KNOWN_TOP_LEVEL_KEYS.include?(key.to_sym)
66
+ result[:notifications] << { key.to_s => "unknown top level key" }
67
+ end
68
+ end
69
+
70
+ if yaml.key?('stack')
71
+ unless yaml['stack'] =~ /\A(?:.+?\/)?(?!-)[a-z0-9\-]+\z/
72
+ result[:notifications] << { 'stack' => 'A stack name should only include a-z, 0-9 and - characters and not start with the - character' }
73
+ end
74
+ end
75
+
76
+ if yaml.key?('services')
77
+ if yaml['services'].kind_of?(Hash)
78
+ yaml['services'].each do |service, options|
79
+ unless service =~ /\A(?!-)[a-z0-9\-]+\z/
80
+ result[:notifications] << { 'services' => { service => { 'name' => 'A service name should only include a-z, 0-9 and - characters and not start with the - character' } } }
81
+ end
82
+ unless options.kind_of?(Hash)
83
+ result[:errors] << { 'services' => { service => { 'options' => "must be a mapping not a #{options.class}"} } }
84
+ next
85
+ end
86
+ option_errors = validate_options(options)
87
+ result[:errors] << { 'services' => { service => option_errors.errors } } unless option_errors.valid?
88
+ if options['volumes']
89
+ mount_path_occurences = Hash.new(0)
90
+ options['volumes'].each do |volume|
91
+ parsed = parse_volume(volume)
92
+ if parsed[:error]
93
+ result[:errors] << { 'services' => { service => { 'volumes' => { volume => parsed[:error] } } } }
94
+ elsif parsed[:path]
95
+ mount_path_occurences[parsed[:path]] += 1
96
+ volume_name = parsed[:volume]
97
+ if volume_name && !volume_name.start_with?('/')
98
+ if yaml.key?('volumes')
99
+ unless yaml['volumes'][volume_name]
100
+ result[:errors] << { 'services' => { service => { 'volumes' => { volume_name => 'not found in top level volumes list' } } } }
101
+ end
102
+ else
103
+ result[:errors] << { 'services' => { service => { 'volumes' => { volume => 'defines volume name, but file does not contain volumes definitions' } } } }
104
+ end
105
+ end
106
+ else
107
+ result[:errors] << { 'services' => { service => { 'volumes' => { volume => 'mount point missing' } } } }
108
+ end
109
+ end
110
+ mount_path_occurences.select {|path, occurences| occurences > 1 }.each do |path, occurences|
111
+ result[:errors] << { 'services' => { service => { 'volumes' => { path => "mount point defined #{occurences} times" } } } }
112
+ end
113
+ end
114
+ end
115
+ else
116
+ result[:errors] << { 'services' => "must be a mapping, not #{yaml['services'].class}" }
117
+ end
118
+ else
119
+ result[:notifications] << { 'file' => 'does not define any services' }
120
+ end
121
+
122
+ if yaml.key?('volumes')
123
+ if yaml['volumes'].kind_of?(Hash)
124
+ yaml['volumes'].each do |volume, options|
125
+ if options.kind_of?(Hash)
126
+ option_errors = validate_volume_options(options)
127
+ unless option_errors.valid?
128
+ result[:errors] << { 'volumes' => { volume => option_errors.errors } }
129
+ end
130
+ else
131
+ result[:errors] << { 'volumes' => { volume => { 'options' => "must be a mapping, not #{options.class}" } } }
132
+ end
133
+ end
134
+ else
135
+ result[:errors] << { 'volumes' => "must be a mapping, not #{yaml['volumes'].class}" }
136
+ end
137
+ end
138
+
139
+ if yaml.key?('networks')
140
+ result[:notifications] << { 'networks' => 'Kontena does not support multiple networks yet. You can reference services with Kontena\'s internal DNS (service_name.kontena.local)' }
141
+ end
142
+
143
+ if (yaml['volumes'].nil? || yaml['volumes'].empty?) && (yaml['services'].nil? || yaml['services'].empty?)
144
+ result[:errors] << { 'file' => 'does not list any services or volumes' }
145
+ end
146
+
147
+ if yaml.key?('depends')
148
+ unless yaml['depends'].kind_of?(Hash)
149
+ result[:errors] << { 'depends' => "Must be a mapping, not #{yaml['depends'].class}" }
150
+ end
151
+
152
+ yaml['depends'].each do |name, dependency_options|
153
+ validator = validate_dependencies(dependency_options)
154
+ result[:errors] << { 'depends' => { name => validator.errors } } unless validator.valid?
155
+ if yaml.key?('services') && yaml['services'][name]
156
+ result[:errors] << { 'depends' => { name => 'is defined both as service and dependency name' } }
157
+ end
158
+ end
159
+ end
160
+ result
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,83 @@
1
+ require 'kontena_cli'
2
+ module Kontena::Cli
3
+ class SubcommandLoader
4
+ attr_reader :path
5
+
6
+ # Create a subcommand loader instance
7
+ #
8
+ # @param [String] path path to command definition
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ # Takes something like /foo/bar/cli/master/foo_coimmand and returns [:Master, :FooCommand]
14
+ #
15
+ # @param path [String]
16
+ # @return [Array<Symbol>]
17
+ def symbolize_path(path)
18
+ path.gsub(/.*\/cli\//, '').split('/').map do |path_part|
19
+ path_part.split('_').map{ |e| e.capitalize }.join
20
+ end.map(&:to_sym)
21
+ end
22
+
23
+ # Takes an array such as [:Foo] or [:Cli, :Foo] and returns [:Kontena, :Cli, :Foo]
24
+ def prepend_kontena_cli(tree)
25
+ [:Kontena, :Cli] + (tree - [:Cli])
26
+ end
27
+
28
+ # Takes an array such as [:Master, :FooCommand] and returns Master::FooCommand
29
+ #
30
+ # @param tree [Array<Symbol]
31
+ # @return [Class]
32
+ def const_get_tree(tree)
33
+ if tree.size == 1
34
+ Object.const_get(tree.first)
35
+ else
36
+ tree[1..-1].inject(Object.const_get(tree.first)) { |new_base, part| new_base.const_get(part) }
37
+ end
38
+ rescue
39
+ raise ArgumentError, "Can't figure out command class name from path #{path} - tried #{tree}"
40
+ end
41
+
42
+ # Tries to require a file, returns false instead of raising LoadError unless succesful
43
+ #
44
+ # @param path [String]
45
+ # @return [TrueClass,FalseClass]
46
+ def safe_require(path)
47
+ require path
48
+ true
49
+ rescue LoadError
50
+ false
51
+ end
52
+
53
+ def klass
54
+ return @subcommand_class if @subcommand_class
55
+ unless safe_require(path) || safe_require(Kontena.cli_root(path))
56
+ raise ArgumentError, "Can't load #{path} or #{Kontena.cli_root(path)}"
57
+ end
58
+ @subcommand_class = const_get_tree(prepend_kontena_cli(symbolize_path(path)))
59
+ end
60
+
61
+ def new(*args)
62
+ klass.new(*args)
63
+ end
64
+
65
+ def method_missing(meth, *args)
66
+ klass.send(meth, *args)
67
+ end
68
+
69
+ def respond_to_missing?(meth)
70
+ klass.respond_to?(meth)
71
+ end
72
+
73
+ def const_get(const)
74
+ klass.const_get(const)
75
+ end
76
+
77
+ def const_defined?(const)
78
+ klass.const_defined?(const)
79
+ end
80
+
81
+ alias_method :class, :klass
82
+ end
83
+ end
@@ -0,0 +1,128 @@
1
+ require 'tty-table'
2
+
3
+ module Kontena
4
+ module Cli
5
+ class TableGenerator
6
+
7
+ attr_reader :data
8
+ attr_reader :fields
9
+ attr_reader :header
10
+ attr_reader :row_format_proc, :header_format_proc, :render_options, :render_mode
11
+
12
+ DEFAULT_HEADER_FORMAT_PROC = lambda { |header| header.to_s.capitalize }
13
+
14
+ DEFAULT_RENDER_OPTS = {padding: [0,2,0,0]}
15
+
16
+ module Helper
17
+ def self.included(base)
18
+ if base.respond_to?(:option)
19
+ base.option ['-q', '--quiet'], :flag, "Output the identifying column only"
20
+ end
21
+ end
22
+
23
+ def table_generator
24
+ Kontena::Cli::TableGenerator
25
+ end
26
+
27
+ def generate_table(array, fields = nil, &block)
28
+ fields ||= self.fields if self.respond_to?(:fields)
29
+ table_generator.new(
30
+ array,
31
+ fields,
32
+ row_format_proc: block_given? ? block.to_proc : nil,
33
+ header_format_proc: lambda { |item| pastel.bold(item.to_s.upcase) },
34
+ render_options: self.respond_to?(:render_options) ? DEFAULT_RENDER_OPTS.merge(self.render_options) : DEFAULT_RENDER_OPTS
35
+ ).render
36
+ end
37
+
38
+ def print_table(array, fields = nil, &block)
39
+ output = generate_table(array, fields, &block)
40
+ puts output unless output.strip.empty?
41
+ end
42
+ end
43
+
44
+ # @param data [Array<Hash>,Array<Array>] an array of hashes or arrays
45
+ # @param fields [Array] an array of field names found in the data hashes.
46
+ # @param fields [Hash] a hash of field_title => field_name_in_the_data_hash, for example 'Users' => 'user_count'
47
+ # @param fields [NilClass] try to auto detect fields (all fields!) from the data hashes
48
+ # @return [TTY::Table]
49
+ def initialize(data, fields = nil, row_format_proc: nil, header_format_proc: nil, render_options: nil)
50
+ @data = data
51
+ @render_options = render_options || { }
52
+ @render_mode = @render_options.delete(:mode) || :basic
53
+ @row_format_proc = row_format_proc
54
+ @header_format_proc = header_format_proc || DEFAULT_HEADER_FORMAT_PROC
55
+ @fields = parse_fields(fields)
56
+ @header = generate_header(fields || @fields)
57
+ end
58
+
59
+ def create_table(header, rows)
60
+ TTY::Table.new(
61
+ header: header,
62
+ rows: rows
63
+ )
64
+ end
65
+
66
+ def table
67
+ create_table(header, rows)
68
+ end
69
+
70
+ def render
71
+ if data.empty?
72
+ fields.size > 1 ? fields.map(&method(:format_header_item)).join(' ') : ''
73
+ else
74
+ table.render(render_mode, render_options).gsub(/\s+$/, '')
75
+ end
76
+ end
77
+
78
+ def format_row(row)
79
+ return row if row_format_proc.nil?
80
+ row_clone = row.dup
81
+ row_format_proc.call(row_clone)
82
+ row_clone
83
+ end
84
+
85
+ def format_header_item(field_name)
86
+ header_format_proc.call(field_name)
87
+ end
88
+
89
+ def rows
90
+ fields.empty? ? data.map { |row| format_row(row).map(&:values) } : data.map { |row| format_row(row).values_at(*fields) }
91
+ end
92
+
93
+ # Collect all the unique keys from the hashes if the data
94
+ # is an array of hashes.
95
+ def detect_fields
96
+ if data.first.respond_to?(:keys)
97
+ data.flat_map(&:keys).uniq
98
+ else
99
+ []
100
+ end
101
+ end
102
+
103
+ def parse_fields(fields)
104
+ if fields.nil? || fields.empty?
105
+ detect_fields
106
+ elsif fields.kind_of?(Hash)
107
+ fields.values
108
+ else
109
+ fields
110
+ end
111
+ end
112
+
113
+ def generate_header(fields)
114
+ if fields.kind_of?(Hash)
115
+ header = fields.keys
116
+ else
117
+ header = Array(fields)
118
+ end
119
+
120
+ if header.size < 2
121
+ nil
122
+ else
123
+ header.map { |head| format_header_item(head) }
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,24 @@
1
+ module Kontena::Cli::Vault
2
+ class ExportCommand < Kontena::Command
3
+ include Kontena::Cli::Common
4
+ include Kontena::Cli::GridOptions
5
+
6
+ banner "Exports secrets from Vault to STDOUT as YAML or JSON."
7
+
8
+ requires_current_master
9
+
10
+ option '--json', :flag, "Output JSON"
11
+
12
+ def execute
13
+ require 'shellwords'
14
+ require 'json'
15
+ require 'yaml'
16
+ meth = json? ? :to_json : :to_yaml
17
+ puts(
18
+ Kontena.run!(['vault', 'ls', '--return']).sort.map do |secret|
19
+ [secret, Kontena.run!(['vault', 'read', '--return', secret])]
20
+ end.to_h.send(meth)
21
+ )
22
+ end
23
+ end
24
+ end