From 4a898cfa2cb118740f6e816e1cc326ef46388ca8 Mon Sep 17 00:00:00 2001 From: Alex Chi Z Date: Thu, 17 Apr 2025 15:23:55 -0400 Subject: [PATCH] feat(pageserver): add initial encrypt key fields Signed-off-by: Alex Chi Z --- pageserver/benches/upload_queue.rs | 1 + .../tenant/remote_timeline_client/index.rs | 258 ++++++++++++++++-- pageserver/src/tenant/upload_queue.rs | 2 + 3 files changed, 234 insertions(+), 27 deletions(-) diff --git a/pageserver/benches/upload_queue.rs b/pageserver/benches/upload_queue.rs index 8de06a6c25..41ef9a28fd 100644 --- a/pageserver/benches/upload_queue.rs +++ b/pageserver/benches/upload_queue.rs @@ -45,6 +45,7 @@ fn bench_upload_queue_next_ready(c: &mut Criterion) { shard: ShardIndex::new(ShardNumber(1), ShardCount(2)), generation: Generation::Valid(1), file_size: 0, + encryption_key: None, }; // Construct the (initial and uploaded) index with layer0. diff --git a/pageserver/src/tenant/remote_timeline_client/index.rs b/pageserver/src/tenant/remote_timeline_client/index.rs index a5cd8989aa..7f0fbb8bc4 100644 --- a/pageserver/src/tenant/remote_timeline_client/index.rs +++ b/pageserver/src/tenant/remote_timeline_client/index.rs @@ -10,6 +10,8 @@ use pageserver_api::models::AuxFilePolicy; use pageserver_api::models::RelSizeMigration; use pageserver_api::shard::ShardIndex; use serde::{Deserialize, Serialize}; +use serde_with::base64::Base64; +use serde_with::serde_as; use utils::id::TimelineId; use utils::lsn::Lsn; @@ -114,6 +116,29 @@ pub struct IndexPart { /// The timestamp when the timeline was marked invisible in synthetic size calculations. #[serde(skip_serializing_if = "Option::is_none", default)] pub(crate) marked_invisible_at: Option, + + /// The encryption key used to encrypt the timeline layer files. + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub(crate) keys: Vec, +} + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct KeyVersion(u32); + +/// An identifier for an encryption key. The scope of the key is the timeline (TBD). +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct EncryptionKeyId { + version: KeyVersion, + generation: Generation, +} + +#[serde_as] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct EncryptionKey { + #[serde_as(as = "Base64")] + pub key: Vec, + pub id: EncryptionKeyId, + pub created_at: NaiveDateTime, } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -142,10 +167,12 @@ impl IndexPart { /// - 12: +l2_lsn /// - 13: +gc_compaction /// - 14: +marked_invisible_at - const LATEST_VERSION: usize = 14; + /// - 15: +keys and encryption_key in layer_metadata + const LATEST_VERSION: usize = 15; // Versions we may see when reading from a bucket. - pub const KNOWN_VERSIONS: &'static [usize] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; + pub const KNOWN_VERSIONS: &'static [usize] = + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; pub const FILE_NAME: &'static str = "index_part.json"; @@ -165,6 +192,7 @@ impl IndexPart { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), } } @@ -222,7 +250,7 @@ impl IndexPart { /// /// Fields have to be `Option`s because remote [`IndexPart`]'s can be from different version, which /// might have less or more metadata depending if upgrading or rolling back an upgrade. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct LayerFileMetadata { pub file_size: u64, @@ -233,6 +261,9 @@ pub struct LayerFileMetadata { #[serde(default = "ShardIndex::unsharded")] #[serde(skip_serializing_if = "ShardIndex::is_unsharded")] pub shard: ShardIndex, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub encryption_key: Option, } impl LayerFileMetadata { @@ -241,6 +272,7 @@ impl LayerFileMetadata { file_size, generation, shard, + encryption_key: None, } } /// Helper to get both generation and file size in a tuple @@ -453,14 +485,16 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for // example. file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -475,6 +509,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -502,14 +537,16 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for // example. file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -524,6 +561,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -552,14 +590,16 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for // example. file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -574,6 +614,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -627,6 +668,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let empty_layers_parsed = IndexPart::from_json_bytes(empty_layers_json.as_bytes()).unwrap(); @@ -653,14 +695,16 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for // example. file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -675,6 +719,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -703,11 +748,13 @@ mod tests { file_size: 23289856, generation: Generation::new(1), shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000014EF499-00000000015A7619".parse().unwrap(), LayerFileMetadata { file_size: 1015808, generation: Generation::new(1), shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: Lsn::from_str("0/15A7618").unwrap(), @@ -726,6 +773,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -756,14 +804,16 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for // example. file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -782,6 +832,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -815,12 +866,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -843,6 +896,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -877,12 +931,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -905,6 +961,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -941,12 +998,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -972,6 +1031,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -1017,12 +1077,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1052,6 +1114,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -1098,12 +1161,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1133,6 +1198,7 @@ mod tests { l2_lsn: None, gc_compaction: None, marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -1183,12 +1249,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1220,6 +1288,7 @@ mod tests { last_completed_lsn: "0/16960E8".parse::().unwrap(), }), marked_invisible_at: None, + keys: Vec::new(), }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); @@ -1271,12 +1340,14 @@ mod tests { ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { file_size: 25600000, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), - shard: ShardIndex::unsharded() + shard: ShardIndex::unsharded(), + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1308,6 +1379,139 @@ mod tests { last_completed_lsn: "0/16960E8".parse::().unwrap(), }), marked_invisible_at: Some(parse_naive_datetime("2023-07-31T09:00:00.123000000")), + keys: Vec::new(), + }; + + let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); + assert_eq!(part, expected); + } + + #[test] + fn v15_keys_are_parsed() { + let example = r#"{ + "version": 15, + "layer_metadata":{ + "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9": { "file_size": 25600000, "encryption_key": { "version": 1, "generation": 5 } }, + "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51": { "file_size": 9007199254741001, "encryption_key": { "version": 2, "generation": 6 } } + }, + "disk_consistent_lsn":"0/16960E8", + "metadata": { + "disk_consistent_lsn": "0/16960E8", + "prev_record_lsn": "0/1696070", + "ancestor_timeline": "e45a7f37d3ee2ff17dc14bf4f4e3f52e", + "ancestor_lsn": "0/0", + "latest_gc_cutoff_lsn": "0/1696070", + "initdb_lsn": "0/1696070", + "pg_version": 14 + }, + "gc_blocking": { + "started_at": "2024-07-19T09:00:00.123", + "reasons": ["DetachAncestor"] + }, + "import_pgdata": { + "V1": { + "Done": { + "idempotency_key": "specified-by-client-218a5213-5044-4562-a28d-d024c5f057f5", + "started_at": "2024-11-13T09:23:42.123", + "finished_at": "2024-11-13T09:42:23.123" + } + } + }, + "rel_size_migration": "legacy", + "l2_lsn": "0/16960E8", + "gc_compaction": { + "last_completed_lsn": "0/16960E8" + }, + "marked_invisible_at": "2023-07-31T09:00:00.123", + "keys": [ + { + "key": "dGVzdF9rZXk=", + "id": { + "version": 1, + "generation": 5 + }, + "created_at": "2024-07-19T09:00:00.123" + }, + { + "key": "dGVzdF9rZXlfMg==", + "id": { + "version": 2, + "generation": 6 + }, + "created_at": "2024-07-19T10:00:00.123" + } + ] + }"#; + + let expected = IndexPart { + version: 15, + layer_metadata: HashMap::from([ + ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9".parse().unwrap(), LayerFileMetadata { + file_size: 25600000, + generation: Generation::none(), + shard: ShardIndex::unsharded(), + encryption_key: Some(EncryptionKeyId { + version: KeyVersion(1), + generation: Generation::Valid(5), + }), + }), + ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { + file_size: 9007199254741001, + generation: Generation::none(), + shard: ShardIndex::unsharded(), + encryption_key: Some(EncryptionKeyId { + version: KeyVersion(2), + generation: Generation::Valid(6), + }), + }) + ]), + disk_consistent_lsn: "0/16960E8".parse::().unwrap(), + metadata: TimelineMetadata::new( + Lsn::from_str("0/16960E8").unwrap(), + Some(Lsn::from_str("0/1696070").unwrap()), + Some(TimelineId::from_str("e45a7f37d3ee2ff17dc14bf4f4e3f52e").unwrap()), + Lsn::INVALID, + Lsn::from_str("0/1696070").unwrap(), + Lsn::from_str("0/1696070").unwrap(), + 14, + ).with_recalculated_checksum().unwrap(), + deleted_at: None, + lineage: Default::default(), + gc_blocking: Some(GcBlocking { + started_at: parse_naive_datetime("2024-07-19T09:00:00.123000000"), + reasons: enumset::EnumSet::from_iter([GcBlockingReason::DetachAncestor]), + }), + last_aux_file_policy: Default::default(), + archived_at: None, + import_pgdata: Some(import_pgdata::index_part_format::Root::V1(import_pgdata::index_part_format::V1::Done(import_pgdata::index_part_format::Done{ + started_at: parse_naive_datetime("2024-11-13T09:23:42.123000000"), + finished_at: parse_naive_datetime("2024-11-13T09:42:23.123000000"), + idempotency_key: import_pgdata::index_part_format::IdempotencyKey::new("specified-by-client-218a5213-5044-4562-a28d-d024c5f057f5".to_string()), + }))), + rel_size_migration: Some(RelSizeMigration::Legacy), + l2_lsn: Some("0/16960E8".parse::().unwrap()), + gc_compaction: Some(GcCompactionState { + last_completed_lsn: "0/16960E8".parse::().unwrap(), + }), + marked_invisible_at: Some(parse_naive_datetime("2023-07-31T09:00:00.123000000")), + keys: vec![ + EncryptionKey { + key: "test_key".as_bytes().to_vec(), + id: EncryptionKeyId { + version: KeyVersion(1), + generation: Generation::Valid(5), + }, + created_at: parse_naive_datetime("2024-07-19T09:00:00.123000000"), + }, + EncryptionKey { + key: "test_key_2".as_bytes().to_vec(), + id: EncryptionKeyId { + version: KeyVersion(2), + generation: Generation::Valid(6), + }, + created_at: parse_naive_datetime("2024-07-19T10:00:00.123000000"), + } + ], }; let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap(); diff --git a/pageserver/src/tenant/upload_queue.rs b/pageserver/src/tenant/upload_queue.rs index be1b55ffa3..5a38b04750 100644 --- a/pageserver/src/tenant/upload_queue.rs +++ b/pageserver/src/tenant/upload_queue.rs @@ -641,6 +641,7 @@ mod tests { generation: timeline.generation, shard: timeline.get_shard_index(), file_size: size as u64, + encryption_key: None, }; make_layer_with_metadata(timeline, name, metadata) } @@ -1378,6 +1379,7 @@ mod tests { shard, generation: Generation::Valid(generation), file_size: 0, + encryption_key: None, }; make_layer_with_metadata(&tli, name, metadata) };