Skip to content

Commit 25943dc

Browse files
committed
test(upgrade): limit Debian qualification to test coverage
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent fe82250 commit 25943dc

17 files changed

Lines changed: 133 additions & 851 deletions

File tree

‎.github/workflows/branch-e2e.yml‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,8 @@ jobs:
188188
cargo-version: ${{ needs.version.outputs.cargo }}
189189

190190
build-vm-driver:
191-
# The Debian package embeds this binary, so unconditional package builds
192-
# require the VM driver artifact even when no E2E suite is selected.
193191
needs: [pr_metadata, version, build-binaries]
194-
if: needs.pr_metadata.outputs.should_run == 'true'
192+
if: needs.pr_metadata.outputs.run_core_e2e == 'true' || needs.pr_metadata.outputs.run_integration_upgrades == 'true'
195193
permissions:
196194
contents: read
197195
uses: ./.github/workflows/build-vm-driver.yml
@@ -237,7 +235,7 @@ jobs:
237235
build-deb:
238236
name: Build Debian packages
239237
needs: [pr_metadata, version, build-binaries, build-vm-driver]
240-
if: needs.pr_metadata.outputs.should_run == 'true'
238+
if: needs.pr_metadata.outputs.run_integration == 'true' || needs.pr_metadata.outputs.run_integration_upgrades == 'true'
241239
permissions:
242240
actions: read
243241
contents: read

‎CI.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,8 @@ to the package installers and downloads the packages selected by its matrix.
128128

129129
Four opt-in labels enable the long-running E2E suites:
130130

131-
Every approved `Branch E2E Checks` run builds the DEB and RPM packages, even
132-
when no optional E2E label is present. The `test:upgrade` label controls only
133-
the tmachine upgrade execution and its runtime-image preparation.
131+
The `test:upgrade` label enables the Debian package and VM driver builds,
132+
runtime-image preparation, and tmachine upgrade execution needed by that suite.
134133

135134
- `test:e2e` runs the Docker, rootless Podman, Kubernetes, and VM E2E suites
136135
with both managed and standalone compute drivers in `Branch E2E Checks`

‎crates/openshell-driver-docker/src/isolation.rs‎

Lines changed: 32 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,15 @@ use openshell_isolation_interface::contract::{
1616
};
1717
use openshell_sandbox_backend::GPU_RESOURCE_CLAIM;
1818
use openshell_sandbox_backend::boundary_protocol::{
19-
BoundaryConfig, BoundaryListener, FenceWireFormat, GatewayVerificationKey,
20-
SandboxRuntimeDescriptor, SandboxTlsClientConfig, SandboxTlsServerConfig, SandboxTransport,
21-
fence_wire_format,
19+
BoundaryConfig, BoundaryListener, GatewayVerificationKey, SandboxRuntimeDescriptor,
20+
SandboxTlsClientConfig, SandboxTlsServerConfig, SandboxTransport,
2221
};
23-
use serde::de::DeserializeOwned;
24-
use serde::{Deserialize, Serialize};
25-
26-
#[derive(Deserialize, Serialize)]
27-
#[serde(tag = "backend", rename_all = "kebab-case", deny_unknown_fields)]
28-
enum LegacyDriverFenceEvidence {
29-
Docker {
30-
container_id: String,
31-
network_mode: String,
32-
unexpected_networks: Vec<String>,
33-
},
34-
}
22+
use serde::Serialize;
3523

3624
#[derive(Serialize)]
3725
struct DockerOuterFenceEvidence<'a> {
3826
container_id: &'a str,
39-
network_mode: &'a str,
27+
network_mode: &'static str,
4028
unexpected_networks: &'a [String],
4129
}
4230

@@ -70,127 +58,6 @@ impl DockerOuterFenceEvidence<'_> {
7058
}
7159
}
7260

73-
fn decode_fence_compatible<T: DeserializeOwned>(
74-
encoded: &[u8],
75-
description: &str,
76-
) -> Result<(T, FenceWireFormat), BackendError> {
77-
let mut value: serde_json::Value = serde_json::from_slice(encoded)
78-
.map_err(|error| BackendError::Descriptor(format!("decode {description}: {error}")))?;
79-
let object = value.as_object_mut().ok_or_else(|| {
80-
BackendError::Descriptor(format!("decode {description}: expected a JSON object"))
81-
})?;
82-
let format = fence_wire_format(object, description)?;
83-
if format == FenceWireFormat::LegacyDriverFence {
84-
let generation = object
85-
.get("generation")
86-
.and_then(serde_json::Value::as_str)
87-
.ok_or_else(|| {
88-
BackendError::Descriptor(format!(
89-
"decode {description}: legacy bootstrap generation is missing"
90-
))
91-
})?
92-
.to_string();
93-
let legacy: LegacyDriverFenceEvidence = serde_json::from_value(
94-
object.remove("driver_fence").expect("checked above"),
95-
)
96-
.map_err(|error| {
97-
BackendError::Descriptor(format!("decode {description} legacy driver fence: {error}"))
98-
})?;
99-
let LegacyDriverFenceEvidence::Docker {
100-
container_id,
101-
network_mode,
102-
unexpected_networks,
103-
} = legacy;
104-
let projection = DockerOuterFenceEvidence {
105-
container_id: &container_id,
106-
network_mode: &network_mode,
107-
unexpected_networks: &unexpected_networks,
108-
}
109-
.project(&generation)?;
110-
object.insert(
111-
"outer_fence".to_string(),
112-
serde_json::to_value(projection).map_err(|error| {
113-
BackendError::Descriptor(format!("encode migrated Docker outer fence: {error}"))
114-
})?,
115-
);
116-
}
117-
serde_json::from_value(value)
118-
.map(|decoded| (decoded, format))
119-
.map_err(|error| BackendError::Descriptor(format!("decode {description}: {error}")))
120-
}
121-
122-
pub fn decode_boundary_config_compatible(
123-
encoded: &[u8],
124-
) -> Result<(BoundaryConfig, FenceWireFormat), BackendError> {
125-
decode_fence_compatible(encoded, "Docker boundary config")
126-
}
127-
128-
pub fn decode_runtime_descriptor_compatible(
129-
encoded: &[u8],
130-
) -> Result<(SandboxRuntimeDescriptor, FenceWireFormat), BackendError> {
131-
decode_fence_compatible(encoded, "Docker runtime descriptor")
132-
}
133-
134-
fn encode_fence_compatible<T: Serialize>(
135-
value: &T,
136-
format: FenceWireFormat,
137-
description: &str,
138-
) -> Result<Vec<u8>, BackendError> {
139-
let mut value = serde_json::to_value(value)
140-
.map_err(|error| BackendError::Descriptor(format!("encode {description}: {error}")))?;
141-
if format == FenceWireFormat::LegacyDriverFence {
142-
let object = value.as_object_mut().ok_or_else(|| {
143-
BackendError::Descriptor(format!("encode {description}: expected a JSON object"))
144-
})?;
145-
let container_id = object
146-
.get("resource_claims")
147-
.and_then(serde_json::Value::as_object)
148-
.and_then(|claims| claims.get("docker.container_id"))
149-
.and_then(serde_json::Value::as_str)
150-
.filter(|container_id| !container_id.is_empty())
151-
.ok_or_else(|| {
152-
BackendError::Descriptor(format!(
153-
"encode {description}: Docker container resource claim is missing"
154-
))
155-
})?
156-
.to_string();
157-
if object.remove("outer_fence").is_none() {
158-
return Err(BackendError::Descriptor(format!(
159-
"encode {description}: outer fence projection is missing"
160-
)));
161-
}
162-
object.insert(
163-
"driver_fence".to_string(),
164-
serde_json::to_value(LegacyDriverFenceEvidence::Docker {
165-
container_id,
166-
network_mode: "none".to_string(),
167-
unexpected_networks: Vec::new(),
168-
})
169-
.map_err(|error| {
170-
BackendError::Descriptor(format!(
171-
"encode {description} legacy driver fence: {error}"
172-
))
173-
})?,
174-
);
175-
}
176-
serde_json::to_vec(&value)
177-
.map_err(|error| BackendError::Descriptor(format!("encode {description}: {error}")))
178-
}
179-
180-
pub fn encode_boundary_config_compatible(
181-
config: &BoundaryConfig,
182-
format: FenceWireFormat,
183-
) -> Result<Vec<u8>, BackendError> {
184-
encode_fence_compatible(config, format, "Docker boundary config")
185-
}
186-
187-
pub fn encode_runtime_descriptor_compatible(
188-
descriptor: &SandboxRuntimeDescriptor,
189-
format: FenceWireFormat,
190-
) -> Result<Vec<u8>, BackendError> {
191-
encode_fence_compatible(descriptor, format, "Docker runtime descriptor")
192-
}
193-
19461
/// Driver-owned inputs that bind one Docker container to one boundary.
19562
pub struct DockerBoundarySpec {
19663
pub boundary_id: String,
@@ -276,12 +143,37 @@ impl DockerBoundarySpec {
276143
mod tests {
277144
use super::*;
278145

279-
fn provisioning() -> DockerBoundaryProvisioning {
146+
#[test]
147+
fn outer_fence_projection_rejects_each_missing_native_fact() {
148+
let unexpected_networks = vec!["bridge".to_string()];
149+
for evidence in [
150+
DockerOuterFenceEvidence {
151+
container_id: "",
152+
network_mode: "none",
153+
unexpected_networks: &[],
154+
},
155+
DockerOuterFenceEvidence {
156+
container_id: "container",
157+
network_mode: "bridge",
158+
unexpected_networks: &[],
159+
},
160+
DockerOuterFenceEvidence {
161+
container_id: "container",
162+
network_mode: "none",
163+
unexpected_networks: &unexpected_networks,
164+
},
165+
] {
166+
assert!(evidence.project("generation-1").is_err());
167+
}
168+
}
169+
170+
#[test]
171+
fn provisioning_binds_container_and_image_claims() {
280172
let session_id = openshell_core::SandboxSessionId::new();
281173
let tls =
282174
openshell_sandbox_backend::boundary_protocol::generate_sandbox_tls_material(session_id)
283175
.unwrap();
284-
DockerBoundarySpec {
176+
let provisioned = DockerBoundarySpec {
285177
boundary_id: "sandbox-1".to_string(),
286178
generation: "generation-1".to_string(),
287179
session_id,
@@ -317,36 +209,7 @@ mod tests {
317209
child_env: HashMap::new(),
318210
}
319211
.provision()
320-
.unwrap()
321-
}
322-
323-
#[test]
324-
fn outer_fence_projection_rejects_each_missing_native_fact() {
325-
let unexpected_networks = vec!["bridge".to_string()];
326-
for evidence in [
327-
DockerOuterFenceEvidence {
328-
container_id: "",
329-
network_mode: "none",
330-
unexpected_networks: &[],
331-
},
332-
DockerOuterFenceEvidence {
333-
container_id: "container",
334-
network_mode: "bridge",
335-
unexpected_networks: &[],
336-
},
337-
DockerOuterFenceEvidence {
338-
container_id: "container",
339-
network_mode: "none",
340-
unexpected_networks: &unexpected_networks,
341-
},
342-
] {
343-
assert!(evidence.project("generation-1").is_err());
344-
}
345-
}
346-
347-
#[test]
348-
fn provisioning_binds_container_and_image_claims() {
349-
let provisioned = provisioning();
212+
.unwrap();
350213

351214
assert_eq!(
352215
provisioned.boundary_config.resource_claims,
@@ -372,36 +235,4 @@ mod tests {
372235
.is_ok()
373236
);
374237
}
375-
376-
#[test]
377-
fn legacy_driver_fence_round_trips_through_current_projection() {
378-
let provisioned = provisioning();
379-
let boundary = encode_boundary_config_compatible(
380-
&provisioned.boundary_config,
381-
FenceWireFormat::LegacyDriverFence,
382-
)
383-
.unwrap();
384-
let descriptor = encode_runtime_descriptor_compatible(
385-
&provisioned.runtime_descriptor,
386-
FenceWireFormat::LegacyDriverFence,
387-
)
388-
.unwrap();
389-
for encoded in [&boundary, &descriptor] {
390-
let value: serde_json::Value = serde_json::from_slice(encoded).unwrap();
391-
assert!(value.get("outer_fence").is_none());
392-
assert_eq!(value["driver_fence"]["backend"], "docker");
393-
assert_eq!(value["driver_fence"]["container_id"], "sha256:container");
394-
}
395-
let (decoded_boundary, boundary_format) =
396-
decode_boundary_config_compatible(&boundary).unwrap();
397-
let (decoded_descriptor, descriptor_format) =
398-
decode_runtime_descriptor_compatible(&descriptor).unwrap();
399-
assert_eq!(boundary_format, FenceWireFormat::LegacyDriverFence);
400-
assert_eq!(descriptor_format, FenceWireFormat::LegacyDriverFence);
401-
assert_eq!(decoded_boundary.outer_fence, decoded_descriptor.outer_fence);
402-
assert_eq!(
403-
decoded_boundary.outer_fence,
404-
provisioned.boundary_config.outer_fence
405-
);
406-
}
407238
}

0 commit comments

Comments
 (0)