class Kontena::Cli::Stacks::UpgradeCommand

Public Instance Methods

execute() click to toggle source

@return [Kontena::Stacks::ChangeResolver]

# File lib/kontena/cli/stacks/upgrade_command.rb, line 32
def execute
  old_data = spinner "Reading stack #{pastel.cyan(stack_name)} from master" do
    gather_master_data(stack_name)
  end

  kontena_requirement = loader.yaml.dig('meta', 'required_kontena_version')
  unless kontena_requirement.nil?
    master_version = Gem::Version.new(client.server_version)
    unless Gem::Requirement.new(kontena_requirement).satisfied_by?(master_version)
      puts "#{pastel.red("Warning: ")} Stack requires kontena version #{kontena_requirement} but Master version is #{master_version}"
      confirm("Are you sure? You can skip this prompt by running this command with --force option") unless force?
    end
  end

  new_data = spinner "Parsing #{pastel.cyan(source)}" do
    loader.flat_dependencies(
      stack_name,
      variables: values_from_options
    )
  end

  changes = process_data(old_data, new_data)

  display_report(changes)

  return if dry_run?

  get_confirmation(changes)

  deployable_stacks = []
  deployable_stacks.concat run_installs(changes)
  deployable_stacks.concat run_upgrades(changes)

  run_deploys(deployable_stacks) if deploy?

  run_removes(changes.removed_stacks)

  changes
end

Private Instance Methods

deployable_stacks() click to toggle source
# File lib/kontena/cli/stacks/upgrade_command.rb, line 202
def deployable_stacks
  @deployable_stacks ||= []
end
display_report(changes) click to toggle source

@param changes [Kontena::Stacks::ChangeResolver]

# File lib/kontena/cli/stacks/upgrade_command.rb, line 134
def display_report(changes)
  puts
  caret "Calculated changes:", dots: false

  unless changes.removed_stacks.empty?
    changes.removed_stacks.each { |stack|
      puts "  #{pastel.red('-')} #{stack}"
      changes.removed_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.red('-')} #{s}"
      end
    }
  end

  unless changes.replaced_stacks.empty?
    changes.replaced_stacks.each { |stack|
      puts "  #{pastel.yellow('-/+')} #{stack}"
      changes.upgraded_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.cyan('~')} #{s}"
      end
      changes.added_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.green('+')} #{s}"
      end
      changes.removed_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.red('-')} #{s}"
      end
    }
    changes.replaced_stacks.each do |installed_name, data|
      puts "- #{pastel.yellow(installed_name)} from #{pastel.cyan(data[:from])} to #{pastel.cyan(data[:to])}"
    end
    puts
  end

  unless changes.added_stacks.empty?
    changes.added_stacks.each { |stack|
      puts "  #{pastel.green('+')} #{stack}"
      changes.added_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.green('+')} #{s}"
      end
    }
  end

  unless changes.upgraded_stacks.empty?
    changes.upgraded_stacks.each { |stack|
      puts "  #{pastel.cyan('~')} #{stack}"
      changes.upgraded_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.cyan('~')} #{s}"
      end
      changes.added_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.green('+')} #{s}"
      end
      changes.removed_services.select { |s| s.split('/')[0] == stack }.each do |s|
        puts "    #{pastel.red('-')} #{s}"
      end
    }
  end
  puts
end
fetch_master_data(stackname) click to toggle source
# File lib/kontena/cli/stacks/upgrade_command.rb, line 261
def fetch_master_data(stackname)
  client.get(stack_url(stackname))
end
gather_master_data(stackname) click to toggle source

Recursively fetch master data in StackFileLoader#flat_dependencies format @return [Hash{string => Hash}] stackname => hash

# File lib/kontena/cli/stacks/upgrade_command.rb, line 76
def gather_master_data(stackname)
  response = fetch_master_data(stackname)
  children = response.delete('children') || []
  result = { stackname => { stack_data: response } }
  children.each do |child|
    result.merge!(gather_master_data(child['name']))
  end
  result
end
get_confirmation(changes) click to toggle source

requires heavier confirmation when something very dangerous is going to happen

# File lib/kontena/cli/stacks/upgrade_command.rb, line 193
def get_confirmation(changes)
  unless force?
    unless changes.safe?
      puts "#{pastel.red('Warning:')} This can not be undone, data will be lost."
      confirm
    end
  end
end
process_data(old_data, new_data) click to toggle source

Preprocess data and return a ChangeResolver @param old_data [Hash] data from master @param new_data [Hash] data from files @return [Kontena::Cli::Stacks::ChangeRsolver]

# File lib/kontena/cli/stacks/upgrade_command.rb, line 90
def process_data(old_data, new_data)
  logger.debug { "Master stacks: #{old_data.keys.join(",")} YAML stacks: #{new_data.keys.join(",")}" }

  new_data.reverse_each do |stackname, data|
    spinner "Processing stack #{pastel.cyan(stackname)}"
    process_stack_data(stackname, data, old_data)
    hint_on_validation_notifications(reader.notifications, reader.loader.source)
    abort_on_validation_errors(reader.errors, reader.loader.source)
  end

  old_set = Kontena::Stacks::StackDataSet.new(old_data)
  new_set = Kontena::Stacks::StackDataSet.new(new_data)
  if skip_dependencies?
    [old_set, new_set].each(&:remove_dependencies)
  end
  spinner "Analyzing upgrade" do
    Kontena::Stacks::ChangeResolver.new(old_set, new_set)
  end
end
process_stack_data(stackname, data, old_data) click to toggle source

@param stackname [String] @param data [Hash] @param old_data [Hash]

# File lib/kontena/cli/stacks/upgrade_command.rb, line 113
def process_stack_data(stackname, data, old_data)
  prev_env = ENV.clone
  reader = data[:loader].reader
  values = data[:variables]
  if reuse_values? && old_data[stackname]
    old_vars = old_data[stackname][:stack_data]['variables']
    values = old_vars.merge(values)
  end
  set_env_variables(stackname, current_grid) # set envs for execution time
  parsed_stack = reader.execute(
    values: values,
    defaults: old_data[stackname].nil? ? nil : old_data[stackname][:stack_data]['variables'],
    parent_name: data[:parent_name],
    name: data[:name]
  )
  data[:stack_data] = parsed_stack
ensure
  prev_env.each { |k, v| ENV[k] = v }
end
run_deploys(deployable_stacks) click to toggle source

@param deployable_stacks [Array<String>] an array of stack names that should be deployed

# File lib/kontena/cli/stacks/upgrade_command.rb, line 247
def run_deploys(deployable_stacks)
  deployable_stacks.each do |deployable_stack|
    Kontena.run!(['stack', 'deploy', deployable_stack])
  end
end
run_installs(changes) click to toggle source

@param changes [Kontena::Stacks::ChangeResolver] @return [Array] an array of stack names that have been installed, but not yet deployed

# File lib/kontena/cli/stacks/upgrade_command.rb, line 215
def run_installs(changes)
  deployable_stacks = []
  changes.added_stacks.reverse_each do |added_stack|
    data = changes.new_data[added_stack]
    cmd = ['stack', 'install', '--name', added_stack, '--no-deploy']
    cmd.concat ['--parent-name', data.parent] unless data.root?
    data.variables.each do |k,v|
      cmd.concat ['-v', "#{k}=#{v}"]
    end
    cmd << data.loader.source
    caret "Installing new dependency #{cmd.last} as #{added_stack}"
    deployable_stacks << added_stack
    Kontena.run!(cmd)
  end
  deployable_stacks
end
run_removes(removed_stacks) click to toggle source

@param removed_stacks [Array<String>]

# File lib/kontena/cli/stacks/upgrade_command.rb, line 207
def run_removes(removed_stacks)
  removed_stacks.reverse_each do |removed_stack|
    Kontena.run!('stack', 'remove', '--force', '--keep-dependencies', removed_stack)
  end
end
run_upgrades(changes) click to toggle source

@param changes [Kontena::Stacks::ChangeResolver] @return [Array] an array of stack names that have been upgraded, but not yet deployed

# File lib/kontena/cli/stacks/upgrade_command.rb, line 234
def run_upgrades(changes)
  deployable_stacks = []
  changes.upgraded_stacks.reverse_each do |upgraded_stack|
    data = changes.new_data[upgraded_stack]
    spinner "Upgrading #{data.root? ? 'stack' : 'dependency'} #{pastel.cyan(upgraded_stack)}" do |spin|
      deployable_stacks << upgraded_stack
      update_stack(upgraded_stack, data.data) || spin.fail!
    end
  end
  deployable_stacks
end
stack_url(name) click to toggle source
# File lib/kontena/cli/stacks/upgrade_command.rb, line 257
def stack_url(name)
  "stacks/#{current_grid}/#{name}"
end
update_stack(name, data) click to toggle source
# File lib/kontena/cli/stacks/upgrade_command.rb, line 253
def update_stack(name, data)
  client.put(stack_url(name), data)
end