Skip to content

Uploading a TUF repo fails while unpacking the composite control plane tarball #8675

@jgallagher

Description

@jgallagher

I tried uploading a TUF repo on dublin, and it failed with this error:

oxide system update repo upload --path tuf-mupdate.zip
Uploading tuf-mupdate.zip...
[00:01:00] [██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████] 2.58 GiB/2.58 GiB (43.37 MiB/s, 0s
error
Error Response: status: 400 Bad Request; headers: {"content-type": "application/json", "x-request-id": "8fa40fc4-8712-46d1-9444-b7f9667fedec", "content-length": "171", "date": "Wed, 23 Jul 2025 19:55:11 GMT"}; value: Error { error_code: None, message: "error extracting tarball for control_plane from repository\nCaused by:\n  -> invalid gzip header", request_id: "8fa40fc4-8712-46d1-9444-b7f9667fedec" }

We're pretty sure this was introduced by #7906. The control plane tarball inside the TUF repo now contains all the zones it did before, but also a couple of measurement CBOR files:

./oxide.json
./measurements
./measurements/e0ffbd6e86b3ba9891ee21d7d1e2a98667ef598070eef941d8088bc724af526b.cbor
./measurements/c1e0ef5f799cb4df2cb41e91c4dd82978e5b4abe5a18fe83715b8f3df05f8958.cbor
./zones
./zones/crucible_pantry.tar.gz
./zones/internal_dns.tar.gz
./zones/clickhouse_keeper.tar.gz
./zones/crucible.tar.gz
./zones/nexus.tar.gz
./zones/clickhouse.tar.gz
./zones/probe.tar.gz
./zones/cockroachdb.tar.gz
./zones/external_dns.tar.gz
./zones/oximeter.tar.gz
./zones/ntp.tar.gz
./zones/clickhouse_server.tar.gz

As we're unpacking this composite artifact, we attempt to extract the artifact versions from each zone or manifest file. This assumes those files are themselves gzipped tar files containing an oxide.json:

ControlPlaneZoneImages::extract_into(reader, |_, kind, reader| {
let known_kind = match kind {
ControlPlaneEntry::Zone => KnownArtifactKind::Zone,
ControlPlaneEntry::MeasurementCorpus => {
KnownArtifactKind::MeasurementCorpus
}
};
let mut out = self.extracted_artifacts.new_tempfile()?;
io::copy(reader, &mut out)?;
let data = self
.extracted_artifacts
.store_tempfile(known_kind.into(), out)?;
// Read the zone name and version from the `oxide.json` at the root
// of the zone.
let data_clone = data.clone();
let file = Handle::current().block_on(async move {
std::io::Result::Ok(data_clone.file().await?.into_std().await)
})?;
let mut tar = tar::Archive::new(flate2::read::GzDecoder::new(file));
let metadata =
tufaceous_brand_metadata::Metadata::read_from_tar(&mut tar)?;
let info = metadata.layer_info()?;
let artifact_id = ArtifactId {
name: info.pkg.clone(),
version: ArtifactVersion::new(info.version.to_string())?,
kind: known_kind.into(),
};
self.record_extracted_artifact(
artifact_id,
data,
known_kind.into(),
self.log,
)?;
Ok(())

These CBOR files are not gzipped tar files, so we get the error above.

For now we're going to revert #7906 to unblock update testing; I'll put some more notes in a comment below.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions