Class: Skylight::Config

Inherits:
Object show all
Includes:
Util::Logging
Defined in:
lib/skylight/config.rb

Constant Summary collapse

MUTEX =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Mutex.new
DEFAULT_IGNORED_ENDPOINTS =
%w[Rails::HealthController#show].freeze
ENV_TO_KEY =

Map environment variable keys with Skylight configuration keys

{
  # == Authentication ==
  -"AUTHENTICATION" => :authentication,
  # == App settings ==
  -"ROOT" => :root,
  -"HOSTNAME" => :hostname,
  -"SESSION_TOKEN" => :session_token,
  # == Component settings ==
  -"ENV" => :env,
  -"COMPONENT" => :component,
  -"REPORT_RAILS_ENV" => :report_rails_env,
  # == Deploy settings ==
  -"DEPLOY_ID" => :"deploy.id",
  -"DEPLOY_GIT_SHA" => :"deploy.git_sha",
  -"DEPLOY_DESCRIPTION" => :"deploy.description",
  # == Logging ==
  -"LOG_FILE" => :log_file,
  -"LOG_LEVEL" => :log_level,
  -"ALERT_LOG_FILE" => :alert_log_file,
  -"NATIVE_LOG_FILE" => :native_log_file,
  -"NATIVE_LOG_LEVEL" => :native_log_level,
  -"LOG_SQL_PARSE_ERRORS" => :log_sql_parse_errors,
  # == Proxy ==
  -"PROXY_URL" => :proxy_url,
  # == Instrumenter ==
  -"ENABLE_SEGMENTS" => :enable_segments,
  -"ENABLE_SIDEKIQ" => :enable_sidekiq,
  -"IGNORED_ENDPOINT" => :ignored_endpoint,
  -"IGNORED_ENDPOINTS" => :ignored_endpoints,
  -"SINATRA_ROUTE_PREFIXES" => :sinatra_route_prefixes,
  -"ENABLE_SOURCE_LOCATIONS" => :enable_source_locations,
  # == Max Span Handling ==
  -"REPORT_MAX_SPANS_EXCEEDED" => :report_max_spans_exceeded,
  -"PRUNE_LARGE_TRACES" => :prune_large_traces,
  # == Skylight Remote ==
  -"AUTH_URL" => :auth_url,
  -"APP_CREATE_URL" => :app_create_url,
  -"MERGES_URL" => :merges_url,
  -"VALIDATION_URL" => :validation_url,
  -"AUTH_HTTP_DEFLATE" => :auth_http_deflate,
  -"AUTH_HTTP_CONNECT_TIMEOUT" => :auth_http_connect_timeout,
  -"AUTH_HTTP_READ_TIMEOUT" => :auth_http_read_timeout,
  -"REPORT_URL" => :report_url,
  -"REPORT_HTTP_DEFLATE" => :report_http_deflate,
  -"REPORT_HTTP_CONNECT_TIMEOUT" => :report_http_connect_timeout,
  -"REPORT_HTTP_READ_TIMEOUT" => :report_http_read_timeout,
  -"REPORT_HTTP_DISABLED" => :report_http_disabled,
  -"REPORT_USE_GRPC" => :report_use_grpc,
  -"REPORT_GRPC_URL" => :report_grpc_url,
  # == Native agent settings ==
  #
  -"LAZY_START" => :"daemon.lazy_start",
  -"DAEMON_EXEC_PATH" => :"daemon.exec_path",
  -"DAEMON_LIB_PATH" => :"daemon.lib_path",
  -"PIDFILE_PATH" => :"daemon.pidfile_path",
  -"SOCKDIR_PATH" => :"daemon.sockdir_path",
  -"BATCH_QUEUE_DEPTH" => :"daemon.batch_queue_depth",
  -"BATCH_SAMPLE_SIZE" => :"daemon.batch_sample_size",
  -"BATCH_FLUSH_INTERVAL" => :"daemon.batch_flush_interval",
  -"DAEMON_TICK_INTERVAL" => :"daemon.tick_interval",
  -"DAEMON_LOCK_CHECK_INTERVAL" => :"daemon.lock_check_interval",
  -"DAEMON_INACTIVITY_TIMEOUT" => :"daemon.inactivity_timeout",
  -"CLIENT_MAX_TRIES" => :"daemon.max_connect_tries",
  -"CLIENT_CONN_TRY_WIN" => :"daemon.connect_try_window",
  -"MAX_PRESPAWN_JITTER" => :"daemon.max_prespawn_jitter",
  -"DAEMON_WAIT_TIMEOUT" => :"daemon.wait_timeout",
  -"CLIENT_CHECK_INTERVAL" => :"daemon.client_check_interval",
  -"CLIENT_QUEUE_DEPTH" => :"daemon.client_queue_depth",
  -"CLIENT_WRITE_TIMEOUT" => :"daemon.client_write_timeout",
  -"SSL_CERT_PATH" => :"daemon.ssl_cert_path",
  -"ENABLE_TCP" => :"daemon.enable_tcp",
  -"TCP_PORT" => :"daemon.tcp_port",
  # == Legacy env vars ==
  #
  -"AGENT_LOCKFILE" => :"agent.lockfile",
  -"AGENT_SOCKFILE_PATH" => :"agent.sockfile_path",
  # == User config settings ==
  -"USER_CONFIG_PATH" => :user_config_path,
  # == Heroku settings ==
  -"HEROKU_DYNO_INFO_PATH" => :"heroku.dyno_info_path",
  # == Source Location ==
  -"SOURCE_LOCATION_IGNORED_GEMS" => :source_location_ignored_gems,
  -"SOURCE_LOCATION_CACHE_SIZE" => :source_location_cache_size,
  -"STANDALONE_DAEMON" => :standalone_daemon
}.freeze
KEY_TO_NATIVE_ENV =
{
  # We use different log files for native and Ruby, but the native code doesn't know this
  native_log_file: "LOG_FILE",
  native_log_level: "LOG_LEVEL"
}.freeze
SERVER_VALIDATE =
%i[].freeze
DEFAULT_IGNORED_SOURCE_LOCATION_GEMS =
[-"skylight", -"activesupport", -"activerecord"].freeze
REQUIRED_KEYS =
{
  authentication: "authentication token",
  hostname: "server hostname",
  auth_url: "authentication url",
  validation_url: "config validation url"
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::Logging

#config_for_logging, #debug, #error, #fmt, #info, #log, #log_context, #raise_on_error?, #t, #trace, #trace?, #warn

Constructor Details

#initialize(*args) ⇒ Config

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Config.



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/skylight/config.rb', line 235

def initialize(*args)
  attrs = {}

  attrs = args.pop.dup if args.last.is_a?(Hash)

  @values = {}
  @priority = {}
  @priority_regexp = nil
  @alert_logger = nil
  @logger = nil

  p = attrs.delete(:priority)

  if (@priority_key = args[0])
    @priority_regexp = /^#{Regexp.escape(priority_key)}\.(.+)$/
  end

  attrs.each { |k, v| self[k] = v }

  p&.each { |k, v| @priority[self.class.remap_key(k)] = v }
end

Instance Attribute Details

#alert_loggerObject



561
562
563
564
565
566
567
568
569
570
571
572
573
# File 'lib/skylight/config.rb', line 561

def alert_logger
  @alert_logger ||=
    MUTEX.synchronize do
      unless (l = @alert_logger)
        out = get(:alert_log_file)
        out = Util::AlertLogger.new(load_logger) if out == "-"

        l = create_logger(out, level: Logger::DEBUG)
      end

      l
    end
end

#loggerObject



543
544
545
# File 'lib/skylight/config.rb', line 543

def logger
  @logger ||= MUTEX.synchronize { load_logger }
end

#priority_keyObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



232
233
234
# File 'lib/skylight/config.rb', line 232

def priority_key
  @priority_key
end

Class Method Details

.default_valuesObject

Default values for Skylight configuration keys



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/skylight/config.rb', line 122

def self.default_values
  @default_values ||=
    begin
      ret = {
        # URLs
        auth_url: -"https://auth.skylight.io/agent",
        app_create_url: -"https://www.skylight.io/apps",
        merges_url: -"https://www.skylight.io/merges",
        validation_url: -"https://auth.skylight.io/agent/config",
        # Logging
        log_file: -"-",
        log_level: -"INFO",
        alert_log_file: -"-",
        log_sql_parse_errors: true,
        native_log_level: -"warn",
        # Features
        enable_segments: true,
        enable_sidekiq: false,
        sinatra_route_prefixes: false,
        enable_source_locations: true,
        # Deploys
        "heroku.dyno_info_path": -"/etc/heroku/dyno",
        report_rails_env: true,
        # Daemon
        "daemon.lazy_start": true,
        hostname: Util::Hostname.default_hostname,
        report_max_spans_exceeded: false,
        prune_large_traces: true,
        standalone_daemon: false
      }

      ret[:"daemon.ssl_cert_path"] = Util::SSL.ca_cert_file_or_default unless Util::Platform::OS == -"darwin"

      if Skylight.native?
        native_path = Skylight.libskylight_path

        ret[:"daemon.lib_path"] = native_path
        ret[:"daemon.exec_path"] = File.join(native_path, "skylightd")
      end

      ret
    end
end

.legacy_keysObject

Maps legacy config keys to new config keys



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

def self.legacy_keys
  @legacy_keys ||= { "agent.sockfile_path": :"daemon.sockdir_path", "agent.lockfile": :"daemon.pidfile_path" }
end

.load(opts = {}, env = ENV) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/skylight/config.rb', line 257

def self.load(opts = {}, env = ENV)
  attrs = {}
  path = opts.delete(:file)
  priority_key = opts.delete(:priority_key)
  priority_key ||= opts[:env] # if a priority_key is not given, use env if available

  if path
    error = nil
    begin
      attrs =
        YAML.safe_load(ERB.new(File.read(path)).result, permitted_classes: [], permitted_symbols: [], aliases: true)
      error = "empty file" unless attrs
      error = "invalid format" if attrs && !attrs.is_a?(Hash)
    rescue Exception => e
      error = e.message
    end

    raise ConfigError, "could not load config file; msg=#{error}" if error
  end

  # The key-value pairs in this `priority` option are inserted into the
  # config's @priority hash *after* anything listed under priority_key;
  # i.e., ENV takes precendence over priority_key
  attrs[:priority] = remap_env(env) if env

  config = new(priority_key, attrs)

  opts.each { |k, v| config[k] = v }

  config
end

.native_env_keysObject



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/skylight/config.rb', line 173

def self.native_env_keys
  @native_env_keys ||= %i[
    native_log_level
    native_log_file
    log_sql_parse_errors
    version
    root
    proxy_url
    hostname
    session_token
    auth_url
    auth_http_deflate
    auth_http_connect_timeout
    auth_http_read_timeout
    report_url
    report_http_deflate
    report_http_connect_timeout
    report_http_read_timeout
    report_http_disabled
    report_use_grpc
    report_grpc_url
    daemon.lazy_start
    daemon.exec_path
    daemon.lib_path
    daemon.pidfile_path
    daemon.sockdir_path
    daemon.batch_queue_depth
    daemon.batch_sample_size
    daemon.batch_flush_interval
    daemon.tick_interval
    daemon.lock_check_interval
    daemon.inactivity_timeout
    daemon.max_connect_tries
    daemon.connect_try_window
    daemon.max_prespawn_jitter
    daemon.wait_timeout
    daemon.client_check_interval
    daemon.client_queue_depth
    daemon.client_write_timeout
    daemon.ssl_cert_path
    daemon.ssl_cert_dir
    daemon.enable_tcp
    daemon.tcp_port
    standalone_daemon
  ]
end

.remap_env(env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/skylight/config.rb', line 295

def self.remap_env(env)
  ret = {}

  return ret unless env

  # Only set if it exists, we don't want to set to a nil value
  if (proxy_url = Util::Proxy.detect_url(env))
    ret[:proxy_url] = proxy_url
  end

  env.each do |k, val|
    next unless k =~ /^(?:SK|SKYLIGHT)_(.+)$/
    next unless (key = ENV_TO_KEY[$1])

    ret[key] = case val
    when /^false$/i
      false
    when /^true$/i
      true
    when /^(nil|null)$/i
      nil
    when /^\d+$/
      val.to_i
    when /^\d+\.\d+$/
      val.to_f
    else
      val
    end
  end

  ret
end

.remap_key(key) ⇒ Object



289
290
291
292
# File 'lib/skylight/config.rb', line 289

def self.remap_key(key)
  key = key.to_sym
  legacy_keys[key] || key
end

.validatorsObject



225
226
227
228
229
# File 'lib/skylight/config.rb', line 225

def self.validators
  @validators ||= {
    "agent.interval": [->(v, _c) { v.is_a?(Integer) && v > 0 }, "must be an integer greater than 0"]
  }
end

Instance Method Details

#apiObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



640
641
642
# File 'lib/skylight/config.rb', line 640

def api
  @api ||= Api.new(self)
end

#as_jsonObject



757
758
759
# File 'lib/skylight/config.rb', line 757

def as_json(*)
  { config: { priority: @priority.merge(component.as_json), values: @values } }
end

#authentication_with_metaObject

Helpers =====


720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
# File 'lib/skylight/config.rb', line 720

def authentication_with_meta
  token = get(:authentication)

  if token
    meta = {}
    meta.merge!(deploy.to_query_hash) if deploy
    meta[:reporting_env] = true if reporting_env?

    # A pipe should be a safe delimiter since it's not in the standard token
    # and is encoded by URI
    token += "|#{URI.encode_www_form(meta)}"
  end

  token
end

#check_file_permissions(file, key) ⇒ Object



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/skylight/config.rb', line 354

def check_file_permissions(file, key)
  file_root = File.dirname(file)

  # Try to make the directory, don't blow up if we can't. Our writable? check will fail later.
  begin
    FileUtils.mkdir_p file_root
  rescue StandardError
    nil
  end

  if File.exist?(file) && !FileTest.writable?(file)
    raise ConfigError, "File `#{file}` is not writable. Please set #{key} in your config to a writable path"
  end

  unless FileTest.writable?(file_root)
    raise ConfigError,
          "Directory `#{file_root}` is not writable. Please set #{key} in your config to a " \
            "writable path"
  end
end

#check_logfile_permissions(log_file, key) ⇒ Object



375
376
377
378
379
380
# File 'lib/skylight/config.rb', line 375

def check_logfile_permissions(log_file, key)
  return if log_file == "-" # STDOUT

  log_file = File.expand_path(log_file, root)
  check_file_permissions(log_file, key)
end

#check_sockdir_permissions(sockdir_path) ⇒ Object



683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
# File 'lib/skylight/config.rb', line 683

def check_sockdir_permissions(sockdir_path)
  # Try to make the directory, don't blow up if we can't. Our writable? check will fail later.
  begin
    FileUtils.mkdir_p sockdir_path
  rescue StandardError
    nil
  end

  unless FileTest.writable?(sockdir_path)
    raise ConfigError,
          "Directory `#{sockdir_path}` is not writable. Please set daemon.sockdir_path in " \
            "your config to a writable path"
  end

  if check_nfs(sockdir_path)
    raise ConfigError,
          "Directory `#{sockdir_path}` is an NFS mount and will not allow sockets. Please set " \
            "daemon.sockdir_path in your config to a non-NFS path."
  end
end

#componentObject



749
750
751
# File 'lib/skylight/config.rb', line 749

def component
  components[:web]
end

#componentsObject



740
741
742
743
744
745
746
747
# File 'lib/skylight/config.rb', line 740

def components
  @components ||= {
    web: Util::Component.new(get(:env), Util::Component::DEFAULT_NAME),
    worker: Util::Component.new(get(:env), get(:component) || get(:worker_component), force_worker: true)
  }
rescue ArgumentError => e
  raise ConfigError, e.message
end

#deployObject



736
737
738
# File 'lib/skylight/config.rb', line 736

def deploy
  @deploy ||= Util::Deploy.build(self)
end

#duration_ms(key, default = nil) ⇒ Object



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/skylight/config.rb', line 435

def duration_ms(key, default = nil)
  if (v = self[key]) && v.to_s =~ /^\s*(\d+)(s|sec|ms|micros|nanos)?\s*$/
    v = $1.to_i
    case $2
    when "ms"
      v
    when "micros"
      v / 1_000
    when "nanos"
      v / 1_000_000
    else
      # "s", "sec", nil
      v * 1000
    end
  else
    default
  end
end

#enable_segments?Boolean

Returns:

  • (Boolean)


575
576
577
# File 'lib/skylight/config.rb', line 575

def enable_segments?
  !!get(:enable_segments)
end

#enable_sidekiq?Boolean

Returns:

  • (Boolean)


579
580
581
# File 'lib/skylight/config.rb', line 579

def enable_sidekiq?
  !!get(:enable_sidekiq)
end

#enable_source_locations?Boolean

Returns:

  • (Boolean)


587
588
589
# File 'lib/skylight/config.rb', line 587

def enable_source_locations?
  !!get(:enable_source_locations)
end

#gcObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



482
483
484
# File 'lib/skylight/config.rb', line 482

def gc
  @gc ||= GC.new(self, get("gc.profiler", VM::GC.new))
end

#get(key, default = nil) ⇒ Object Also known as: []



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/skylight/config.rb', line 387

def get(key, default = nil)
  key = self.class.remap_key(key)

  return @priority[key] if @priority.key?(key)
  return @values[key] if @values.key?(key)
  return self.class.default_values[key] if self.class.default_values.key?(key)

  if default
    return default
  elsif block_given?
    return yield key
  end

  nil
end

#ignored_endpointsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/skylight/config.rb', line 487

def ignored_endpoints
  @ignored_endpoints ||=
    begin
      ignored_endpoints = get(:ignored_endpoints)

      # If, for some odd reason you have a comma in your endpoint name, use the
      # YML config instead.
      ignored_endpoints = ignored_endpoints.split(/\s*,\s*/) if ignored_endpoints.is_a?(String)

      val = Array(get(:ignored_endpoint))
      val.concat(Array(ignored_endpoints))
      val | DEFAULT_IGNORED_ENDPOINTS
    end
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


382
383
384
385
# File 'lib/skylight/config.rb', line 382

def key?(key)
  key = self.class.remap_key(key)
  @priority.key?(key) || @values.key?(key)
end

#log_levelObject



517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/skylight/config.rb', line 517

def log_level
  @log_level ||=
    if trace?
      Logger::DEBUG
    else
      case get(:log_level)
      when /^debug$/i
        Logger::DEBUG
      when /^info$/i
        Logger::INFO
      when /^warn$/i
        Logger::WARN
      when /^error$/i
        Logger::ERROR
      when /^fatal$/i
        Logger::FATAL
      else
        Logger::ERROR
      end
    end
end

#native_log_fileObject



547
548
549
550
551
552
553
554
555
556
557
# File 'lib/skylight/config.rb', line 547

def native_log_file
  @native_log_file ||=
    get("native_log_file") do
      log_file = self["log_file"]
      return "-" if log_file == "-"

      parts = log_file.to_s.split(".")
      parts.insert(-2, "native")
      parts.join(".")
    end
end

#native_log_levelObject



539
540
541
# File 'lib/skylight/config.rb', line 539

def native_log_level
  get(:native_log_level).to_s.downcase
end

#on_heroku?Boolean

Returns:

  • (Boolean)


595
596
597
# File 'lib/skylight/config.rb', line 595

def on_heroku?
  File.exist?(get(:"heroku.dyno_info_path"))
end

#rootObject



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

def root
  @root ||= Pathname.new(self[:root] || Dir.pwd).realpath
end

#send_or_get(val) ⇒ Object



431
432
433
# File 'lib/skylight/config.rb', line 431

def send_or_get(val)
  respond_to?(val) ? send(val) : get(val)
end

#set(key, val, scope = nil) ⇒ Object Also known as: []=



405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/skylight/config.rb', line 405

def set(key, val, scope = nil)
  key = [scope, key].join(".") if scope

  if val.is_a?(Hash)
    val.each { |k, v| set(k, v, key) }
  else
    k = self.class.remap_key(key)

    if (validator = self.class.validators[k])
      blk, msg = validator

      unless blk.call(val, self)
        error_msg = "invalid value for #{k} (#{val})"
        error_msg << ", #{msg}" if msg
        raise ConfigError, error_msg
      end
    end

    @priority[$1.to_sym] = val if @priority_regexp && k =~ @priority_regexp

    @values[k] = val
  end
end

#sinatra_route_prefixes?Boolean

Returns:

  • (Boolean)


583
584
585
# File 'lib/skylight/config.rb', line 583

def sinatra_route_prefixes?
  !!get(:sinatra_route_prefixes)
end

#source_location_ignored_gemsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



503
504
505
506
507
508
509
510
511
# File 'lib/skylight/config.rb', line 503

def source_location_ignored_gems
  @source_location_ignored_gems ||=
    begin
      ignored_gems = get(:source_location_ignored_gems)
      ignored_gems = ignored_gems.split(/\s*,\s*/) if ignored_gems.is_a?(String)

      Array(ignored_gems) | DEFAULT_IGNORED_SOURCE_LOCATION_GEMS
    end
end

#to_jsonObject



753
754
755
# File 'lib/skylight/config.rb', line 753

def to_json(*)
  JSON.generate(as_json)
end

#to_native_envObject



454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/skylight/config.rb', line 454

def to_native_env
  ret = []

  self.class.native_env_keys.each do |key|
    value = send_or_get(key)
    unless value.nil?
      env_key = KEY_TO_NATIVE_ENV[key] || ENV_TO_KEY.key(key) || key.upcase
      ret << "SKYLIGHT_#{env_key}" << cast_for_env(value)
    end
  end

  ret << "SKYLIGHT_AUTHENTICATION" << authentication_with_meta
  ret << "SKYLIGHT_VALIDATE_AUTHENTICATION" << "false"

  ret
end

#user_configObject



591
592
593
# File 'lib/skylight/config.rb', line 591

def user_config
  @user_config ||= UserConfig.new(self)
end

#validate!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/skylight/config.rb', line 329

def validate!
  REQUIRED_KEYS.each { |k, v| raise ConfigError, "#{v} required" unless get(k) }

  log_file = self[:log_file]
  alert_log_file = self[:alert_log_file]
  native_log_file = self.native_log_file

  check_logfile_permissions(log_file, "log_file")
  check_logfile_permissions(alert_log_file, "alert_log_file")

  # TODO: Support rotation interpolation in this check
  check_logfile_permissions(native_log_file, "native_log_file")

  # TODO: Move this out of the validate! method: https://github.com/tildeio/direwolf-agent/issues/273
  # FIXME: Why not set the sockdir_path and pidfile_path explicitly?
  # That way we don't have to keep this in sync with the Rust repo.
  sockdir_path = File.expand_path(self[:"daemon.sockdir_path"] || ".", root)
  pidfile_path = File.expand_path(self[:"daemon.pidfile_path"] || "skylight.pid", sockdir_path)

  check_file_permissions(pidfile_path, "daemon.pidfile_path or daemon.sockdir_path")
  check_sockdir_permissions(sockdir_path)

  true
end

#validate_with_serverObject



644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
# File 'lib/skylight/config.rb', line 644

def validate_with_server
  res = api.validate_config

  unless res.token_valid?
    warn("Invalid authentication token")
    return false
  end

  warn("Unable to reach server for config validation") if res.error_response?

  unless res.config_valid?
    warn("Invalid configuration") unless res.error_response?
    res.validation_errors.each { |k, v| warn("  #{k}: #{v}") }

    return false if res.forbidden?

    corrected_config = res.corrected_config

    # Use defaults if no corrected config is available. This will happen if the request failed.
    corrected_config ||= SERVER_VALIDATE.to_h { |k| [k, self.class.default_values.fetch(k)] }

    config_to_update = corrected_config.reject { |k, v| get(k) == v }
    unless config_to_update.empty?
      info("Updating config values:")
      config_to_update.each do |k, v|
        info("  setting #{k} to #{v}")

        # This is a weird way to handle priorities
        # See https://github.com/tildeio/direwolf-agent/issues/275
        k = "#{priority_key}.#{k}" if priority_key

        set(k, v)
      end
    end
  end

  true
end

#versionObject

Helpers =====


477
478
479
# File 'lib/skylight/config.rb', line 477

def version
  VERSION
end

#write(path) ⇒ Object



704
705
706
707
708
709
710
711
712
# File 'lib/skylight/config.rb', line 704

def write(path)
  FileUtils.mkdir_p(File.dirname(path))

  File.open(path, "w") { |f| f.puts <<~YAML }
      ---
      # The authentication token for the application.
      authentication: #{self[:authentication]}
    YAML
end