class Bosh::Cli::Release

This class encapsulates the details of handling dev and final releases: also it partitions release metadata between final config (which is under version control) and user dev config.

Attributes

dir[R]

Public Class Methods

new(dir, final = false) click to toggle source
# File lib/cli/release.rb, line 10
def initialize(dir, final = false)
  @dir = dir
  @final = final

  config_dir = File.join(dir, "config")
  @final_config_file = File.join(config_dir, "final.yml")
  @dev_config_file = File.join(config_dir, "dev.yml")

  @private_config_file = File.join(config_dir, "private.yml")

  unless File.directory?(dir)
    err("Cannot find release directory `#{dir}'")
  end

  unless File.directory?(config_dir)
    err("Cannot find release config directory `#{config_dir}'")
  end

  @final_config = load_config(@final_config_file)
  @dev_config = load_config(@dev_config_file)
  @private_config = load_config(@private_config_file)

  migrate_legacy_configs
end

Public Instance Methods

blobstore() click to toggle source

Picks blobstore client to use with current release.

@return [Bosh::Blobstore::Client] blobstore client

# File lib/cli/release.rb, line 96
def blobstore
  return @blobstore if @blobstore
  blobstore_config = Marshal.load(Marshal.dump(@final_config["blobstore"]))

  if blobstore_config.nil?
    err("Missing blobstore configuration, please update config/final.yml") if @final
    unless @user_warned
      warning("Missing blobstore configuration, please update config/final.yml before making a final release")
      @user_warned = true
    end
    return nil
  end

  provider = blobstore_config["provider"]
  options  = blobstore_config["options"] || {}

  options = merge_private_data(provider, options)

  opts = Bosh::Common.symbolize_keys(options)
  @blobstore = Bosh::Blobstore::Client.safe_create(provider, opts)

rescue Bosh::Blobstore::BlobstoreError => e
  err("Cannot initialize blobstore: #{e}")
end
has_blobstore_secret?() click to toggle source
# File lib/cli/release.rb, line 64
def has_blobstore_secret?
  bs = @private_config["blobstore"]

  return false unless @final_config['blobstore']

  # Add special handling for local blobstore which should not need need credentials
  provider = @final_config['blobstore']['provider']
  return true if provider == 'local'

  has_legacy_secret? ||
    has_blobstore_secrets?(bs, "simple", "user", "password") ||
    has_blobstore_secrets?(bs, "dav", "user", "password") ||
    has_blobstore_secrets?(bs, "s3", "access_key_id", "secret_access_key")
end
has_legacy_secret?() click to toggle source

Check if the deprecated blobstore secret is provided in the private config file @return [Boolean]

# File lib/cli/release.rb, line 60
def has_legacy_secret?
  @private_config.has_key?("blobstore_secret")
end
save_config() click to toggle source
# File lib/cli/release.rb, line 121
def save_config
  write_yaml(@dev_config_file, @dev_config)
  write_yaml(@final_config_file, @final_config)
end

Private Instance Methods

has_blobstore_secrets?(blobstore, name, *keys) click to toggle source
# File lib/cli/release.rb, line 128
def has_blobstore_secrets?(blobstore, name, *keys)
  return false unless blobstore
  return false unless blobstore[name]
  keys.each {|key| return false unless blobstore[name][key]}
  true
end
load_config(file) click to toggle source
# File lib/cli/release.rb, line 184
def load_config(file)
  if File.exists?(file)
    load_yaml_file(file)
  else
    {}
  end
end
merge_private_data(provider, options) click to toggle source

Extracts private blobstore data from final.yml (i.e. secrets) and merges it into the blobstore options.

# File lib/cli/release.rb, line 138
def merge_private_data(provider, options)
  err("Missing blobstore secret configuration, please update config/private.yml") if @final && !has_blobstore_secret?

  bs = @private_config["blobstore"]
  return options unless bs

  if bs[provider].nil? && has_blobstore_secret?
    err("blobstore private provider does not match final provider")
  end

  options.merge(bs[provider] ? bs[provider] : {})
end
migrate_legacy_configs() click to toggle source

Upgrade path for legacy clients that kept release metadata in config/dev.yml and config/final.yml

# File lib/cli/release.rb, line 154
def migrate_legacy_configs
  # We're using blobstore_options as old config marker.
  # Unfortunately old CLI won't tell you to upgrade because it checks
  # for valid blobstore options first, so instead of removing
  # blobstore_options we mark it as deprecated, so new CLI proceeds
  # to migrate while the old one tells you to upgrade.
  if @dev_config.has_key?("blobstore_options") &&
      @dev_config["blobstore_options"] != "deprecated"
    say("Found legacy dev config file `#{@dev_config_file}'".make_yellow)

    new_dev_config = {
      "dev_name" => @dev_config["name"],
      "latest_release_filename" =>
          @dev_config["latest_release_filename"],

      # Following two options are only needed for older clients
      # to fail gracefully and never actually read by a new client
      "blobstore_options" => "deprecated",
    }

    @dev_config = new_dev_config

    File.open(@dev_config_file, "w") do |f|
      Psych.dump(@dev_config, f)
    end
    say("Migrated dev config file format".make_green)
  end

end
write_yaml(file, hash) click to toggle source
# File lib/cli/release.rb, line 192
def write_yaml(file, hash)
  unless hash == load_config(file)
    File.open(file, "w+") do |f|
      Psych.dump(hash, f)
    end
  end
end