Skip to content

Commit

Permalink
Fixes a bug where a VM ends up in a state where it does not have a "r…
Browse files Browse the repository at this point in the history
…endered_templates_archive" in the spec json

Steps to reproduce:
- Deploy a VM
- Redeploy the VM without making any changes (this updates the spec json in the instances table to no longer have the rendered_templates_archive data since no templates were rendered)
- bosh restart vm/0 --no-converge

The restart action won't cause templates to be re-rendered, but will cause an update of the VM. Since rendered_templates_archive is no longer part of the spec json, the
agent thinks it should not be starting any jobs and "monit summary" will be empty.

This fix makes two changes.
- When sending rendered templates over the NATS bus to VMs, rather than through the blobstore, it will persist the entry into the "rendered_templates_archive" table. This seems
  a bit weird since the templates aren't actually in the blobstore, but this odd pattern is already in use. The spec json on the VM always has a "blobstore_id" even though the
  templates never went through the blobstore. So it's a bit odd, but it is consistent.
- When constructing the instances in the deployment plan from existing instance models, it will check if the database has the existing rendered templates archive and will merge
  that into the instance plan model. During a normal deploy, this will simply get replaced after the templates are rendered as part of the deployment process. However, this
  makes the template archive details available for deploys that do not end up rendering templates.
  • Loading branch information
jpalermo committed Dec 31, 2024
1 parent 05bdb51 commit 7ee9d1b
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ def bind_existing_instance_model(existing_instance_model)
@model = existing_instance_model
@desired_variable_set = @deployment_model.last_successful_variable_set || @deployment_model.current_variable_set
@previous_variable_set = existing_instance_model.variable_set
templates_archive = existing_instance_model.latest_rendered_templates_archive
if templates_archive
self.rendered_templates_archive = Core::Templates::RenderedTemplatesArchive.new(
templates_archive.blobstore_id,
templates_archive.sha1,
)
end
end

def bind_existing_reservations(reservations)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ def send_templates_to_agent(instance_plan)

rendered_templates_archive = instance_plan.rendered_templates.persist_through_agent(instance.agent_client)

instance.model.add_rendered_templates_archive(rendered_templates_archive.spec.merge({
content_sha1: instance.configuration_hash,
created_at: Time.now,
}))
instance.rendered_templates_archive = rendered_templates_archive
end
end
Expand Down
11 changes: 11 additions & 0 deletions src/bosh-director/spec/unit/deployment_plan/instance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ module Bosh::Director::DeploymentPlan
expect(instance.previous_variable_set).to eq(variable_set_model)
end
end

context 'if there is an existing rendered_templates_archive for the model' do
let!(:rendered_templates_archive) { FactoryBot.create(:models_rendered_templates_archive, instance: instance_model) }

it 'configures the instance to have the same rendered_templates_archive as the existing model' do
instance.bind_existing_instance_model(instance_model)

expect(instance.rendered_templates_archive.sha1).to eq(rendered_templates_archive.sha1)
expect(instance.rendered_templates_archive.blobstore_id).to eq(rendered_templates_archive.blobstore_id)
end
end
end

describe '#bind_new_instance_model' do
Expand Down
21 changes: 19 additions & 2 deletions src/bosh-director/spec/unit/rendered_templates_persister_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ module Bosh::Director
let(:instance_plan) { instance_double('Bosh::Director::DeploymentPlan::InstancePlan') }
let(:instance) { instance_double('Bosh::Director::DeploymentPlan::Instance') }

let(:rendered_templates_archive) { instance_double('Bosh::Director::Core::Templates::RenderedTemplatesArchive') }
let(:blobstore_id) { 'generated-blobstore-id' }
let(:sha1) { 'generated-sha1' }
let(:configuration_hash) { 'configuraiton-hash' }
let(:rendered_templates_archive) { Bosh::Director::Core::Templates::RenderedTemplatesArchive.new(blobstore_id, sha1) }

let(:rendered_job_instance) { instance_double('Bosh::Director::Core::Templates::RenderedJobInstance') }

Expand All @@ -32,6 +35,8 @@ module Bosh::Director

allow(instance).to receive(:rendered_templates_archive=)
allow(instance).to receive(:agent_client).and_return(agent_client)
allow(instance).to receive(:model).and_return(FactoryBot.create(:models_instance))
allow(instance).to receive(:configuration_hash).and_return(configuration_hash)

allow(SecureRandom).to receive(:uuid).and_return(blobstore_id)
end
Expand All @@ -55,7 +60,7 @@ module Bosh::Director

it 'should deliver the templates through NATS' do
allow(Base64).to receive(:encode64).with(template_contents).and_return(mock_base64_contents)
expect(rendered_job_instance).to receive(:persist_through_agent).with(agent_client)
expect(rendered_job_instance).to receive(:persist_through_agent).with(agent_client).and_return(rendered_templates_archive)

persister.persist(instance_plan)
end
Expand All @@ -67,6 +72,18 @@ module Bosh::Director
persister.persist(instance_plan)
end

it 'persists the rendered_templates_archive for the instance model' do
allow(rendered_job_instance).to receive(:persist_through_agent).with(agent_client).and_return(rendered_templates_archive)

persister.persist(instance_plan)

rendered_templates_archive = instance.model.latest_rendered_templates_archive
expect(rendered_templates_archive).to be
expect(rendered_templates_archive.content_sha1).to eq(configuration_hash)
expect(rendered_templates_archive.sha1).to eq(sha1)
expect(rendered_templates_archive.blobstore_id).to eq(blobstore_id)
end

context 'when persist through agent fails with AgentUnsupportedAction error' do
it 'should delegate to persist_to_blobstore' do
allow(rendered_job_instance).to receive(:persist_through_agent)
Expand Down

0 comments on commit 7ee9d1b

Please sign in to comment.