class SimplyGenius::Atmos::Config

Attributes

atmos_env[RW]
config_file[RW]
root_dir[RW]
tmp_root[RW]
user_config_file[RW]
working_group[RW]

Public Class Methods

new(atmos_env, working_group = 'default', root: ENV['ATMOS_ROOT'], config: ENV['ATMOS_CONFIG']) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 23
def initialize(atmos_env, working_group = 'default', root: ENV['ATMOS_ROOT'], config: ENV['ATMOS_CONFIG'])
  @atmos_env = atmos_env
  @working_group = working_group
  @root_dir = File.expand_path(root || Dir.pwd)
  @config_file = config ? File.expand_path(config, root_dir) : File.join(root_dir, "config", "atmos.yml")
  @user_config_file = "~/.atmos.yml"
  @tmp_root = File.join(root_dir, "tmp")
  @included_configs = {}
end

Public Instance Methods

[](key) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 37
def [](key)
  load
  result = @config.notation_get(key)
  return result
end
[]=(key, value, additive: true) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 43
def []=(key, value, additive: true)
  load
  result = @config.notation_put(key, value, additive: additive)
  return result
end
account_hash() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 67
def account_hash
  load
  environments = @full_config[:environments] || {}
  environments.inject(Hash.new) do |accum, entry|
    accum[entry.first] = entry.last[:account_id]
    accum
  end
end
add_user_load_path(*paths) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 103
def add_user_load_path(*paths)
  load_path = paths + Array(self["atmos.load_path"])
  if load_path.present?
    load_path = load_path.collect { |path| File.expand_path(path) }
    logger.debug("Adding to load path: #{load_path.inspect}")
    $LOAD_PATH.insert(0, *load_path)
  end
end
all_env_names() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 62
def all_env_names
  load
  (@full_config[:environments] || {}).keys
end
auth_cache_dir() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 85
def auth_cache_dir
  @auth_cache_dir ||= begin
    dir = File.join(tmp_dir, 'auth')
    logger.debug("Auth cache dir: #{dir}")
    mkdir_p(dir)
    dir
  end
end
config_merge(lhs, rhs, debug_state=[]) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 124
def config_merge(lhs, rhs, debug_state=[])
  result = nil

  return rhs if lhs.nil?
  return lhs if rhs.nil?

  # Warn if user fat fingered config
  if lhs.is_a?(rhs.class) || rhs.is_a?(lhs.class)

    case rhs
    when Hash
      result = lhs.deep_dup

      rhs.each do |k, v|
        new_state = debug_state + [k]

        # The load sequence could have multiple overrides and its not
        # obvious to the user which are lhs vs rhs, so rather than having
        # one seem to win arbitrarily, we collect them all additively
        # under their key, then at the end of the load process we push the
        # override back as a replacement for its base key in the
        # finalize_merge method. This means that if one has multiple
        # overrides for the same key (not usually what one wants), those
        # overrides get merged together additively before replacing the
        # base.  Thus the need for the log messages below.
        #
        if k =~ /^\^/
          logger.debug { "Override seen at #{new_state.join(" -> ")}" }
          logger.warn { "Multiple overrides on a single key seen at #{new_state.join(" -> ")}" } if result.has_key?(k)
        end

        result[k] = config_merge(result[k], v, new_state)
      end

    when Enumerable
      result = lhs + rhs
    else
      result = rhs
    end

  else

    logger.warn("Type mismatch while merging in: #{debug_state.delete_at(0)}")
    logger.warn("Deep merge path: #{debug_state.join(" -> ")}")
    logger.warn("Deep merge LHS (#{lhs.class}): #{lhs.inspect}")
    logger.warn("Deep merge RHS (#{rhs.class}): #{rhs.inspect}")
    result = rhs

  end

  return result
end
is_atmos_repo?() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 33
def is_atmos_repo?
  File.exist?(config_file)
end
plugin_manager() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 58
def plugin_manager
  @plugin_manager ||= PluginManager.new(self["atmos.plugins"])
end
provider() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 54
def provider
  @provider ||= ProviderFactory.get(self[:provider])
end
save_user_config_file(data, merge_to_existing: true) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 112
def save_user_config_file(data, merge_to_existing: true)
  logger.debug("Saving to user config file (merging=#{merge_to_existing}): #{user_config_file}")

  if merge_to_existing
    existing = load_file(user_config_file, SettingsHash.new)
    data = config_merge(existing, data, ["saving #{user_config_file}"])
    data = finalize_merge(data)
  end
  File.write(user_config_file, YAML.dump(data.to_hash))
  File.chmod(0600, user_config_file)
end
tf_working_dir() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 94
def tf_working_dir
  @tf_working_dir ||= begin
    dir = File.join(tmp_dir, 'tf', working_group)
    logger.debug("Terraform working dir: #{dir}")
    mkdir_p(dir)
    dir
  end
end
tmp_dir() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 76
def tmp_dir
  @tmp_dir ||= begin
    dir = File.join(tmp_root, atmos_env)
    logger.debug("Tmp dir: #{dir}")
    mkdir_p(dir)
    dir
  end
end
to_h() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 49
def to_h
  load
  @config.to_hash
end

Private Instance Methods

create_settings(config, finalize: true) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 312
def create_settings(config, finalize: true)
  builtins = {
      atmos_env: atmos_env,
      atmos_working_group: working_group,
      atmos_version: VERSION
  }

  global = SettingsHash.new(config.reject {|k, v| ['providers', 'environments'].include?(k) })
  conf = config_merge(global, builtins, ["builtins"])
  conf = finalize_merge(conf) if finalize

  conf.error_resolver = ->(statement) { find_config_error(statement) }
  conf.enable_expansion = true

  conf
end
finalize_merge(config) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 179
def finalize_merge(config)
  result = config.deep_dup

  config.each do |k, v|

    key = k

    if k =~ /^\^(.*)/
      key = k.is_a?(Symbol) ? $1.to_sym : $1
      result[key] = result.delete(k)
    end

    if v.is_a?(Hash)
      result[key] = finalize_merge(v)
    end

  end

  return result
end
find_config_error(statement) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 329
def find_config_error(statement)
  filename = nil
  line = 0

  @included_configs.each do |location, contents|
    current_line = 0
    contents.each_line do |line|
      current_line += 1
      if line.include?(statement)
        filename = location
        line = current_line
        break
      end
    end
  end

  return filename, line
end
load() click to toggle source
# File lib/simplygenius/atmos/config.rb, line 247
def load
  @config ||= begin

    logger.debug("Atmos env: #{atmos_env}")

    if ! File.exist?(config_file)
      logger.warn "Could not find an atmos config file at: #{config_file}"
      @full_config = SettingsHash.new
    else
      @full_config = load_file(config_file, SettingsHash.new)
    end

    @user_config_file = @full_config.notation_get("atmos.user_config") || @user_config_file
    @user_config_file = File.expand_path(@user_config_file)
    user_config_file_data = load_file(@user_config_file)
    temp_settings = create_settings(config_merge(@full_config, user_config_file_data, [@user_config_file]), finalize: true)

    @full_config = load_config_sources(File.dirname(config_file), @full_config, *Array(temp_settings.notation_get("atmos.config_sources")))
    @full_config = load_remote_config_sources(@full_config, *Array(temp_settings.notation_get("atmos.remote_config_sources")))

    @full_config['provider'] = provider_name = @full_config['provider'] || 'aws'

    @full_config = load_submap(File.dirname(config_file), 'providers', provider_name, @full_config)
    @full_config = load_submap(File.dirname(config_file), 'environments', atmos_env, @full_config)

    @full_config = config_merge(@full_config, user_config_file_data, [@user_config_file])

    conf = create_settings(@full_config, finalize: true)

    # hash emptied out to allow GC of all loaded file contents
    @included_configs = {}

    conf

  end
end
load_config(location, yml_string, config=SettingsHash.new, &block) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 296
def load_config(location, yml_string, config=SettingsHash.new, &block)
  data = YAML.load(yml_string)

  if ! data.is_a?(Hash)
    logger.debug("Skipping invalid atmos config (not hash-like): #{location}")
  else
    data = SettingsHash.new(data)
    data = block.call(data) if block
    # if lhs has a override ^, then it loses it when rhs gets merged in, which breaks things for subsequent merges
    config = config_merge(config, data, [location])
    @included_configs[location] = yml_string
  end

  config
end
load_config_sources(relative_root, config, *patterns) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 210
def load_config_sources(relative_root, config, *patterns)
  patterns.each do |pattern|
    logger.debug("Loading atmos config files using pattern: #{pattern}")

    # relative to main atmos config file unless qualified
    if pattern !~ /^[\/~]/
      pattern = File.join(relative_root, pattern)
    end
    # expand to handle tilde/etc
    pattern = File.expand_path(pattern)
    logger.debug("Expanded pattern: #{pattern}")

    Dir[pattern].sort.each do |f|
      config = load_file(f, config)
    end
  end

  config
end
load_file(file, config=SettingsHash.new, &block) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 284
def load_file(file, config=SettingsHash.new, &block)
  if File.exist?(file)
    logger.debug("Loading atmos config file #{file}")
    contents = File.read(file)
    config = load_config(file, contents, config, &block)
  else
    logger.debug   "Could not find an atmos config file at: #{file}"
  end

  config
end
load_remote_config_sources(config, *remote_sources) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 200
def load_remote_config_sources(config, *remote_sources)
  remote_sources.each do |remote_source|
    logger.debug("Loading remote atmos config file: #{remote_source}")
    contents = URI.open(remote_source).read
    config = load_config(remote_source, contents, config)
  end

  config
end
load_submap(relative_root, group, name, config) click to toggle source
# File lib/simplygenius/atmos/config.rb, line 230
def load_submap(relative_root, group, name, config)
  submap_dir = File.join(relative_root, 'atmos', group)
  submap_file = File.join(submap_dir, "#{name}.yml")
  config = load_file(submap_file, config) do |d|
    SettingsHash.new({group => {name => d}})
  end

  begin
    submap = config.deep_fetch(group, name)
    config = config_merge(config, submap, ["#{submap_file} submap(#{group}.#{name})"])
  rescue
    logger.debug("No #{group} config found for '#{name}'")
  end

  config
end