Class: Kontena::Cli::Config

Inherits:
OpenStruct
  • Object
show all
Extended by:
Forwardable
Includes:
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, TokenSerializer Classes: Account, Server, Token

Constant Summary collapse

TokenExpiredError =
Class.new(StandardError)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ Config

Returns a new instance of Config.



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/kontena/cli/config.rb', line 27

def initialize
  super
  @logger = Logger.new(ENV["DEBUG"] ? $stderr : $stdout)
  @logger.level = ENV["DEBUG"].nil? ? Logger::INFO : Logger::DEBUG
  @logger.progname = 'CONFIG'
  load_settings_from_env || load_settings_from_config_file

  logger.debug "Configuration loaded with #{servers.count} servers."
  logger.debug "Current master: #{current_server || '(not selected)'}"
  logger.debug "Current grid: #{current_grid || '(not selected)'}"
end

Instance Attribute Details

#current_account ⇒ Object

Returns the value of attribute current_account.



18
19
20
# File 'lib/kontena/cli/config.rb', line 18

def 
  @current_account
end

#current_server ⇒ Object

Returns the value of attribute current_server.



17
18
19
# File 'lib/kontena/cli/config.rb', line 17

def current_server
  @current_server
end

#logger ⇒ Object

Returns the value of attribute logger.



16
17
18
# File 'lib/kontena/cli/config.rb', line 16

def logger
  @logger
end

Class Method Details

.reset_instance ⇒ Object



20
21
22
23
# File 'lib/kontena/cli/config.rb', line 20

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

Instance Method Details

#accounts ⇒ Array

List of configured accounts

Returns:

  • (Array)


221
222
223
# File 'lib/kontena/cli/config.rb', line 221

def accounts
  @accounts ||= []
end

#add_server(data) ⇒ Object

Add a new server to the configuration

Parameters:

  • server_data (Hash)


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/kontena/cli/config.rb', line 228

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)


152
153
154
# File 'lib/kontena/cli/config.rb', line 152

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

#config_filename ⇒ String

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

Returns:



202
203
204
# File 'lib/kontena/cli/config.rb', line 202

def config_filename
  @config_filename ||= ENV['KONTENA_CONFIG'] || default_config_filename
end

#current_grid ⇒ String, NilClass

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

Returns:



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

def current_grid
  ENV['KONTENA_GRID'] || (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



379
380
381
382
383
384
385
# File 'lib/kontena/cli/config.rb', line 379

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_master ⇒ Server

Currently selected master's configuration data

Returns:



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

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



344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/kontena/cli/config.rb', line 344

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

#default_config_filename ⇒ Object

Generate the default configuration filename



207
208
209
# File 'lib/kontena/cli/config.rb', line 207

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

#default_settings ⇒ Hash

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

Returns:

  • (Hash)


159
160
161
162
163
164
165
# File 'lib/kontena/cli/config.rb', line 159

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

#extract_token!(hash = {}) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/kontena/cli/config.rb', line 61

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



281
282
283
# File 'lib/kontena/cli/config.rb', line 281

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

#find_account_index(name) ⇒ Object



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

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:



269
270
271
# File 'lib/kontena/cli/config.rb', line 269

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:



250
251
252
# File 'lib/kontena/cli/config.rb', line 250

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)


277
278
279
# File 'lib/kontena/cli/config.rb', line 277

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)


261
262
263
# File 'lib/kontena/cli/config.rb', line 261

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

#kontena_account_data ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/kontena/cli/config.rb', line 121

def 
  {
    name: 'kontena',
    url: 'https://cloud-api.kontena.io',
    stacks_url: 'https://stacks.kontena.io',
    token_endpoint: 'https://cloud-api.kontena.io/oauth2/token',
    authorization_endpoint: 'https://cloud.kontena.io/login/oauth/authorize',
    userinfo_endpoint: 'https://cloud-api.kontena.io/user',
    token_post_content_type: 'application/x-www-form-urlencoded',
    code_requires_basic_auth: false,
    token_method: 'post',
    scope: 'user',
    client_id: nil
  }
end

#kontena_account_hash ⇒ Object

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



403
404
405
406
407
408
409
410
411
# File 'lib/kontena/cli/config.rb', line 403

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_file ⇒ Object

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



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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/kontena/cli/config.rb', line 70

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?
      logger.debug "Renamed server to #{server.name} because a duplicate was found in config"
    end
    servers << server
  end

  self.current_server = ENV['KONTENA_MASTER'] || 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

#load_settings_from_env ⇒ Object

Craft a regular looking configuration based on ENV variables



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

def load_settings_from_env
  return nil unless ENV['KONTENA_URL']
  logger.debug 'Loading configuration from ENV'
  servers << Server.new(
    url: ENV['KONTENA_URL'],
    name: 'default',
    token: Token.new(access_token: ENV['KONTENA_TOKEN'], parent_type: :master, parent_name: 'default'),
    grid: ENV['KONTENA_GRID'],
    parent_type: :master,
    parent_name: 'default'
  )
  accounts << Account.new(
    url: ENV['AUTH_API_URL'] || 'https://auth.kontena.io',
    name: 'kontena',
    token: Token.new(access_token: ENV['KONTENA_ACCOUNT_TOKEN'], parent_type: :account, parent_name: 'default')
  )

  self.current_master  = 'default'
  self. = 'kontena'
end

#master_account_data ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/kontena/cli/config.rb', line 137

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



171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/kontena/cli/config.rb', line 171

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

#parse_config_file ⇒ Hash

Read, parse and migrate the configuration file

Returns:

  • (Hash) —

    config_data



188
189
190
191
192
193
194
195
196
# File 'lib/kontena/cli/config.rb', line 188

def parse_config_file
  logger.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_account ⇒ Account

Raises unless current account is selected.

Returns:

Raises:

  • (ArgumentError) —

    if no account is selected



326
327
328
329
# File 'lib/kontena/cli/config.rb', line 326

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_token ⇒ Object



331
332
333
334
335
336
337
338
# File 'lib/kontena/cli/config.rb', line 331

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_grid ⇒ String

Raises unless current grid is selected.

Returns:

  • (String) —

    current_grid_name

Raises:

  • (ArgumentError) —

    if no grid is selected



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

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

#require_current_master ⇒ Server

Raises unless current master is selected.

Returns:

Raises:

  • (ArgumentError) —

    if no account is selected



317
318
319
320
# File 'lib/kontena/cli/config.rb', line 317

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_token ⇒ Token

Raises unless current master has token.

Returns:

  • (Token) —

    current_master_token

Raises:

  • (ArgumentError) —

    if no token available



303
304
305
306
307
308
309
310
311
# File 'lib/kontena/cli/config.rb', line 303

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

#servers ⇒ Array

List of configured servers

Returns:

  • (Array)


214
215
216
# File 'lib/kontena/cli/config.rb', line 214

def servers
  @servers ||= []
end

#to_hash ⇒ Hash

Generate a hash from the current configuration.

Returns:

  • (Hash)


416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/kontena/cli/config.rb', line 416

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_json ⇒ String

Generate a JSON string from the current configuration

Returns:



432
433
434
# File 'lib/kontena/cli/config.rb', line 432

def to_json
  JSON.pretty_generate(to_hash)
end

#write ⇒ Object

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



438
439
440
441
442
# File 'lib/kontena/cli/config.rb', line 438

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