kontena-cli 0.15.5 → 0.16.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +0 -3
  3. data/Gemfile +3 -0
  4. data/LOGO +8 -0
  5. data/VERSION +1 -1
  6. data/kontena-cli.gemspec +2 -3
  7. data/lib/kontena/callback.rb +57 -0
  8. data/lib/kontena/callbacks/.gitkeep +0 -0
  9. data/lib/kontena/callbacks/auth/01_list_and_select_grid_after_master_auth.rb +27 -0
  10. data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +20 -0
  11. data/lib/kontena/callbacks/master/deploy/01_show_logo_before_deploy.rb +15 -0
  12. data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +124 -0
  13. data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +53 -0
  14. data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +32 -0
  15. data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +49 -0
  16. data/lib/kontena/callbacks/master/deploy/90_suggest_inviting_yourself_after_deploy.rb +24 -0
  17. data/lib/kontena/cli/app_command.rb +2 -1
  18. data/lib/kontena/cli/apps/build_command.rb +1 -1
  19. data/lib/kontena/cli/apps/common.rb +6 -1
  20. data/lib/kontena/cli/apps/config_command.rb +1 -1
  21. data/lib/kontena/cli/apps/deploy_command.rb +1 -1
  22. data/lib/kontena/cli/apps/init_command.rb +3 -5
  23. data/lib/kontena/cli/apps/list_command.rb +1 -1
  24. data/lib/kontena/cli/apps/logs_command.rb +1 -1
  25. data/lib/kontena/cli/apps/monitor_command.rb +1 -1
  26. data/lib/kontena/cli/apps/remove_command.rb +2 -3
  27. data/lib/kontena/cli/apps/restart_command.rb +1 -1
  28. data/lib/kontena/cli/apps/scale_command.rb +1 -1
  29. data/lib/kontena/cli/apps/show_command.rb +1 -1
  30. data/lib/kontena/cli/apps/start_command.rb +1 -1
  31. data/lib/kontena/cli/apps/stop_command.rb +1 -1
  32. data/lib/kontena/cli/apps/yaml/reader.rb +3 -13
  33. data/lib/kontena/cli/apps/yaml/validator.rb +0 -4
  34. data/lib/kontena/cli/apps/yaml/validator_v2.rb +1 -5
  35. data/lib/kontena/cli/certificate/authorize_command.rb +1 -1
  36. data/lib/kontena/cli/certificate/get_command.rb +1 -1
  37. data/lib/kontena/cli/certificate/register_command.rb +1 -1
  38. data/lib/kontena/cli/certificate_command.rb +1 -1
  39. data/lib/kontena/cli/cloud/login_command.rb +128 -0
  40. data/lib/kontena/cli/cloud/master/add_command.rb +54 -0
  41. data/lib/kontena/cli/cloud/master/delete_command.rb +20 -0
  42. data/lib/kontena/cli/cloud/master/list_command.rb +29 -0
  43. data/lib/kontena/cli/cloud/master/show_command.rb +23 -0
  44. data/lib/kontena/cli/cloud/master/update_command.rb +58 -0
  45. data/lib/kontena/cli/cloud/master_command.rb +21 -0
  46. data/lib/kontena/cli/cloud_command.rb +10 -0
  47. data/lib/kontena/cli/common.rb +230 -88
  48. data/lib/kontena/cli/config.rb +537 -0
  49. data/lib/kontena/cli/container_command.rb +1 -1
  50. data/lib/kontena/cli/containers/exec_command.rb +1 -1
  51. data/lib/kontena/cli/containers/inspect_command.rb +1 -1
  52. data/lib/kontena/cli/etcd/get_command.rb +1 -1
  53. data/lib/kontena/cli/etcd/list_command.rb +1 -1
  54. data/lib/kontena/cli/etcd/mkdir_command.rb +1 -1
  55. data/lib/kontena/cli/etcd/remove_command.rb +1 -1
  56. data/lib/kontena/cli/etcd/set_command.rb +1 -1
  57. data/lib/kontena/cli/etcd_command.rb +1 -1
  58. data/lib/kontena/cli/external_registries/add_command.rb +1 -1
  59. data/lib/kontena/cli/external_registries/delete_command.rb +1 -1
  60. data/lib/kontena/cli/external_registries/list_command.rb +1 -1
  61. data/lib/kontena/cli/external_registries/remove_command.rb +1 -1
  62. data/lib/kontena/cli/external_registry_command.rb +1 -1
  63. data/lib/kontena/cli/grid_command.rb +1 -1
  64. data/lib/kontena/cli/grids/audit_log_command.rb +6 -5
  65. data/lib/kontena/cli/grids/cloud_config_command.rb +1 -1
  66. data/lib/kontena/cli/grids/common.rb +1 -1
  67. data/lib/kontena/cli/grids/create_command.rb +8 -4
  68. data/lib/kontena/cli/grids/current_command.rb +1 -1
  69. data/lib/kontena/cli/grids/env_command.rb +1 -1
  70. data/lib/kontena/cli/grids/list_command.rb +35 -10
  71. data/lib/kontena/cli/grids/logs_command.rb +1 -1
  72. data/lib/kontena/cli/grids/remove_command.rb +2 -2
  73. data/lib/kontena/cli/grids/show_command.rb +1 -1
  74. data/lib/kontena/cli/grids/trusted_subnet_command.rb +1 -1
  75. data/lib/kontena/cli/grids/trusted_subnets/add_command.rb +1 -1
  76. data/lib/kontena/cli/grids/trusted_subnets/list_command.rb +1 -1
  77. data/lib/kontena/cli/grids/trusted_subnets/remove_command.rb +1 -1
  78. data/lib/kontena/cli/grids/update_command.rb +1 -1
  79. data/lib/kontena/cli/grids/use_command.rb +11 -6
  80. data/lib/kontena/cli/grids/user_command.rb +1 -1
  81. data/lib/kontena/cli/grids/users/add_command.rb +1 -1
  82. data/lib/kontena/cli/grids/users/list_command.rb +1 -1
  83. data/lib/kontena/cli/grids/users/remove_command.rb +1 -1
  84. data/lib/kontena/cli/localhost_web_server.rb +93 -0
  85. data/lib/kontena/cli/login_command.rb +5 -118
  86. data/lib/kontena/cli/logout_command.rb +33 -2
  87. data/lib/kontena/cli/master/audit_log_command.rb +19 -0
  88. data/lib/kontena/cli/master/config/export_command.rb +47 -0
  89. data/lib/kontena/cli/master/config/get_command.rb +24 -0
  90. data/lib/kontena/cli/master/config/import_command.rb +69 -0
  91. data/lib/kontena/cli/master/config/set_command.rb +19 -0
  92. data/lib/kontena/cli/master/config/unset_command.rb +20 -0
  93. data/lib/kontena/cli/master/config_command.rb +24 -0
  94. data/lib/kontena/cli/master/create_command.rb +76 -0
  95. data/lib/kontena/cli/master/current_command.rb +10 -2
  96. data/lib/kontena/cli/master/join_command.rb +20 -0
  97. data/lib/kontena/cli/master/list_command.rb +4 -4
  98. data/lib/kontena/cli/master/login_command.rb +274 -0
  99. data/lib/kontena/cli/master/use_command.rb +8 -19
  100. data/lib/kontena/cli/master/users/invite_command.rb +33 -6
  101. data/lib/kontena/cli/master/users/list_command.rb +2 -2
  102. data/lib/kontena/cli/master/users/remove_command.rb +1 -1
  103. data/lib/kontena/cli/master/users/role_command.rb +1 -1
  104. data/lib/kontena/cli/master/users/roles/add_command.rb +18 -16
  105. data/lib/kontena/cli/master/users/roles/remove_command.rb +1 -1
  106. data/lib/kontena/cli/master/users_command.rb +1 -1
  107. data/lib/kontena/cli/master_command.rb +21 -1
  108. data/lib/kontena/cli/node_command.rb +1 -1
  109. data/lib/kontena/cli/nodes/label_command.rb +1 -1
  110. data/lib/kontena/cli/nodes/labels/add_command.rb +1 -1
  111. data/lib/kontena/cli/nodes/labels/remove_command.rb +1 -1
  112. data/lib/kontena/cli/nodes/list_command.rb +1 -1
  113. data/lib/kontena/cli/nodes/remove_command.rb +1 -1
  114. data/lib/kontena/cli/nodes/show_command.rb +1 -1
  115. data/lib/kontena/cli/nodes/ssh_command.rb +1 -1
  116. data/lib/kontena/cli/nodes/update_command.rb +1 -1
  117. data/lib/kontena/cli/plugin_command.rb +1 -1
  118. data/lib/kontena/cli/plugins/install_command.rb +2 -2
  119. data/lib/kontena/cli/plugins/list_command.rb +2 -2
  120. data/lib/kontena/cli/plugins/search_command.rb +1 -1
  121. data/lib/kontena/cli/plugins/uninstall_command.rb +2 -2
  122. data/lib/kontena/cli/registry/create_command.rb +2 -4
  123. data/lib/kontena/cli/registry/delete_command.rb +1 -1
  124. data/lib/kontena/cli/registry/remove_command.rb +1 -1
  125. data/lib/kontena/cli/registry_command.rb +1 -1
  126. data/lib/kontena/cli/service_command.rb +1 -1
  127. data/lib/kontena/cli/services/container_command.rb +1 -1
  128. data/lib/kontena/cli/services/containers_command.rb +1 -1
  129. data/lib/kontena/cli/services/create_command.rb +1 -1
  130. data/lib/kontena/cli/services/delete_command.rb +1 -1
  131. data/lib/kontena/cli/services/deploy_command.rb +1 -1
  132. data/lib/kontena/cli/services/env_command.rb +1 -1
  133. data/lib/kontena/cli/services/envs/add_command.rb +1 -1
  134. data/lib/kontena/cli/services/envs/list_command.rb +1 -1
  135. data/lib/kontena/cli/services/envs/remove_command.rb +1 -1
  136. data/lib/kontena/cli/services/link_command.rb +1 -1
  137. data/lib/kontena/cli/services/list_command.rb +1 -1
  138. data/lib/kontena/cli/services/logs_command.rb +1 -1
  139. data/lib/kontena/cli/services/monitor_command.rb +1 -1
  140. data/lib/kontena/cli/services/remove_command.rb +1 -1
  141. data/lib/kontena/cli/services/restart_command.rb +1 -1
  142. data/lib/kontena/cli/services/scale_command.rb +1 -1
  143. data/lib/kontena/cli/services/secret_command.rb +1 -1
  144. data/lib/kontena/cli/services/secrets/link_command.rb +1 -1
  145. data/lib/kontena/cli/services/secrets/unlink_command.rb +1 -1
  146. data/lib/kontena/cli/services/services_helper.rb +6 -3
  147. data/lib/kontena/cli/services/show_command.rb +1 -1
  148. data/lib/kontena/cli/services/start_command.rb +1 -1
  149. data/lib/kontena/cli/services/stats_command.rb +1 -1
  150. data/lib/kontena/cli/services/stop_command.rb +1 -1
  151. data/lib/kontena/cli/services/unlink_command.rb +1 -1
  152. data/lib/kontena/cli/services/update_command.rb +1 -1
  153. data/lib/kontena/cli/spinner.rb +122 -0
  154. data/lib/kontena/cli/stack_command.rb +1 -1
  155. data/lib/kontena/cli/stacks/create_command.rb +1 -1
  156. data/lib/kontena/cli/stacks/deploy_command.rb +1 -1
  157. data/lib/kontena/cli/stacks/list_command.rb +1 -1
  158. data/lib/kontena/cli/stacks/remove_command.rb +1 -1
  159. data/lib/kontena/cli/stacks/show_command.rb +1 -1
  160. data/lib/kontena/cli/stacks/update_command.rb +1 -1
  161. data/lib/kontena/cli/vault/list_command.rb +1 -1
  162. data/lib/kontena/cli/vault/read_command.rb +1 -1
  163. data/lib/kontena/cli/vault/remove_command.rb +1 -1
  164. data/lib/kontena/cli/vault/update_command.rb +1 -1
  165. data/lib/kontena/cli/vault/write_command.rb +1 -1
  166. data/lib/kontena/cli/vault_command.rb +1 -1
  167. data/lib/kontena/cli/version.rb +1 -1
  168. data/lib/kontena/cli/version_command.rb +1 -1
  169. data/lib/kontena/cli/vpn/config_command.rb +1 -1
  170. data/lib/kontena/cli/vpn/create_command.rb +2 -4
  171. data/lib/kontena/cli/vpn/delete_command.rb +1 -1
  172. data/lib/kontena/cli/vpn/remove_command.rb +1 -1
  173. data/lib/kontena/cli/vpn_command.rb +1 -1
  174. data/lib/kontena/cli/whoami_command.rb +16 -13
  175. data/lib/kontena/client.rb +410 -90
  176. data/lib/kontena/command.rb +172 -0
  177. data/lib/kontena/main_command.rb +7 -8
  178. data/lib/kontena/presets/github_auth_provider.yml +11 -0
  179. data/lib/kontena/presets/kontena_auth_provider.yml +11 -0
  180. data/lib/kontena_cli.rb +51 -1
  181. data/spec/kontena/cli/app/deploy_command_spec.rb +14 -44
  182. data/spec/kontena/cli/app/scale_spec.rb +1 -1
  183. data/spec/kontena/cli/app/yaml/reader_spec.rb +0 -48
  184. data/spec/kontena/cli/common_spec.rb +63 -59
  185. data/spec/kontena/cli/grids/use_command_spec.rb +43 -0
  186. data/spec/kontena/cli/master/current_command_spec.rb +3 -24
  187. data/spec/kontena/cli/master/use_command_spec.rb +2 -27
  188. data/spec/kontena/cli/master/users/invite_command_spec.rb +4 -18
  189. data/spec/kontena/cli/master/users/roles/add_command_spec.rb +2 -16
  190. data/spec/kontena/cli/master/users/roles/remove_command_spec.rb +2 -13
  191. data/spec/kontena/cli/services/restart_command_spec.rb +1 -1
  192. data/spec/kontena/cli/services/update_command_spec.rb +5 -5
  193. data/spec/kontena/client_spec.rb +104 -35
  194. data/spec/kontena/config_spec.rb +65 -0
  195. data/spec/spec_helper.rb +25 -3
  196. data/spec/support/client_helpers.rb +10 -3
  197. data/spec/support/requirements_helper.rb +32 -0
  198. metadata +61 -48
  199. data/lib/kontena/cli/register_command.rb +0 -23
  200. data/lib/kontena/cli/user/forgot_password_command.rb +0 -16
  201. data/lib/kontena/cli/user/reset_password_command.rb +0 -23
  202. data/lib/kontena/cli/user/verify_command.rb +0 -20
  203. data/lib/kontena/cli/user_command.rb +0 -13
  204. data/spec/fixtures/kontena-malformed-yaml.yml +0 -6
  205. data/spec/fixtures/kontena-not-hash-service-config.yml +0 -3
  206. data/spec/kontena/cli/login_command_spec.rb +0 -32
  207. data/spec/kontena/cli/register_command_spec.rb +0 -57
@@ -0,0 +1,537 @@
1
+ require 'ostruct'
2
+ require 'singleton'
3
+ require 'forwardable'
4
+ require 'json'
5
+ require 'logger'
6
+
7
+ module Kontena
8
+ module Cli
9
+ # Helper to access and update the CLI configuration file.
10
+ #
11
+ # Also provides a "fake" config hash that behaves just like the file based
12
+ # config when ENV-variables are used instead of config file.
13
+ class Config < OpenStruct
14
+ include Singleton
15
+
16
+ attr_accessor :logger
17
+ attr_accessor :current_server
18
+ attr_reader :current_account
19
+
20
+ def self.reset_instance
21
+ Singleton.send :__init__, self
22
+ self
23
+ end
24
+
25
+ class TokenExpiredError < StandardError; end
26
+
27
+ def initialize
28
+ @logger = Logger.new(STDOUT)
29
+ @logger.level = ENV["DEBUG"].nil? ? Logger::INFO : Logger::DEBUG
30
+ @logger.progname = 'CONFIG'
31
+ load_settings_from_env || load_settings_from_config_file
32
+
33
+ logger.debug "Configuration loaded with #{servers.count} servers."
34
+ logger.debug "Current master: #{current_server || '(not selected)'}"
35
+ logger.debug "Current grid: #{current_grid || '(not selected)'}"
36
+ end
37
+
38
+ # Craft a regular looking configuration based on ENV variables
39
+ def load_settings_from_env
40
+ return nil unless ENV['KONTENA_URL']
41
+ logger.debug 'Loading configuration from ENV'
42
+ servers << Server.new(
43
+ url: ENV['KONTENA_URL'],
44
+ name: 'default',
45
+ token: Token.new(access_token: ENV['KONTENA_TOKEN'], parent_type: :master, parent_name: 'default'),
46
+ grid: ENV['KONTENA_GRID'],
47
+ parent_type: :master,
48
+ parent_name: 'default'
49
+ )
50
+ accounts << Account.new(
51
+ url: ENV['AUTH_API_URL'] || 'https://auth.kontena.io',
52
+ name: 'kontena',
53
+ token: Token.new(access_token: ENV['KONTENA_ACCOUNT_TOKEN'], parent_type: :account, parent_name: 'default')
54
+ )
55
+
56
+ self.current_master = 'default'
57
+ self.current_account = 'kontena'
58
+ end
59
+
60
+ def extract_token!(hash={})
61
+ Token.new(
62
+ access_token: hash.delete('token'),
63
+ refresh_token: hash.delete('refresh_token'),
64
+ expires_at: hash.delete('token_expires_at').to_i
65
+ )
66
+ end
67
+
68
+ # Load configuration from default location ($HOME/.kontena_client.json)
69
+ def load_settings_from_config_file
70
+ settings = config_file_available? ? parse_config_file : default_settings
71
+
72
+ Array(settings['servers']).each do |server_data|
73
+ if server_data['token']
74
+ token = extract_token!(server_data)
75
+ token.parent_type = :master
76
+ token.parent_name = server_data['name']
77
+ server = Server.new(server_data)
78
+ server.token = token
79
+ else
80
+ server = Server.new(server_data)
81
+ end
82
+ server.account ||= 'master'
83
+ servers << server
84
+ end
85
+
86
+ self.current_server = ENV['KONTENA_MASTER'] || settings['current_server']
87
+
88
+ Array(settings['accounts']).each do |account_data|
89
+ if account_data['token']
90
+ token = extract_token!(account_data)
91
+ token.parent_type = :account
92
+ token.parent_name = account_data['name']
93
+ account = Account.new(account_data)
94
+ account.token = token
95
+ else
96
+ account = Account.new(account_data)
97
+ end
98
+ accounts << account
99
+ end
100
+
101
+ ka = find_account('kontena')
102
+ if ka
103
+ kontena_account_data.each {|k,v| ka[k] = v}
104
+ else
105
+ accounts << Account.new(kontena_account_data)
106
+ end
107
+
108
+ master_index = find_account_index('master')
109
+ accounts.delete_at(master_index) if master_index
110
+ accounts << Account.new(master_account_data)
111
+
112
+ self.current_account = settings['current_account'] || 'kontena'
113
+ end
114
+
115
+ def kontena_account_data
116
+ {
117
+ name: 'kontena',
118
+ url: 'https://cloud-api.kontena.io',
119
+ token_endpoint: 'https://auth2.kontena.io/v1/oauth2/token',
120
+ authorization_endpoint: 'https://cloud-beta.kontena.io/login/oauth/authorize',
121
+ userinfo_endpoint: 'https://auth2.kontena.io/v1/user',
122
+ token_post_content_type: 'application/x-www-form-urlencoded',
123
+ code_requires_basic_auth: false,
124
+ token_method: 'post',
125
+ scope: 'user',
126
+ client_id: nil
127
+ }
128
+ end
129
+
130
+ def master_account_data
131
+ {
132
+ name: 'master',
133
+ token_endpoint: '/oauth2/token',
134
+ authorization_endpoint: '/oauth2/authorize',
135
+ userinfo_endpoint: '/v1/user',
136
+ token_post_content_type: 'application/json',
137
+ token_method: 'post',
138
+ code_requires_basic_auth: false
139
+ }
140
+ end
141
+
142
+ # Verifies access to existing configuration file
143
+ #
144
+ # @return [Boolean]
145
+ def config_file_available?
146
+ File.exist?(config_filename) && File.readable?(config_filename)
147
+ end
148
+
149
+ # Default settings hash, used when configuration file does not exist.
150
+ #
151
+ # @return [Hash]
152
+ def default_settings
153
+ logger.debug 'Configuration file not found, using default settings.'
154
+ {
155
+ 'current_server' => 'default',
156
+ 'servers' => []
157
+ }
158
+ end
159
+
160
+ # Converts old style settings hash into modern one
161
+ #
162
+ # @param [Hash] settings_hash
163
+ # @return [Hash] migrated_settings_hash
164
+ def migrate_legacy_settings(settings)
165
+ logger.debug "Migrating from legacy style configuration"
166
+ {
167
+ 'current_server' => 'default',
168
+ 'servers' => [
169
+ settings['server'].merge(
170
+ 'name' => 'default',
171
+ 'account' => 'kontena'
172
+ )
173
+ ],
174
+ 'accounts' => [ kontena_account_data ]
175
+ }
176
+ end
177
+
178
+ # Read, parse and migrate the configuration file
179
+ #
180
+ # @return [Hash] config_data
181
+ def parse_config_file
182
+ logger.debug "Loading configuration from #{config_filename}"
183
+ settings = JSON.load(File.read(config_filename))
184
+ if settings.has_key?('server')
185
+ settings = migrate_legacy_settings(settings)
186
+ else
187
+ settings
188
+ end
189
+ end
190
+
191
+ # Return the configuration file path. You can override the default
192
+ # by using KONTENA_CONFIG environment variable.
193
+ #
194
+ # @return [String] path
195
+ def config_filename
196
+ @config_filename ||= ENV['KONTENA_CONFIG'] || default_config_filename
197
+ end
198
+
199
+ # Generate the default configuration filename
200
+ def default_config_filename
201
+ File.join(Dir.home, '.kontena_client.json')
202
+ end
203
+
204
+ # List of configured servers
205
+ #
206
+ # @return [Array]
207
+ def servers
208
+ @servers ||= []
209
+ end
210
+
211
+ # List of configured accounts
212
+ #
213
+ # @return [Array]
214
+ def accounts
215
+ @accounts ||= []
216
+ end
217
+
218
+ # Add a new server to the configuration
219
+ #
220
+ # @param [Hash] server_data
221
+ def add_server(data)
222
+ token = Token.new(
223
+ access_token: data.delete('token'),
224
+ refresh_token: data.delete('refresh_token'),
225
+ expires_at: data.delete('token_expires_at'),
226
+ parent_type: :master,
227
+ parent_name: data['name'] || data[:name]
228
+ )
229
+ server = Server.new(data.merge(token: token))
230
+ if (existing_index = find_server_index(server.name))
231
+ servers[existing_index] = server
232
+ else
233
+ servers << server
234
+ end
235
+ write
236
+ end
237
+
238
+ # Search the server list for a server by field(s) and value(s).
239
+ # @example
240
+ # find_server_by(url: 'https://localhost', token: 'abcd')
241
+ # @param [Hash] search_criteria
242
+ # @return [Server, NilClass]
243
+ def find_server_by(criteria = {})
244
+ servers.find{|s| criteria.none? {|k,v| v != s[k]}}
245
+ end
246
+
247
+ # Search the server list for a server by field(s) and value(s)
248
+ # and return its index.
249
+ #
250
+ # @example
251
+ # find_server_index(url: 'https://localhost')
252
+ # @param [Hash] search_criteria
253
+ # @return [Fixnum, NilClass]
254
+ def find_server_index_by(criteria = {})
255
+ servers.find_index{|s| criteria.none? {|k,v| v != s[k]}}
256
+ end
257
+
258
+ # Shortcut to find_server_by(name: name)
259
+ #
260
+ # @param [String] server_name
261
+ # @return [Server, NilClass]
262
+ def find_server(name)
263
+ find_server_by(name: name)
264
+ end
265
+
266
+ # Shortcut to find_server_index_by(name: name)
267
+ #
268
+ # @param [String] server_name
269
+ # @return [Fixnum, NilClass]
270
+ def find_server_index(name)
271
+ find_server_index_by(name: name)
272
+ end
273
+
274
+ def find_account(name)
275
+ accounts.find{|a| a['name'] == name.to_s}
276
+ end
277
+
278
+ def find_account_index(name)
279
+ accounts.find_index{|a| a['name'] == name.to_s}
280
+ end
281
+
282
+ # Currently selected master's configuration data
283
+ #
284
+ # @return [Server]
285
+ def current_master
286
+ return servers[@current_master_index] if @current_master_index
287
+ return nil unless current_server
288
+ @current_master_index = find_server_index(current_server)
289
+ servers[@current_master_index] if @current_master_index
290
+ end
291
+
292
+ # Raises unless current master has token.
293
+ #
294
+ # @return [Token] current_master_token
295
+ # @raise [ArgumentError] if no token available
296
+ def require_current_master_token
297
+ require_current_master
298
+ token = current_master.token
299
+ if token && token.access_token
300
+ return token unless token.expired?
301
+ raise TokenExpiredError, "The access token has expired and needs to be refreshed."
302
+ end
303
+ raise ArgumentError, "You are not logged into a Kontena Master. Use: kontena master login"
304
+ end
305
+
306
+ # Raises unless current master is selected.
307
+ #
308
+ # @return [Server] current_master
309
+ # @raise [ArgumentError] if no account is selected
310
+ def require_current_master
311
+ return current_master if current_master
312
+ raise ArgumentError, "You are not logged into a Kontena Master. Use: kontena master login"
313
+ end
314
+
315
+ # Raises unless current account is selected.
316
+ #
317
+ # @return [Account] current_account
318
+ # @raise [ArgumentError] if no account is selected
319
+ def require_current_account
320
+ return @current_account if @current_account
321
+ raise ArgumentError, "You are not logged into an authorization provider. Use: kontena cloud login"
322
+ end
323
+
324
+ def require_current_account_token
325
+ account = require_current_account
326
+ if !account || account.token.nil? || account.token.access_token.nil?
327
+ raise ArgumentError, "You are not logged in to Kontena Cloud. Use: kontena cloud login"
328
+ elsif account.token.expired?
329
+ raise TokenExpiredError, "The cloud access token has expired and needs to be refreshed." unless cloud_client.refresh_token
330
+ end
331
+ end
332
+
333
+ # Set the current master.
334
+ #
335
+ # @param [String] server_name
336
+ # @raise [ArgumentError] if server by that name doesn't exist
337
+ def current_master=(name)
338
+ @current_master_index = nil
339
+ if name.nil?
340
+ self.current_server = nil
341
+ else
342
+ index = find_server_index(name.respond_to?(:name) ? name.name : name)
343
+ if index
344
+ self.current_server = servers[index].name
345
+ else
346
+ raise ArgumentError, "Server '#{name}' does not exist, can't add as current master."
347
+ end
348
+ end
349
+ end
350
+
351
+ # Raises unless current grid is selected.
352
+ #
353
+ # @return [String] current_grid_name
354
+ # @raise [ArgumentError] if no grid is selected
355
+ def require_current_grid
356
+ return current_grid if current_grid
357
+ raise ArgumentError, "You have not selected a grid. Use: kontena grid"
358
+ end
359
+
360
+ # Name of the currently selected grid. Can override using
361
+ # KONTENA_GRID environment variable.
362
+ #
363
+ # @return [String, NilClass]
364
+ def current_grid
365
+ ENV['KONTENA_GRID'] || (current_master && current_master.grid)
366
+ end
367
+
368
+ # Set the current grid name.
369
+ #
370
+ # @param [String] grid_name
371
+ # @raise [ArgumentError] if current master hasn't been selected
372
+ def current_grid=(name)
373
+ if current_master
374
+ current_master.grid = name
375
+ else
376
+ raise ArgumentError, "Current master not selected, can't set grid."
377
+ end
378
+ end
379
+
380
+ def current_account=(name)
381
+ if name.nil?
382
+ @current_account = nil
383
+ elsif name == 'master'
384
+ raise ArgumentError, "The master account can not be used as current account."
385
+ else
386
+ account = find_account(name.respond_to?(:name) ? name.name : name)
387
+ if account
388
+ @current_account = account
389
+ else
390
+ raise ArgumentError, "Account '#{name}' not found in configuration"
391
+ end
392
+ end
393
+ end
394
+
395
+ # Returns a cleaned up version of the kontena account data with only the token and name.
396
+ def kontena_account_hash
397
+ hash = { name: 'kontena' }
398
+ acc = find_account('kontena')
399
+ if acc && acc.token
400
+ hash[:username] = acc.username if acc.username
401
+ hash.merge!(acc.token.to_h)
402
+ end
403
+ hash
404
+ end
405
+
406
+ # Generate a hash from the current configuration.
407
+ #
408
+ # @return [Hash]
409
+ def to_hash
410
+ hash = {
411
+ current_server: (self.current_server && find_server(self.current_server)) ? self.current_server : nil,
412
+ current_account: self.current_account ? self.current_account.name : nil,
413
+ servers: servers.map(&:to_h),
414
+ accounts: accounts.reject{|a| a.name == 'master' || a.name == 'kontena'}.map(&:to_h) + [kontena_account_hash]
415
+ }
416
+ hash[:servers].each do |server|
417
+ server.delete(:account) if server[:account] == 'master'
418
+ end
419
+ hash
420
+ end
421
+
422
+ # Generate a JSON string from the current configuration
423
+ #
424
+ # @return [String]
425
+ def to_json
426
+ JSON.pretty_generate(to_hash)
427
+ end
428
+
429
+ # Write the current configuration to config file.
430
+ # Does nothing if using settings from environment variables.
431
+ def write
432
+ return nil if ENV['KONTENA_URL']
433
+ logger.debug "Writing configuration to #{config_filename}"
434
+ File.write(config_filename, to_json)
435
+ end
436
+
437
+ class << self
438
+ extend Forwardable
439
+ def_delegators :instance, *Config.instance_methods(false)
440
+ end
441
+
442
+ module TokenSerializer
443
+ # Modified to_h to handle token data serialization
444
+ #
445
+ # @return [Hash]
446
+ def to_h
447
+ token = delete_field(:token) if respond_to?(:token)
448
+ result = super
449
+ if token
450
+ self.token = token
451
+ result.merge!(token.to_h)
452
+ end
453
+ result
454
+ end
455
+ end
456
+
457
+ module ConfigurationInstance
458
+ def config
459
+ Kontena::Cli::Config.instance
460
+ end
461
+ end
462
+
463
+ class Account < OpenStruct
464
+ include TokenSerializer
465
+ include ConfigurationInstance
466
+
467
+ # Strip token info from master-account, the token is saved with the server.
468
+ def to_h
469
+ if self.name == 'master'
470
+ super.to_h.reject do |k,_|
471
+ [:url, :token, :refresh_token, :token_expires_at].include?(k)
472
+ end
473
+ else
474
+ super
475
+ end
476
+ end
477
+ end
478
+
479
+ class Server < OpenStruct
480
+ include TokenSerializer
481
+ include ConfigurationInstance
482
+
483
+ def initialize(*args)
484
+ super
485
+ @table[:account] ||= 'master'
486
+ end
487
+ end
488
+
489
+ class Token < OpenStruct
490
+ include ConfigurationInstance
491
+
492
+ # Hash representation of token data
493
+ #
494
+ # @return [Hash]
495
+ def to_h
496
+ {
497
+ token: self.access_token,
498
+ token_expires_at: self.expires_at,
499
+ refresh_token: self.refresh_token
500
+ }.merge(self.respond_to?(:username) ? {username: self.username} : {})
501
+ end
502
+
503
+ def expires?
504
+ expires_at.nil? ? false : expires_at.to_i > 0
505
+ end
506
+
507
+ def expired?
508
+ expires? && expires_at && expires_at.to_i < Time.now.utc.to_i
509
+ end
510
+
511
+ def account
512
+ return @account if @account
513
+ @account =
514
+ case parent_type
515
+ when :master then config.find_account(parent.account)
516
+ when :account then parent
517
+ else
518
+ nil
519
+ end
520
+ end
521
+
522
+ def parent
523
+ return nil unless parent_type
524
+ return nil unless parent_name
525
+ case parent_type
526
+ when :master
527
+ config.find_server(parent_name)
528
+ when :account
529
+ config.find_account(parent_name)
530
+ else
531
+ nil
532
+ end
533
+ end
534
+ end
535
+ end
536
+ end
537
+ end