Skip to content

Commit 8019b67

Browse files
committed
Plan according to RFD 565 §9
1 parent 08c316d commit 8019b67

File tree

11 files changed

+508
-292
lines changed

11 files changed

+508
-292
lines changed

common/src/api/external/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,10 @@ impl Generation {
748748
);
749749
Generation(next_gen)
750750
}
751+
752+
pub const fn prev(&self) -> Option<Generation> {
753+
if self.0 > 1 { Some(Generation(self.0 - 1)) } else { None }
754+
}
751755
}
752756

753757
impl<'de> Deserialize<'de> for Generation {

nexus-sled-agent-shared/src/inventory.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use omicron_common::{
1616
DatasetManagementStatus, DatasetsConfig, DiskManagementStatus,
1717
DiskVariant, OmicronPhysicalDisksConfig,
1818
},
19+
update::ArtifactId,
1920
zpool_name::ZpoolName,
2021
};
2122
use omicron_uuid_kinds::{DatasetUuid, OmicronZoneUuid};
@@ -26,7 +27,7 @@ use serde::{Deserialize, Serialize};
2627
// depend on sled-hardware-types.
2728
pub use sled_hardware_types::Baseboard;
2829
use strum::EnumIter;
29-
use tufaceous_artifact::ArtifactHash;
30+
use tufaceous_artifact::{ArtifactHash, KnownArtifactKind};
3031

3132
/// Identifies information about disks which may be attached to Sleds.
3233
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
@@ -492,13 +493,14 @@ impl OmicronZoneType {
492493
///
493494
/// # String representations of this type
494495
///
495-
/// There are no fewer than four string representations for this type, all
496+
/// There are no fewer than five string representations for this type, all
496497
/// slightly different from each other.
497498
///
498499
/// 1. [`Self::zone_prefix`]: Used to construct zone names.
499500
/// 2. [`Self::service_prefix`]: Used to construct SMF service names.
500501
/// 3. [`Self::name_prefix`]: Used to construct `Name` instances.
501502
/// 4. [`Self::report_str`]: Used for reporting and testing.
503+
/// 5. [`Self::artifact_name`]: Used to match TUF artifact names.
502504
///
503505
/// There is no `Display` impl to ensure that users explicitly choose the
504506
/// representation they want. (Please play close attention to this! The
@@ -636,6 +638,20 @@ impl ZoneKind {
636638
ZoneKind::Oximeter => "oximeter",
637639
}
638640
}
641+
642+
/// Return true if an artifact represents a control plane zone image
643+
/// of this kind.
644+
pub fn is_control_plane_zone_artifact(
645+
self,
646+
artifact_id: &ArtifactId,
647+
) -> bool {
648+
artifact_id
649+
.kind
650+
.to_known()
651+
.map(|kind| matches!(kind, KnownArtifactKind::Zone))
652+
.unwrap_or(false)
653+
&& artifact_id.name == self.artifact_name()
654+
}
639655
}
640656

641657
/// Where Sled Agent should get the image for a zone.

nexus/db-queries/src/db/datastore/target_release.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
use super::DataStore;
88
use crate::authz;
99
use crate::context::OpContext;
10-
use crate::db::model::{SemverVersion, TargetRelease, TargetReleaseSource};
10+
use crate::db::model::{
11+
Generation, SemverVersion, TargetRelease, TargetReleaseSource,
12+
};
1113
use async_bb8_diesel::AsyncRunQueryDsl as _;
1214
use diesel::insert_into;
1315
use diesel::prelude::*;
@@ -44,6 +46,25 @@ impl DataStore {
4446
Ok(current)
4547
}
4648

49+
/// Fetch a target release by generation number.
50+
pub async fn target_release_get_generation(
51+
&self,
52+
opctx: &OpContext,
53+
generation: Generation,
54+
) -> LookupResult<Option<TargetRelease>> {
55+
opctx
56+
.authorize(authz::Action::Read, &authz::TARGET_RELEASE_CONFIG)
57+
.await?;
58+
let conn = self.pool_connection_authorized(opctx).await?;
59+
dsl::target_release
60+
.select(TargetRelease::as_select())
61+
.filter(dsl::generation.eq(generation))
62+
.first_async(&*conn)
63+
.await
64+
.optional()
65+
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
66+
}
67+
4768
/// Insert a new target release row and return it. It will only become
4869
/// the current target release if its generation is larger than any
4970
/// existing row.

nexus/reconfigurator/execution/src/dns.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,7 @@ mod test {
14251425
clickhouse_policy: None,
14261426
oximeter_read_policy: OximeterReadPolicy::new(1),
14271427
tuf_repo: None,
1428+
old_repo: None,
14281429
log,
14291430
}
14301431
.build()

nexus/reconfigurator/planning/src/blueprint_builder/builder.rs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::blueprint_editor::ExternalSnatNetworkingChoice;
1414
use crate::blueprint_editor::NoAvailableDnsSubnets;
1515
use crate::blueprint_editor::SledEditError;
1616
use crate::blueprint_editor::SledEditor;
17+
use crate::planner::OrderedComponent;
1718
use crate::planner::ZoneExpungeReason;
1819
use crate::planner::rng::PlannerRng;
1920
use anyhow::Context as _;
@@ -33,7 +34,6 @@ use nexus_types::deployment::BlueprintSledConfig;
3334
use nexus_types::deployment::BlueprintZoneConfig;
3435
use nexus_types::deployment::BlueprintZoneDisposition;
3536
use nexus_types::deployment::BlueprintZoneImageSource;
36-
use nexus_types::deployment::BlueprintZoneImageVersion;
3737
use nexus_types::deployment::BlueprintZoneType;
3838
use nexus_types::deployment::ClickhouseClusterConfig;
3939
use nexus_types::deployment::CockroachDbPreserveDowngrade;
@@ -57,6 +57,7 @@ use omicron_common::address::DNS_PORT;
5757
use omicron_common::address::NTP_PORT;
5858
use omicron_common::address::ReservedRackSubnet;
5959
use omicron_common::api::external::Generation;
60+
use omicron_common::api::external::TufRepoDescription;
6061
use omicron_common::api::external::Vni;
6162
use omicron_common::api::internal::shared::NetworkInterface;
6263
use omicron_common::api::internal::shared::NetworkInterfaceKind;
@@ -83,7 +84,6 @@ use std::net::Ipv6Addr;
8384
use std::net::SocketAddr;
8485
use std::net::SocketAddrV6;
8586
use thiserror::Error;
86-
use tufaceous_artifact::KnownArtifactKind;
8787

8888
use super::ClickhouseZonesThatShouldBeRunning;
8989
use super::clickhouse::ClickhouseAllocator;
@@ -1903,34 +1903,55 @@ impl<'a> BlueprintBuilder<'a> {
19031903
self.pending_mgs_updates.remove(baseboard_id);
19041904
}
19051905

1906-
/// Try to find an artifact in the release repo that contains an image
1907-
/// for a zone of the given kind. Defaults to the install dataset.
1906+
fn zone_image_artifact(
1907+
repo: Option<&TufRepoDescription>,
1908+
zone_kind: ZoneKind,
1909+
) -> BlueprintZoneImageSource {
1910+
repo.and_then(|repo| {
1911+
repo.artifacts
1912+
.iter()
1913+
.find(|artifact| {
1914+
zone_kind.is_control_plane_zone_artifact(&artifact.id)
1915+
})
1916+
.map(BlueprintZoneImageSource::from_available_artifact)
1917+
})
1918+
.unwrap_or(BlueprintZoneImageSource::InstallDataset)
1919+
}
1920+
1921+
/// Try to find an artifact in either the current or previous release repo
1922+
/// that contains an image for a zone of the given kind; see RFD 565 §9.
1923+
/// Defaults to the install dataset.
19081924
pub(crate) fn zone_image_source(
19091925
&self,
19101926
zone_kind: ZoneKind,
19111927
) -> BlueprintZoneImageSource {
1912-
self.input
1913-
.tuf_repo()
1914-
.and_then(|repo| {
1915-
repo.artifacts
1916-
.iter()
1917-
.find(|artifact| {
1918-
artifact
1919-
.id
1920-
.kind
1921-
.to_known()
1922-
.map(|kind| matches!(kind, KnownArtifactKind::Zone))
1923-
.unwrap_or(false)
1924-
&& artifact.id.name == zone_kind.artifact_name()
1925-
})
1926-
.map(|artifact| BlueprintZoneImageSource::Artifact {
1927-
version: BlueprintZoneImageVersion::Available {
1928-
version: artifact.id.version.clone(),
1929-
},
1930-
hash: artifact.hash,
1928+
let new_repo = self.input.tuf_repo();
1929+
let old_repo = self.input.old_repo();
1930+
let new_artifact = Self::zone_image_artifact(new_repo, zone_kind);
1931+
let old_artifact = Self::zone_image_artifact(old_repo, zone_kind);
1932+
if let Some(prev) = OrderedComponent::from(zone_kind).prev() {
1933+
if prev >= OrderedComponent::ControlPlaneZone
1934+
&& self.sled_ids_with_zones().any(|sled_id| {
1935+
self.current_sled_zones(
1936+
sled_id,
1937+
BlueprintZoneDisposition::is_in_service,
1938+
)
1939+
.any(|z| {
1940+
let kind = z.zone_type.kind();
1941+
let old_artifact =
1942+
Self::zone_image_artifact(old_repo, kind);
1943+
OrderedComponent::from(kind) == prev
1944+
&& z.image_source == old_artifact
19311945
})
1932-
})
1933-
.unwrap_or(BlueprintZoneImageSource::InstallDataset)
1946+
})
1947+
{
1948+
old_artifact
1949+
} else {
1950+
new_artifact
1951+
}
1952+
} else {
1953+
new_artifact
1954+
}
19341955
}
19351956
}
19361957

0 commit comments

Comments
 (0)