class KubeDeployTools::DeployConfigFile

Read-only model for the deploy.yaml configuration file.

Attributes

artifact_registries[RW]
artifact_registry[RW]
artifacts[RW]
default_flags[RW]
expiration[RW]
flavors[RW]
hooks[RW]
image_registries[RW]
valid_image_registries[RW]

Public Class Methods

deep_merge(h, other) click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 286
def self.deep_merge(h, other)

end
new(filename) click to toggle source

TODO(joshk): Refactor into initialize(fp) which takes a file-like object; after this, auto discovery should go into DeployConfigFile.locate classmethod. This would require erasing auto-upgrade capability, which should be possible if we major version bump.

# File lib/kube_deploy_tools/deploy_config_file.rb, line 28
def initialize(filename)
  config = nil
  if !filename.nil? && Pathname.new(filename).absolute?
    config = YAML.load_file(filename)
  else
    original_dir = Dir.pwd
    changed_dir = false
    until Dir.pwd == '/'
      # Try looking for filename specified by user.
      # If no filename was specified by the user, then look for
      # deploy.yml or deploy.yaml.
      if !filename.nil? && File.exist?(filename)
        config = YAML.load_file(filename)
        break
      elsif filename.nil? && File.exist?(DEPLOY_YAML)
        filename = DEPLOY_YAML
        config = YAML.load_file(filename)
        break
      end

      # KDT should run in the directory containing the deploy config file.
      changed_dir = true
      Dir.chdir('..')
    end
    if config.nil?
      Dir.chdir(original_dir)
      if ! filename.nil?
        raise "Could not locate file: config file '#{filename}' in any directory"
      else
        raise "Could not locate file: config file '#{DEPLOY_YAML}' in any directory"
      end
    end
    if changed_dir
      Logger.warn "Changed directory to #{Dir.pwd} (location of #{filename})"
    end
  end
  @filename = filename
  @original_config = config

  version = config.fetch('version', 1)
  check_and_warn(
    config.has_key?('version'),
    'Expected .version to be specified, but .version is missing. Falling back to version 1 config schema')
  check_and_err([1, 2].include?(version), "Expected valid version, but received unsupported version '#{version}'")

  case version
  when 2
    fetch_and_parse_version2_config!
  else
    raise "Unsupported version #{version}"
  end
end

Public Instance Methods

extend!(other) click to toggle source

Extend this DeployConfigFile with another instance.

# File lib/kube_deploy_tools/deploy_config_file.rb, line 245
def extend!(other)
  # Any image_registries entry in |self| should take precedence
  # over any identical key in |other|. The behavior of merge is that
  # the 'other' hash wins.
  @image_registries = other.image_registries.merge(@image_registries)

  @artifact_registries = other.artifact_registries.merge(@artifact_registries)

  @artifact_registry = other.artifact_registry if @artifact_registry.empty?

  # Same behavior as above for #default_flags.
  @default_flags = other.default_flags.merge(@default_flags)

  # artifacts should be merged by 'name'. In other words, if |self| and |other|
  # specify the same 'name' of a registry, self's config for that registry
  # should win wholesale (no merging of flags.)
  @artifacts = (@artifacts + other.artifacts).uniq { |h| h.fetch('name') }

  # Same behavior as for flags and registries, but the flags within the flavor
  # are in a Hash, so we need a deep merge.
  @flavors = other.flavors.deep_merge(@flavors)

  # A break from the preceding merging logic - Dependent hooks have to come
  # first and a given named hook can only be run once. But seriously, you
  # probably don't want to make a library that specifies hooks.
  @hooks = (other.hooks + @hooks).uniq

  @expiration = (@expiration + other.expiration).uniq { |h| h.fetch('repository') }
end
fetch_and_parse_version2_config!() click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 81
def fetch_and_parse_version2_config!
  # The literal contents of your deploy.yaml are now populated into |self|.
  config = @original_config
  @image_registries = parse_image_registries(config.fetch('image_registries', []))
  @default_flags = config.fetch('default_flags', {})
  @artifacts = config.fetch('artifacts', [])
  @flavors = config.fetch('flavors', {})
  @hooks = config.fetch('hooks', ['default'])
  @expiration = config.fetch('expiration', [])
  @artifact_registries = parse_artifact_registries(config.fetch('artifact_registries', []))
  @artifact_registry = parse_artifact_registry(config.fetch('artifact_registry', ''), @artifact_registries)

  validate_default_flags
  validate_flavors
  validate_hooks
  validate_expiration

  # Augment these literal contents by resolving all libraries.
  # extend! typically gives the current file precedence when merge conflicts occur,
  # but the expected precedence of library inclusion is the reverse (library 2 should
  # overwrite what library 1 specifies), so reverse the libraries list first.
  config.fetch('libraries', []).reverse.each do |libfn|
    extend!(load_library(libfn))
  end

  # Now that we have a complete list of image registries, validation is now possible.
  # Note that this also populates @valid_image_registries.
  validate_artifacts!
end
map_image_registry(image_registries) click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 127
def map_image_registry(image_registries)
  valid_image_registries = {}
  image_registries.each do |reg_name, reg_info|
    valid_image_registries[reg_name] = reg_info.prefix
  end
  valid_image_registries
end
parse_artifact_registries(artifact_registries) click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 187
def parse_artifact_registries(artifact_registries)
  check_and_err(artifact_registries.is_a?(Array), '.artifact_registries is not an Array')
  artifact_registries = artifact_registries.map { |r| ArtifactRegistry.new(r) }

  # Validate that each artifact registry is named uniquely
  duplicates = select_duplicates(artifact_registries.map { |r| r.name })
  check_and_err(
    duplicates.count == 0,
    "Expected .artifact_registries names to be unique, but found duplicates: #{duplicates}"
  )

  unsupported_drivers = artifact_registries.
    select { |r| !ArtifactRegistry::Driver::MAPPINGS.key? r.driver_name }.
    map { |r| r.driver_name }
  check_and_err(
    unsupported_drivers.count == 0,
    "Expected .artifact_registries drivers to be valid, but found unsupported drivers: #{unsupported_drivers}. Must be a driver in: #{ArtifactRegistry::Driver::MAPPINGS.keys}",
  )

  artifact_registries
    .select { |r| r.driver_name == "gcs" }
    .select { |r| !r.config.has_key? "bucket" }
    .each { |r| check_and_err(false, "Expected .artifact_registries['#{r.config.name}'].config.bucket to exist, but no GCS bucket is specified") }


  artifact_registries
    .map { |r| [r.name, r] }
    .to_h
end
parse_artifact_registry(artifact_registry, artifact_registries) click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 217
def parse_artifact_registry(artifact_registry, artifact_registries)
  check_and_err(artifact_registry.is_a?(String), '.artifact_registry is not a String')
  check_and_err(
    artifact_registry.empty? || artifact_registries.key?(artifact_registry),
    "#{artifact_registry} is not a valid Artifact Registry. Has to be one of #{artifact_registries.keys}"
  )

  artifact_registry
end
parse_image_registries(image_registries) click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 111
def parse_image_registries(image_registries)
  check_and_err(image_registries.is_a?(Array), '.image_registries is not an Array')
  image_registries = image_registries.map { |i| ImageRegistry.new(i) }

  # Validate that only one instance of each driver is registered
  duplicates = select_duplicates(image_registries.map { |i| i.name })
  check_and_err(
    duplicates.count == 0,
    "Expected .image_registries names to be unique, but found duplicates: #{duplicates}"
  )

  image_registries
    .map { |i| [i.name, i] }
    .to_h
end
select_duplicates(array) click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 240
def select_duplicates(array)
  array.select { |n| array.count(n) > 1 }.uniq
end
to_h() click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 275
def to_h
  {
    'image_registries' => @image_registries.values.map(&:to_h),
    'default_flags' => @default_flags,
    'artifacts' => @artifacts,
    'flavors' => @flavors,
    'hooks' => @hooks,
    'expiration' => @expiration,
  }
end
upgrade!() click to toggle source

upgrade! converts the config to a YAML string in the format of the latest supported version e.g. with the latest supported version as v2, to_yaml will always print a valid v2 YAML

# File lib/kube_deploy_tools/deploy_config_file.rb, line 231
def upgrade!
  version = @original_config.fetch('version', 1)
  case version
  when 2
    # TODO(joshk): Any required updates to v3 or remove this entire method
    true
  end
end
validate_artifacts!() click to toggle source

.artifacts depends on .default_flags and .image_registries

# File lib/kube_deploy_tools/deploy_config_file.rb, line 136
def validate_artifacts!
  check_and_err(artifacts.is_a?(Array), '.artifacts is not an Array')

  duplicates = select_duplicates(artifacts.map { |i| i.fetch('name') })
  check_and_err(
    duplicates.count == 0,
    "Expected .artifacts names to be unique, but found duplicates: #{duplicates}"
  )

  @valid_image_registries = map_image_registry(@image_registries)

  artifacts.each_with_index { |artifact, index|
    check_and_err(
      artifact.key?('name'),
      "Expected .artifacts[#{index}].name key to exist, but .name is missing"
    )
    name = artifact.fetch('name')
    check_and_err(
      artifact.key?('image_registry'),
      "Expected .artifacts[#{index}].image_registry key to exist, but .image_registry is missing"
    )

    image_registry = artifact.fetch('image_registry')
    check_and_err(
      @valid_image_registries.key?(image_registry),
      "#{image_registry} is not a valid Image Registry. Has to be one of #{@valid_image_registries.keys}"
    )

    check_and_err(
      artifact.key?('flags'),
      "Expected .artifacts.#{name}.flags key to exist, but .flags is missing"
    )
  }
end
validate_default_flags() click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 171
def validate_default_flags
  check_and_err(@default_flags.is_a?(Hash), '.default_flags is not a Hash')
end
validate_expiration() click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 183
def validate_expiration
  check_and_err(@expiration.is_a?(Array), '.expiration is not an Array')
end
validate_flavors() click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 175
def validate_flavors
  check_and_err(@flavors.is_a?(Hash), '.flavors is not a Hash')
end
validate_hooks() click to toggle source
# File lib/kube_deploy_tools/deploy_config_file.rb, line 179
def validate_hooks
  check_and_err(@hooks.is_a?(Array), '.hooks is not an Array')
end