Class: Kontena::Cli::Config

Inherits:
OpenStruct
  • Object
show all
Extended by:
Forwardable
Includes:
Fields, Singleton
Defined in:
lib/kontena/cli/config.rb

Overview

Helper to access and update the CLI configuration file.

Also provides a “fake” config hash that behaves just like the file based config when ENV-variables are used instead of config file.

Defined Under Namespace

Modules: ConfigurationInstance, Fields, TokenSerializer Classes: Account, Server, Token

Constant Summary collapse

TokenExpiredError =
Class.new(StandardError)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Fields

#keys, #values_at

Constructor Details

#initializeConfig

Returns a new instance of Config.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/kontena/cli/config.rb', line 40

def initialize
  super
  @logger = Kontena.logger

  load_settings_from_config_file

  override_master_settings_from_env
  override_cloud_settings_from_env

  debug { "Configuration loaded with #{servers.count} servers and #{accounts.count} accounts." }
  debug { "Current master: #{current_server || '(not selected)'}" }
  debug { "Current grid: #{current_grid || '(not selected)'}" }
  debug { "Current account: #{.nil? ? '(not selected)' : .name}" }
end

Instance Attribute Details

#current_accountObject

Returns the value of attribute current_account.



30
31
32
# File 'lib/kontena/cli/config.rb', line 30

def 
  @current_account
end

#current_serverObject

Returns the value of attribute current_server.



29
30
31
# File 'lib/kontena/cli/config.rb', line 29

def current_server
  @current_server
end

#loggerObject

Returns the value of attribute logger.



28
29
30
# File 'lib/kontena/cli/config.rb', line 28

def logger
  @logger
end

Class Method Details

.reset_instanceObject



33
34
35
36
# File 'lib/kontena/cli/config.rb', line 33

def self.reset_instance
  Singleton.send :__init__, self
  self
end

Instance Method Details

#accountsArray

List of configured accounts

Returns:

  • (Array)


300
301
302
# File 'lib/kontena/cli/config.rb', line 300

def accounts
  @accounts ||= []
end

#add_server(data) ⇒ Object

Add a new server to the configuration

Parameters:

  • server_data (Hash)


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/kontena/cli/config.rb', line 307

def add_server(data)
  token = Token.new(
    access_token: data.delete('token'),
    refresh_token: data.delete('refresh_token'),
    expires_at: data.delete('token_expires_at'),
    parent_type: :master,
    parent_name: data['name'] || data[:name]
  )
  server = Server.new(data.merge(token: token))
  if (existing_index = find_server_index(server.name))
    servers[existing_index] = server
  else
    servers << server
  end
  write
end

#config_file_available?Boolean

Verifies access to existing configuration file

Returns:

  • (Boolean)


225
226
227
# File 'lib/kontena/cli/config.rb', line 225

def config_file_available?
  File.exist?(config_filename) && File.readable?(config_filename)
end

#config_filenameString

Return the configuration file path. You can override the default by using KONTENA_CONFIG environment variable.

Returns:



275
276
277
278
279
280
281
282
283
# File 'lib/kontena/cli/config.rb', line 275

def config_filename
  return @config_filename if @config_filename
  if ENV['KONTENA_CONFIG']
    debug { "Using #{ENV['KONTENA_CONFIG']} as config file set through env KONTENA_CONFIG"}
    @config_filename = ENV['KONTENA_CONFIG']
  else
    @config_filename = default_config_filename
  end
end

#current_gridString, NilClass

Name of the currently selected grid. Can override using KONTENA_GRID environment variable.

Returns:



450
451
452
453
454
# File 'lib/kontena/cli/config.rb', line 450

def current_grid
  return ENV['KONTENA_GRID'] unless ENV['KONTENA_GRID'].to_s.empty?
  return nil unless current_master
  current_master.grid
end

#current_grid=(name) ⇒ Object

Set the current grid name.

Parameters:

Raises:

  • (ArgumentError)

    if current master hasn’t been selected



460
461
462
463
464
465
466
# File 'lib/kontena/cli/config.rb', line 460

def current_grid=(name)
  if current_master
    current_master.grid = name
  else
    raise ArgumentError, "Current master not selected, can't set grid."
  end
end

#current_masterServer

Currently selected master’s configuration data

Returns:



371
372
373
374
375
376
# File 'lib/kontena/cli/config.rb', line 371

def current_master
  return servers[@current_master_index] if @current_master_index
  return nil unless current_server
  @current_master_index = find_server_index(current_server)
  servers[@current_master_index] if @current_master_index
end

#current_master=(name) ⇒ Object

Set the current master.

Parameters:

Raises:

  • (ArgumentError)

    if server by that name doesn’t exist



423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/kontena/cli/config.rb', line 423

def current_master=(name)
  @current_master_index = nil
  if name.nil?
    self.current_server = nil
  else
    index = find_server_index(name.respond_to?(:name) ? name.name : name)
    if index
      self.current_server = servers[index].name
    else
      raise ArgumentError, "Server '#{name}' does not exist, can't add as current master."
    end
  end
end

#debug(&block) ⇒ Object



55
56
57
# File 'lib/kontena/cli/config.rb', line 55

def debug(&block)
  Kontena.logger.add(Logger::DEBUG, nil, 'CONFIG', &block)
end

#default_config_filenameObject

Generate the default configuration filename



286
287
288
# File 'lib/kontena/cli/config.rb', line 286

def default_config_filename
  File.join(Dir.home, '.krates.json')
end

#default_settingsHash

Default settings hash, used when configuration file does not exist.

Returns:

  • (Hash)


232
233
234
235
236
237
238
# File 'lib/kontena/cli/config.rb', line 232

def default_settings
  debug { 'Configuration file not found, using default settings.' }
  {
    'current_server' => 'default',
    'servers' => []
  }
end

#extract_token!(hash = {}) ⇒ Object



133
134
135
136
137
138
139
# File 'lib/kontena/cli/config.rb', line 133

def extract_token!(hash={})
  Token.new(
    access_token: hash.delete('token'),
    refresh_token: hash.delete('refresh_token'),
    expires_at: hash.delete('token_expires_at').to_i
  )
end

#find_account(name) ⇒ Object



360
361
362
# File 'lib/kontena/cli/config.rb', line 360

def (name)
  accounts.find{|a| a['name'] == name.to_s}
end

#find_account_index(name) ⇒ Object



364
365
366
# File 'lib/kontena/cli/config.rb', line 364

def (name)
  accounts.find_index{|a| a['name'] == name.to_s}
end

#find_server(name) ⇒ Server, NilClass

Shortcut to find_server_by(name: name)

Parameters:

Returns:



348
349
350
# File 'lib/kontena/cli/config.rb', line 348

def find_server(name)
  find_server_by(name: name)
end

#find_server_by(criteria = {}) ⇒ Server, NilClass

Search the server list for a server by field(s) and value(s).

Examples:

find_server_by(url: 'https://localhost', token: 'abcd')

Parameters:

  • search_criteria (Hash)

Returns:



329
330
331
# File 'lib/kontena/cli/config.rb', line 329

def find_server_by(criteria = {})
  servers.find{|s| criteria.none? {|k,v| v != s[k]}}
end

#find_server_index(name) ⇒ Fixnum, NilClass

Shortcut to find_server_index_by(name: name)

Parameters:

Returns:

  • (Fixnum, NilClass)


356
357
358
# File 'lib/kontena/cli/config.rb', line 356

def find_server_index(name)
  find_server_index_by(name: name)
end

#find_server_index_by(criteria = {}) ⇒ Fixnum, NilClass

Search the server list for a server by field(s) and value(s) and return its index.

Examples:

find_server_index(url: 'https://localhost')

Parameters:

  • search_criteria (Hash)

Returns:

  • (Fixnum, NilClass)


340
341
342
# File 'lib/kontena/cli/config.rb', line 340

def find_server_index_by(criteria = {})
  servers.find_index{|s| criteria.none? {|k,v| v != s[k]}}
end

#kontena_account_dataObject



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/kontena/cli/config.rb', line 193

def 
  {
    name: 'kontena',
    url: ENV['KONTENA_CLOUD_URL'] || 'https://cloud-api.kontena.io',
    stacks_url: ENV['KONTENA_STACK_REGISTRY_URL'] || 'https://stacks.kontena.io',
    token_endpoint: ENV['AUTH_TOKEN_ENDPOINT'] || 'https://cloud-api.kontena.io/oauth2/token',
    authorization_endpoint: ENV['AUTH_AUTHORIZE_ENDPOINT'] || 'https://cloud.kontena.io/login/oauth/authorize',
    userinfo_endpoint: ENV['AUTH_USERINFO_ENDPOINT'] || 'https://cloud-api.kontena.io/user',
    token_post_content_type: ENV['AUTH_TOKEN_POST_CONTENT_TYPE'] || 'application/x-www-form-urlencoded',
    code_requires_basic_auth: ENV['AUTH_CODE_REQUIRES_BASIC_AUTH'].to_s == true,
    token_method: ENV['AUTH_TOKEN_METHOD'] || 'post',
    scope: ENV['AUTH_USERINFO_SCOPE'] || 'user',
    client_id: nil,
    stacks_read_authentication: ENV['KONTENA_STACK_REGISTRY_READ_AUTHENTICATION'].to_s == 'true'
  }
end

#kontena_account_hashObject

Returns a cleaned up version of the kontena account data with only the token and name.



484
485
486
487
488
489
490
491
492
# File 'lib/kontena/cli/config.rb', line 484

def 
  hash = { name: 'kontena' }
  acc  = ('kontena')
  if acc && acc.token
    hash[:username] = acc.username if acc.username
    hash.merge!(acc.token.to_h)
  end
  hash
end

#load_settings_from_config_fileObject

Load configuration from default location ($HOME/.krates.json)



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/kontena/cli/config.rb', line 142

def load_settings_from_config_file
  settings = config_file_available? ? parse_config_file : default_settings

  Array(settings['servers']).each do |server_data|
    if server_data['token']
      token = extract_token!(server_data)
      token.parent_type = :master
      token.parent_name = server_data['name']
      server = Server.new(server_data)
      server.token = token
    else
      server = Server.new(server_data)
    end
    server. ||= 'master'
    if servers.find { |s| s['name'] == server.name}
      server.name = "#{server.name}-2"
      server.name.succ! until servers.find { |s| s['name'] == server.name }.nil?
      debug { "Renamed server to #{server.name} because a duplicate was found in config" }
    end
    servers << server
  end

  self.current_server = settings['current_server']

  Array(settings['accounts']).each do ||
    if ['token']
      token = extract_token!()
      token.parent_type = :account
      token.parent_name = ['name']
       = Account.new()
      .token = token
    else
       = Account.new()
    end
    accounts << 
  end

  ka = ('kontena')
  if ka
    .each {|k,v| ka[k] = v}
  else
    accounts << Account.new()
  end

  master_index = ('master')
  accounts.delete_at(master_index) if master_index
  accounts << Account.new()

  self. = settings['current_account'] || 'kontena'
end

#master_account_dataObject



210
211
212
213
214
215
216
217
218
219
220
# File 'lib/kontena/cli/config.rb', line 210

def 
  {
    name: 'master',
    token_endpoint: '/oauth2/token',
    authorization_endpoint: '/oauth2/authorize',
    userinfo_endpoint: '/v1/user',
    token_post_content_type: 'application/json',
    token_method: 'post',
    code_requires_basic_auth: false
  }
end

#migrate_legacy_settings(settings) ⇒ Hash

Converts old style settings hash into modern one

Parameters:

  • settings_hash (Hash)

Returns:

  • (Hash)

    migrated_settings_hash



244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/kontena/cli/config.rb', line 244

def migrate_legacy_settings(settings)
  debug { "Migrating from legacy style configuration" }
  {
    'current_server' => 'default',
    'servers' => [
      settings['server'].merge(
        'name' => 'default',
        'account' => 'kontena'
      )
    ],
    'accounts' => [  ]
  }
end

#override_cloud_settings_from_envObject



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/kontena/cli/config.rb', line 103

def override_cloud_settings_from_env
  if ENV['KONTENA_CLOUD']
     = (ENV['KONTENA_CLOUD'])
    if 
      debug { "Using cloud account #{ENV['KONTENA_CLOUD']} from config selected by env KONTENA_CLOUD" }
      self. = .name
    else
      debug { "Using new cloud account #{ENV['KONTENA_CLOUD']} from env" }
       = Accout.new(.merge(name: ENV['KONTENA_CLOUD']))
      accounts << 
    end
  elsif 
     = 
  end

  if .nil?
    debug { 'No account data from config or env' }
    self. = nil
    return
  end

  if ENV['KONTENA_CLOUD_TOKEN']
    debug { 'Using cloud token from env KONTENA_CLOUD_TOKEN' }
    .token ||= Token.new(parent_type: :account, parent_name: .name)
    .token.access_token = ENV['KONTENA_CLOUD_TOKEN']
    .token.refresh_token = nil
    .token.expires_at = nil
  end
end

#override_master_settings_from_envObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/kontena/cli/config.rb', line 59

def override_master_settings_from_env
  if ENV['KONTENA_URL']
    server = find_server_by(url: ENV['KONTENA_URL'])
    if server.nil?
      debug { 'Using new master url from env KONTENA_URL' }
      server = Server.new(
        url: ENV['KONTENA_URL'],
        name: ENV['KONTENA_MASTER'] || 'default'
      )
      servers << server
    else
      debug { "Using master #{server.name} in config found via url in env KONTENA_URL" }
    end
  elsif ENV['KONTENA_MASTER']
    server = find_server_by(name: ENV['KONTENA_MASTER'])
    if server
      debug { "Using master #{ENV['KONTENA_MASTER']} set via env KONTENA_MASTER" }
    end
  elsif current_master
    server = current_master
  end

  if server.nil?
    debug { 'Could not determine a master through config or env' }
    self.current_master = nil
    return
  else
    self.current_master = server.name
  end

  if ENV['KONTENA_GRID']
    debug { "Using grid #{ENV['KONTENA_GRID']} from env KONTENA_GRID" }
    server.grid = ENV['KONTENA_GRID']
  end

  if ENV['KONTENA_TOKEN']
    debug { 'Using master token from env KONTENA_TOKEN' }
    server.token ||= Token.new(parent_type: :master, parent_name: server.name)
    server.token.access_token = ENV['KONTENA_TOKEN']
    server.token.refresh_token = nil
    server.token.expires_at = nil
  end
end

#parse_config_fileHash

Read, parse and migrate the configuration file

Returns:

  • (Hash)

    config_data



261
262
263
264
265
266
267
268
269
# File 'lib/kontena/cli/config.rb', line 261

def parse_config_file
  debug { "Loading configuration from #{config_filename}" }
  settings = JSON.load(File.read(config_filename))
  if settings.has_key?('server')
    settings = migrate_legacy_settings(settings)
  else
    settings
  end
end

#require_current_accountAccount

Raises unless current account is selected.

Returns:

Raises:

  • (ArgumentError)

    if no account is selected



405
406
407
408
# File 'lib/kontena/cli/config.rb', line 405

def 
  return @current_account if @current_account
  raise ArgumentError, "You are not logged into an authorization provider. Use: kontena cloud login"
end

#require_current_account_tokenObject



410
411
412
413
414
415
416
417
# File 'lib/kontena/cli/config.rb', line 410

def 
   = 
  if ! || .token.nil? || .token.access_token.nil?
    raise ArgumentError, "You are not logged in to Kontena Cloud. Use: kontena cloud login"
  elsif .token.expired?
    raise TokenExpiredError, "The cloud access token has expired and needs to be refreshed." unless cloud_client.refresh_token
  end
end

#require_current_gridString

Raises unless current grid is selected.

Returns:

  • (String)

    current_grid_name

Raises:

  • (ArgumentError)

    if no grid is selected



441
442
443
444
# File 'lib/kontena/cli/config.rb', line 441

def require_current_grid
  return current_grid if current_grid
  raise ArgumentError, "You have not selected a grid. Use: kontena grid"
end

#require_current_masterServer

Raises unless current master is selected.

Returns:

Raises:

  • (ArgumentError)

    if no account is selected



396
397
398
399
# File 'lib/kontena/cli/config.rb', line 396

def require_current_master
  return current_master if current_master
  raise ArgumentError, "You are not logged into a Kontena Master. Use: kontena master login"
end

#require_current_master_tokenToken

Raises unless current master has token.

Returns:

  • (Token)

    current_master_token

Raises:

  • (ArgumentError)

    if no token available



382
383
384
385
386
387
388
389
390
# File 'lib/kontena/cli/config.rb', line 382

def require_current_master_token
  require_current_master
  token = current_master.token
  if token && token.access_token
    return token unless token.expired?
    raise TokenExpiredError, "The access token has expired and needs to be refreshed."
  end
  raise ArgumentError, "You are not logged into a Kontena Master. Use: kontena master login"
end

#serversArray

List of configured servers

Returns:

  • (Array)


293
294
295
# File 'lib/kontena/cli/config.rb', line 293

def servers
  @servers ||= []
end

#to_hashHash

Generate a hash from the current configuration.

Returns:

  • (Hash)


497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/kontena/cli/config.rb', line 497

def to_hash
  hash = {
    current_server: (self.current_server && find_server(self.current_server)) ? self.current_server : nil,
    current_account: self. ? self..name : nil,
    servers: servers.map(&:to_h),
    accounts: accounts.reject{|a| a.name == 'master' || a.name == 'kontena'}.map(&:to_h) + []
  }
  hash[:servers].each do |server|
    server.delete(:account) if server[:account] == 'master'
  end
  hash
end

#to_jsonString

Generate a JSON string from the current configuration

Returns:



513
514
515
# File 'lib/kontena/cli/config.rb', line 513

def to_json
  JSON.pretty_generate(to_hash)
end

#writeObject

Write the current configuration to config file. Does nothing if using settings from environment variables.



519
520
521
522
523
# File 'lib/kontena/cli/config.rb', line 519

def write
  return nil if ENV['KONTENA_URL']
  debug { "Writing configuration to #{config_filename}" }
  File.write(config_filename, to_json)
end