Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 91 additions & 96 deletions nexus/reconfigurator/planning/src/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2143,7 +2143,6 @@ pub(crate) mod test {
use nexus_types::deployment::OmicronZoneExternalSnatIp;
use nexus_types::deployment::SledDisk;
use nexus_types::deployment::TargetReleaseDescription;
use nexus_types::deployment::TufRepoPolicy;
use nexus_types::deployment::blueprint_zone_type;
use nexus_types::deployment::blueprint_zone_type::InternalDns;
use nexus_types::external_api::views::PhysicalDiskState;
Expand Down Expand Up @@ -5663,13 +5662,7 @@ pub(crate) mod test {
))
);

// This generation is successively incremented for each TUF repo. We use
// generation 2 to represent the first generation with a TUF repo
// attached.
let target_release_generation = Generation::from_u32(2);

// Manually specify a TUF repo with fake zone images.
let mut input_builder = example.input.clone().into_builder();
let version = ArtifactVersion::new_static("1.0.0-freeform")
.expect("can't parse artifact version");
let fake_hash = ArtifactHash([0; 32]);
Expand All @@ -5680,22 +5673,23 @@ pub(crate) mod test {
hash: fake_hash,
};
let artifacts = create_artifacts_at_version(&version);
let target_release_generation = target_release_generation.next();
input_builder.policy_mut().tuf_repo = TufRepoPolicy {
target_release_generation,
description: TargetReleaseDescription::TufRepo(
TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
artifacts,
let description =
TargetReleaseDescription::TufRepo(TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
),
};
artifacts,
});
example.system.set_target_release_and_old_repo(description);

let mut input_builder = example
.system
.to_planning_input_builder()
.expect("created PlanningInputBuilder");

// Some helper predicates for the assertions below.
let is_old_nexus = |zone: &BlueprintZoneConfig| -> bool {
Expand Down Expand Up @@ -5989,7 +5983,6 @@ pub(crate) mod test {
// The planner should avoid doing this update until it has confirmation
// from inventory that the cluster is healthy.

let mut input_builder = example.input.clone().into_builder();
let version = ArtifactVersion::new_static("1.0.0-freeform")
.expect("can't parse artifact version");
let fake_hash = ArtifactHash([0; 32]);
Expand All @@ -6000,23 +5993,24 @@ pub(crate) mod test {
hash: fake_hash,
};
let artifacts = create_artifacts_at_version(&version);
let target_release_generation = Generation::from_u32(2);
input_builder.policy_mut().tuf_repo = TufRepoPolicy {
target_release_generation,
description: TargetReleaseDescription::TufRepo(
TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
artifacts,
let description =
TargetReleaseDescription::TufRepo(TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
),
};
example.input = input_builder.build();
artifacts,
});
example.system.set_target_release_and_old_repo(description);

example.input = example
.system
.to_planning_input_builder()
.expect("created PlanningInputBuilder")
.build();

// Manually update all zones except Cockroach
//
Expand Down Expand Up @@ -6316,10 +6310,12 @@ pub(crate) mod test {
);

// Use that boundary NTP zone to promote others.
let mut input_builder = example.input.clone().into_builder();
input_builder.policy_mut().target_boundary_ntp_zone_count =
BOUNDARY_NTP_REDUNDANCY;
example.input = input_builder.build();
example.system.target_boundary_ntp_zone_count(BOUNDARY_NTP_REDUNDANCY);
example.input = example
.system
.to_planning_input_builder()
.expect("created PlanningInputBuilder")
.build();
let blueprint_name = "blueprint_with_boundary_ntp";
let new_blueprint = Planner::new_based_on(
log.clone(),
Expand Down Expand Up @@ -6410,7 +6406,6 @@ pub(crate) mod test {
// The planner should avoid doing this update until it has confirmation
// from inventory that the cluster is healthy.

let mut input_builder = example.input.clone().into_builder();
let version = ArtifactVersion::new_static("1.0.0-freeform")
.expect("can't parse artifact version");
let fake_hash = ArtifactHash([0; 32]);
Expand All @@ -6421,23 +6416,24 @@ pub(crate) mod test {
hash: fake_hash,
};
let artifacts = create_artifacts_at_version(&version);
let target_release_generation = Generation::from_u32(2);
input_builder.policy_mut().tuf_repo = TufRepoPolicy {
target_release_generation,
description: TargetReleaseDescription::TufRepo(
TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
artifacts,
let description =
TargetReleaseDescription::TufRepo(TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
),
};
example.input = input_builder.build();
artifacts,
});
example.system.set_target_release_and_old_repo(description);

example.input = example
.system
.to_planning_input_builder()
.expect("created PlanningInputBuilder")
.build();

// Manually update all zones except boundary NTP
//
Expand Down Expand Up @@ -6838,7 +6834,6 @@ pub(crate) mod test {
// The planner should avoid doing this update until it has confirmation
// from inventory that the Internal DNS servers are ready.

let mut input_builder = example.input.clone().into_builder();
let version = ArtifactVersion::new_static("1.0.0-freeform")
.expect("can't parse artifact version");
let fake_hash = ArtifactHash([0; 32]);
Expand All @@ -6849,23 +6844,25 @@ pub(crate) mod test {
hash: fake_hash,
};
let artifacts = create_artifacts_at_version(&version);
let target_release_generation = Generation::from_u32(2);
input_builder.policy_mut().tuf_repo = TufRepoPolicy {
target_release_generation,
description: TargetReleaseDescription::TufRepo(
TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
artifacts,

let description =
TargetReleaseDescription::TufRepo(TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
),
};
example.input = input_builder.build();
artifacts,
});
example.system.set_target_release_and_old_repo(description);

example.input = example
.system
.to_planning_input_builder()
.expect("created PlanningInputBuilder")
.build();

// Manually update all zones except Internal DNS
//
Expand Down Expand Up @@ -7102,7 +7099,6 @@ pub(crate) mod test {

// Manually specify a TUF repo with fake images for all zones.
// Only the name and kind of the artifacts matter.
let mut input_builder = example.input.clone().into_builder();
let version = ArtifactVersion::new_static("2.0.0-freeform")
.expect("can't parse artifact version");
let fake_hash = ArtifactHash([0; 32]);
Expand All @@ -7112,25 +7108,24 @@ pub(crate) mod test {
},
hash: fake_hash,
};
// We use generation 2 to represent the first generation with a TUF repo
// attached.
let target_release_generation = Generation::new().next();
let tuf_repo = TufRepoPolicy {
target_release_generation,
description: TargetReleaseDescription::TufRepo(
TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
artifacts: create_artifacts_at_version(&version),

let description =
TargetReleaseDescription::TufRepo(TufRepoDescription {
repo: TufRepoMeta {
hash: fake_hash,
targets_role_version: 0,
valid_until: Utc::now(),
system_version: Version::new(1, 0, 0),
file_name: String::from(""),
},
),
};
input_builder.policy_mut().tuf_repo = tuf_repo;
artifacts: create_artifacts_at_version(&version),
});
example.system.set_target_release_and_old_repo(description);

let input_builder = example
.system
.to_planning_input_builder()
.expect("created PlanningInputBuilder");
let input = input_builder.build();

/// Expected number of planner iterations required to converge.
Expand Down
39 changes: 37 additions & 2 deletions nexus/reconfigurator/planning/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ impl SystemDescription {
self.target_nexus_zone_count
}

pub fn target_boundary_ntp_zone_count(
&mut self,
count: usize,
) -> &mut Self {
self.target_boundary_ntp_zone_count = count;
self
}

pub fn get_target_boundary_ntp_zone_count(&self) -> usize {
self.target_boundary_ntp_zone_count
}

pub fn target_crucible_pantry_zone_count(
&mut self,
count: usize,
Expand Down Expand Up @@ -786,19 +798,42 @@ impl SystemDescription {
description,
};

self.tuf_repo = new_repo;
let _old_repo = self.set_tuf_repo_inner(new_repo);

// It's tempting to consider setting old_repo to the current tuf_repo,
// but that requires the invariant that old_repo is always the current
// target release and that an update isn't currently in progress. See
// https://github.com/oxidecomputer/omicron/issues/8056 for some
// discussion.
//
// We may want a more explicit operation to set the old repo, though.
// We provide a method to set the old repo explicitly with these
// assumptions in mind: `set_target_release_and_old_repo`.

self
}

pub fn set_target_release_and_old_repo(
&mut self,
description: TargetReleaseDescription,
) -> &mut Self {
let new_repo = TufRepoPolicy {
target_release_generation: self
.tuf_repo
.target_release_generation
.next(),
description,
};

let old_repo = self.set_tuf_repo_inner(new_repo);
self.old_repo = old_repo;

self
}

fn set_tuf_repo_inner(&mut self, new_repo: TufRepoPolicy) -> TufRepoPolicy {
mem::replace(&mut self.tuf_repo, new_repo)
}

pub fn set_ignore_impossible_mgs_updates_since(
&mut self,
since: DateTime<Utc>,
Expand Down
Loading