Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce on-stemcell-change variable strategy #2460

Merged
merged 1 commit into from Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -416,6 +416,7 @@ def initialize(config)
options['canaries'] = params[:canaries] if params['canaries']
options['max_in_flight'] = params[:max_in_flight] if params['max_in_flight']
options['scopes'] = token_scopes
options['force_latest_variables'] = true if params['force_latest_variables'] == 'true'

# since authorizer does not look at manifest payload for deployment name
@deployment = Models::Deployment[name: deployment_name]
Expand Down
96 changes: 53 additions & 43 deletions src/bosh-director/lib/bosh/director/config_server/client.rb
Expand Up @@ -6,6 +6,9 @@ class ConfigServerClient
GENERATION_MODE_CONVERGE = 'converge'.freeze
GENERATION_MODE_NO_OVERWRITE = 'no-overwrite'.freeze

ON_DEPLOY_UPDATE_STRATEGY = 'on-deploy'.freeze
ON_STEMCELL_CHANGE_UPDATE_STRATEGY = 'on-stemcell-change'.freeze

def initialize(http_client, director_name, logger)
@config_server_http_client = http_client
@director_name = director_name
Expand Down Expand Up @@ -86,29 +89,53 @@ def interpolate_cross_deployment_link(link_properties_hash, consumer_variable_se

# @param [DeploymentPlan::Variables] variables Object representing variables passed by the user
# @param [String] deployment_name
def generate_values(variables, deployment_name, converge_variables = false, use_link_dns_names = false)
def generate_values(variables, deployment_name, converge_variables = false, use_link_dns_names = false, stemcell_change = false)
deployment_model = @deployment_lookup.by_name(deployment_name)

variables.spec.map do |variable|
ConfigServerHelper.validate_variable_name(variable['name'])
constructed_name = ConfigServerHelper.add_prefix_if_not_absolute(variable['name'], @director_name, deployment_name)

strategy = variable.dig('update', 'strategy') || ON_DEPLOY_UPDATE_STRATEGY
use_latest_version =
strategy == ON_DEPLOY_UPDATE_STRATEGY ||
stemcell_change && strategy == ON_STEMCELL_CHANGE_UPDATE_STRATEGY ||
deployment_model.previous_variable_set&.find_variable_by_name(constructed_name).nil?

if use_latest_version
if variable['type'] == 'certificate'
has_ca = variable['options'] && variable['options']['ca']
generate_ca(variable, deployment_name) if has_ca
variable = generate_links(variable, deployment_model, use_link_dns_names)
end

if variable['type'] == 'certificate'
has_ca = variable['options'] && variable['options']['ca']
generate_ca(variable, deployment_name) if has_ca
variable = generate_links(variable, deployment_model, use_link_dns_names)
generation_mode = variable['update_mode']
generation_mode ||= converge_variables ? GENERATION_MODE_CONVERGE : GENERATION_MODE_NO_OVERWRITE

variable_id = generate_latest_version_id(
constructed_name,
variable['type'],
deployment_name,
deployment_model.current_variable_set,
variable['options'],
generation_mode,
)
else
previous_variable_version = deployment_model.previous_variable_set.find_variable_by_name(constructed_name)
variable_id = previous_variable_version[:variable_id]
end

generation_mode = variable['update_mode']
generation_mode ||= converge_variables ? GENERATION_MODE_CONVERGE : GENERATION_MODE_NO_OVERWRITE
begin
save_variable(get_name_root(constructed_name), deployment_model.current_variable_set, variable_id)
rescue Sequel::UniqueConstraintViolation
@logger.debug("variable '#{get_name_root(constructed_name)}' was already added to set '#{deployment_model.current_variable_set.id}'")
end

constructed_name = ConfigServerHelper.add_prefix_if_not_absolute(variable['name'], @director_name, deployment_name)
generate_value_and_record_event(
constructed_name,
variable['type'],
deployment_name,
deployment_model.current_variable_set,
variable['options'],
generation_mode,
add_event(
action: 'create',
deployment_name: deployment_name,
object_name: constructed_name,
context: { 'update_strategy' => strategy, 'latest_version' => use_latest_version, 'name' => constructed_name, 'id' => variable_id },
)

variable
Expand Down Expand Up @@ -395,25 +422,6 @@ def save_variable(name_root, variable_set, variable_id)
variable_set.add_variable(variable_name: name_root, variable_id: variable_id)
end

def generate_and_save_value(name, type, variable_set, options, generation_mode)
unless variable_set.writable
raise Bosh::Director::ConfigServerGenerationError,
"Variable '#{get_name_root(name)}' cannot be generated. Variable generation allowed only during deploy action"
end

generated_variable = generate_value(name, type, options, generation_mode)

raise Bosh::Director::ConfigServerGenerationError, "Failed to version generated variable '#{name}'. Expected Config Server response to have key 'id'" unless generated_variable.key?('id')

begin
save_variable(get_name_root(name), variable_set, generated_variable['id'])
rescue Sequel::UniqueConstraintViolation
@logger.debug("variable '#{get_name_root(name)}' was already added to set '#{variable_set.id}'")
end

generated_variable
end

def generate_value(name, type, options, mode)
parameters = options.nil? ? {} : options

Expand Down Expand Up @@ -458,15 +466,17 @@ def add_event(options)
)
end

def generate_value_and_record_event(variable_name, variable_type, deployment_name, variable_set, options, generation_mode)
result = generate_and_save_value(variable_name, variable_type, variable_set, options, generation_mode)
add_event(
action: 'create',
deployment_name: deployment_name,
object_name: variable_name,
context: { 'name' => result['name'], 'id' => result['id'] },
)
result
def generate_latest_version_id(variable_name, variable_type, deployment_name, variable_set, options, generation_mode)
unless variable_set.writable
raise Bosh::Director::ConfigServerGenerationError,
"Variable '#{get_name_root(variable_name)}' cannot be generated. Variable generation allowed only during deploy action"
end

generated_variable = generate_value(variable_name, variable_type, options, generation_mode)

raise Bosh::Director::ConfigServerGenerationError, "Failed to version generated variable '#{variable_name}'. Expected Config Server response to have key 'id'" unless generated_variable.key?('id')

generated_variable['id']
rescue Exception => e
add_event(
action: 'create',
Expand Down
Expand Up @@ -49,8 +49,8 @@ def interpolate_with_versioning(raw_hash, variable_set, options = {})
@config_server_client.interpolate_with_versioning(raw_hash, variable_set, options)
end

def generate_values(variables, deployment_name, converge_variables = false, use_link_dns_names = false)
@config_server_client.generate_values(variables, deployment_name, converge_variables, use_link_dns_names)
def generate_values(variables, deployment_name, converge_variables = false, use_link_dns_names = false, stemcell_change = false)
@config_server_client.generate_values(variables, deployment_name, converge_variables, use_link_dns_names, stemcell_change)
end

def interpolated_versioned_variables_changed?(previous_raw_hash, next_raw_hash, previous_variable_set, target_variable_set)
Expand Down
Expand Up @@ -25,6 +25,7 @@ def bind_models(options = {})
should_bind_links = is_deploy_action && options.fetch(:should_bind_links, true)
should_bind_properties = options.fetch(:should_bind_properties, true)
should_bind_new_variable_set = options.fetch(:should_bind_new_variable_set, false)
stemcell_change = options.fetch(:stemcell_change, false)
deployment_options = @deployment_plan.deployment_wide_options
fix = deployment_options.fetch(:fix, false)
tags = deployment_options.fetch(:tags, {})
Expand Down Expand Up @@ -100,7 +101,7 @@ def bind_models(options = {})
bind_instance_networks
bind_dns
bind_links if should_bind_links
generate_variables if is_deploy_action
generate_variables(stemcell_change) if is_deploy_action
end

private
Expand Down Expand Up @@ -169,12 +170,13 @@ def bind_links
end
end

def generate_variables
def generate_variables(stemcell_change)
@variables_interpolator.generate_values(
@deployment_plan.variables,
@deployment_plan.name,
@deployment_plan.features.converge_variables,
@deployment_plan.features.use_link_dns_names,
stemcell_change,
)
end

Expand Down
Expand Up @@ -26,7 +26,6 @@ class Planner
# Default job update configuration
attr_accessor :update

# @return [Array<Bosh::Director::DeploymentPlan::Job>]
# All instance_groups in the deployment
attr_reader :instance_groups

Expand Down
6 changes: 6 additions & 0 deletions src/bosh-director/lib/bosh/director/jobs/update_deployment.rb
Expand Up @@ -91,9 +91,15 @@ def prepare_deployment
# the DNS encoder having an updated index before bind_models is called
dns_encoder # TODO(ja): unit test that new_encoder_with_updated_index is called before bind_models

existing_stemcells = current_deployment.stemcells.map { |s| { os: s.operating_system, version: s.version } }.uniq
plan_stemcells = deployment_plan.stemcells.values.map { |s| { os: s.os || s.name, version: s.version } }.uniq

all_stemcell_versions_changed = (existing_stemcells.intersection(plan_stemcells)).empty?

DeploymentPlan::Assembler.create(deployment_plan, @variables_interpolator).bind_models(
is_deploy_action: deploy_action?,
should_bind_new_variable_set: deploy_action?,
stemcell_change: @options['force_latest_variables'] || all_stemcell_versions_changed
)
end
end
Expand Down
4 changes: 4 additions & 0 deletions src/bosh-director/lib/bosh/director/models/deployment.rb
Expand Up @@ -104,6 +104,10 @@ def current_variable_set
variable_sets_dataset.order(Sequel.desc(:created_at)).limit(1).first
end

def previous_variable_set
variable_sets_dataset.order(Sequel.desc(:created_at)).limit(2, 1).first
end

def last_successful_variable_set
variable_sets_dataset.where(deployed_successfully: true).order(Sequel.desc(:created_at)).limit(1).first
end
Expand Down
Expand Up @@ -421,6 +421,17 @@ def manifest_with_errand(deployment_name='errand')
post '/', spec_asset('test_manifest.yml'), { 'CONTENT_TYPE' => 'text/yaml' }
end
end

context 'with the "force_latest_variables" param which is needed because their BOSH DNS certs expire tomorrow and there is no new stemcell to trigger a cert rotation' do
it 'passes the parameter' do
expect_any_instance_of(DeploymentManager)
.to receive(:create_deployment)
.with(anything(), anything(), anything(), anything(), anything(), hash_including('force_latest_variables' => true), anything())
.and_return(OpenStruct.new(:id => 1))
post '/?force_latest_variables=true', spec_asset('test_conf.yaml'), {'CONTENT_TYPE' => 'text/yaml'}
expect(last_response).to be_redirect
end
end
end

describe 'deleting deployment' do
Expand Down