Skip to content

Commit 9ff91cf

Browse files
committed
Review comments
1 parent ca44ead commit 9ff91cf

File tree

1 file changed

+115
-58
lines changed

1 file changed

+115
-58
lines changed

rs/registry/admin/bin/main.rs

Lines changed: 115 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,24 +1610,28 @@ impl ProposalAction for ProposeToUpdateCanisterSettingsCmd {
16101610
#[derive(Parser, ProposalMetadata)]
16111611
struct ProposeToBlessAlternativeGuestOsVersionCmd {
16121612
/// Subnet to query for chip IDs and launch measurements.
1613-
/// If specified, chip_ids and base_launch_measurements are automatically fetched from the registry.
1613+
/// If specified, base_launch_measurements are automatically fetched from the registry.
1614+
/// If --node-ids or --chip-ids are also provided, they act as filters (must be part of the subnet).
1615+
/// Otherwise, chip IDs for all subnet members are used.
16141616
/// Either --subnet or (--chip-ids/--node-ids and --base-launch-measurements) must be provided.
16151617
#[clap(
16161618
long,
1617-
conflicts_with_all = &["chip_ids", "base_launch_measurements", "node_ids"],
1619+
conflicts_with = "base_launch_measurements",
16181620
required_unless_present_any = &["chip_ids", "node_ids"]
16191621
)]
16201622
subnet: Option<SubnetDescriptor>,
16211623

16221624
/// Node IDs of nodes to bless the alternative Guest OS version on.
1623-
/// Either --subnet or (--chip-ids/--node-ids and --base-launch-measurements) must be provided.
1624-
#[clap(long, value_parser = parse_node_id)]
1625+
/// If --subnet is provided, all the node IDs in this list must be part of that subnet.
1626+
/// If --subnet is not provided, --base-launch-measurements must be specified.
1627+
#[clap(long, num_args(1..), value_parser = parse_node_id, conflicts_with = "chip_ids")]
16251628
node_ids: Vec<NodeId>,
16261629

16271630
/// Hex-encoded chip IDs (AMD Secure Processor chip IDs). Each must be exactly 64 bytes.
16281631
/// Multiple values can be passed.
1629-
/// Either --subnet or (--chip-ids/--node-ids and --base-launch-measurements) must be provided.
1630-
#[clap(long, requires = "base_launch_measurements", conflicts_with = "subnet")]
1632+
/// If --subnet is provided, all the chip IDs must belong to nodes in that subnet.
1633+
/// If --subnet is not provided, --base-launch-measurements must be specified.
1634+
#[clap(long, num_args(1..), conflicts_with = "node_ids")]
16311635
chip_ids: Vec<String>,
16321636

16331637
/// Hexadecimal fingerprint of the Recovery-GuestOS rootfs that is allowed to run on the nodes
@@ -1650,38 +1654,81 @@ impl ProposalTitle for ProposeToBlessAlternativeGuestOsVersionCmd {
16501654
None => {
16511655
if let Some(subnet) = self.subnet {
16521656
format!(
1653-
"Bless alternative Guest OS for with rootfs_hash={} on subnet {}",
1657+
"Bless alternative Guest OS with rootfs hash {} on subnet {}",
16541658
self.rootfs_hash,
16551659
shortened_subnet_string(&subnet)
16561660
)
16571661
} else {
1658-
format!("Bless alternative Guest OS for hash {}", self.rootfs_hash)
1662+
format!(
1663+
"Bless alternative Guest OS with rootfs hash {} for {} nodes",
1664+
self.rootfs_hash,
1665+
self.node_ids.len() + self.chip_ids.len()
1666+
)
16591667
}
16601668
}
16611669
}
16621670
}
16631671
}
16641672

16651673
impl ProposeToBlessAlternativeGuestOsVersionCmd {
1666-
/// Fetches chip IDs and launch measurements from the registry for a given subnet.
1667-
async fn get_chip_ids_and_measurements_from_subnet(
1668-
subnet: &SubnetDescriptor,
1669-
agent: &Agent,
1670-
) -> (Vec<Vec<u8>>, ic_nns_governance_api::GuestLaunchMeasurements) {
1671-
let registry_canister = RegistryCanister::new_with_agent(agent.clone());
1674+
/// Returns chip IDs for a subnet, optionally filtered by node IDs or chip IDs.
1675+
///
1676+
/// `select_node_ids` and `select_chip_ids` are mutually exclusive and must refer to a subset
1677+
/// of the subnet's nodes.
1678+
async fn get_chip_ids_from_subnet(
1679+
subnet_descriptor: &SubnetDescriptor,
1680+
subnet_record: &SubnetRecord,
1681+
select_node_ids: &[NodeId],
1682+
select_chip_ids: &[String],
1683+
registry_canister: &RegistryCanister,
1684+
) -> Vec<Vec<u8>> {
1685+
assert!(
1686+
select_node_ids.is_empty() || select_chip_ids.is_empty(),
1687+
"--node-ids and --chip-ids are mutually exclusive"
1688+
);
16721689

1673-
let subnet_record = Self::fetch_subnet_record(&registry_canister, subnet).await;
1674-
let node_ids: Vec<NodeId> = Self::get_subnet_member_node_ids(&subnet_record);
1675-
let chip_ids = Self::get_chip_ids_from_node_ids(&registry_canister, &node_ids, false).await;
1690+
let subnet_member_node_ids = Self::get_subnet_member_node_ids(subnet_record);
16761691

1677-
// Query the ReplicaVersionRecord to get guest_launch_measurements
1678-
let guest_launch_measurements = Self::get_guest_launch_measurements_from_replica_version(
1679-
&registry_canister,
1680-
&subnet_record.replica_version_id,
1681-
)
1682-
.await;
1692+
// No selection: use all nodes in the subnet.
1693+
if select_node_ids.is_empty() && select_chip_ids.is_empty() {
1694+
return Self::get_chip_ids_from_node_ids(
1695+
registry_canister,
1696+
&subnet_member_node_ids,
1697+
false,
1698+
)
1699+
.await;
1700+
}
1701+
1702+
// Select node IDs: validate membership, then fetch their chip IDs.
1703+
if !select_node_ids.is_empty() {
1704+
let subnet_node_id_set: HashSet<&NodeId> = subnet_member_node_ids.iter().collect();
1705+
for node_id in select_node_ids {
1706+
assert!(
1707+
subnet_node_id_set.contains(node_id),
1708+
"Node ID {node_id} is not a member of subnet {}",
1709+
shortened_subnet_string(subnet_descriptor),
1710+
);
1711+
}
1712+
return Self::get_chip_ids_from_node_ids(registry_canister, select_node_ids, true)
1713+
.await;
1714+
}
16831715

1684-
(chip_ids, guest_launch_measurements)
1716+
// Select chip IDs: validate against the subnet's actual chip IDs.
1717+
let all_subnet_chip_ids: HashSet<Vec<u8>> =
1718+
Self::get_chip_ids_from_node_ids(registry_canister, &subnet_member_node_ids, false)
1719+
.await
1720+
.into_iter()
1721+
.collect();
1722+
let decoded = Self::decode_chip_ids(select_chip_ids);
1723+
for chip_id in &decoded {
1724+
assert!(
1725+
all_subnet_chip_ids.contains(chip_id),
1726+
"Chip ID {} does not belong to any node in subnet {}",
1727+
hex::encode(chip_id),
1728+
shortened_subnet_string(subnet_descriptor),
1729+
);
1730+
}
1731+
decoded
16851732
}
16861733

16871734
fn get_subnet_member_node_ids(subnet_record: &SubnetRecord) -> Vec<NodeId> {
@@ -1786,14 +1833,11 @@ impl ProposeToBlessAlternativeGuestOsVersionCmd {
17861833
});
17871834

17881835
// Execute all futures in parallel and collect results
1789-
let chip_ids = futures::future::join_all(futures).await;
1790-
let chip_ids: Vec<_> = chip_ids.into_iter().flatten().collect();
1791-
1792-
if chip_ids.is_empty() {
1793-
panic!("No chip IDs found for subnet");
1794-
}
1795-
1796-
chip_ids
1836+
futures::future::join_all(futures)
1837+
.await
1838+
.into_iter()
1839+
.flatten()
1840+
.collect()
17971841
}
17981842

17991843
/// Queries the ReplicaVersionRecord and extracts GuestLaunchMeasurements.
@@ -1837,36 +1881,49 @@ impl ProposeToBlessAlternativeGuestOsVersionCmd {
18371881
#[async_trait]
18381882
impl ProposalAction for ProposeToBlessAlternativeGuestOsVersionCmd {
18391883
async fn action(&self, agent: &Agent) -> ProposalActionRequest {
1840-
let (chip_ids, measurements) = if let Some(subnet) = &self.subnet {
1841-
Self::get_chip_ids_and_measurements_from_subnet(subnet, agent).await
1842-
} else if let (chip_ids, node_ids, Some(base_launch_measurements)) = (
1843-
&self.chip_ids,
1844-
&self.node_ids,
1845-
&self.base_launch_measurements,
1846-
) && chip_ids.len() + node_ids.len() > 0
1847-
{
1848-
let mut chip_ids = Self::decode_chip_ids(chip_ids);
1849-
if !node_ids.is_empty() {
1850-
let registry_canister = RegistryCanister::new_with_agent(agent.clone());
1851-
let chip_ids_from_node_ids =
1852-
Self::get_chip_ids_from_node_ids(&registry_canister, node_ids, true).await;
1853-
if chip_ids_from_node_ids.len() != node_ids.len() {
1854-
panic!("Not all node IDs provided have a chip ID in the registry.");
1855-
}
1856-
chip_ids.extend(chip_ids_from_node_ids);
1857-
}
1884+
let chip_ids: Vec<Vec<u8>>;
1885+
let measurements: ic_nns_governance_api::GuestLaunchMeasurements;
1886+
let registry_canister = RegistryCanister::new_with_agent(agent.clone());
18581887

1859-
(
1860-
chip_ids,
1861-
read_from_json_file::<ic_nns_governance_api::GuestLaunchMeasurements>(
1862-
base_launch_measurements,
1863-
),
1888+
if let Some(subnet) = &self.subnet {
1889+
let subnet_record = Self::fetch_subnet_record(&registry_canister, subnet).await;
1890+
1891+
measurements = Self::get_guest_launch_measurements_from_replica_version(
1892+
&registry_canister,
1893+
&subnet_record.replica_version_id,
18641894
)
1895+
.await;
1896+
chip_ids = Self::get_chip_ids_from_subnet(
1897+
subnet,
1898+
&subnet_record,
1899+
&self.node_ids,
1900+
&self.chip_ids,
1901+
&registry_canister,
1902+
)
1903+
.await;
1904+
1905+
assert!(
1906+
!chip_ids.is_empty(),
1907+
"No SEV nodes with chip IDs found in subnet {}",
1908+
shortened_subnet_string(subnet)
1909+
);
18651910
} else {
1866-
panic!(
1867-
"Either --subnet or (--chip-ids/--node-ids and --base-launch-measurements) must be provided"
1911+
measurements = read_from_json_file::<ic_nns_governance_api::GuestLaunchMeasurements>(
1912+
self.base_launch_measurements.as_ref().expect(
1913+
"--base-launch-measurements must be provided if --subnet is not provided",
1914+
),
18681915
);
1869-
};
1916+
1917+
if !self.chip_ids.is_empty() && self.node_ids.is_empty() {
1918+
chip_ids = Self::decode_chip_ids(&self.chip_ids);
1919+
} else if !self.node_ids.is_empty() && self.chip_ids.is_empty() {
1920+
chip_ids =
1921+
Self::get_chip_ids_from_node_ids(&registry_canister, &self.node_ids, true)
1922+
.await;
1923+
} else {
1924+
panic!("Exactly one of --chip-ids or --node-ids must be provided");
1925+
}
1926+
}
18701927

18711928
ProposalActionRequest::BlessAlternativeGuestOsVersion(
18721929
ic_nns_governance_api::BlessAlternativeGuestOsVersion {

0 commit comments

Comments
 (0)