Skip to content

Commit 132719d

Browse files
committed
feat(boil): Support layer caching
1 parent b7b616f commit 132719d

3 files changed

Lines changed: 137 additions & 11 deletions

File tree

rust/boil/src/cli/build.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ pub struct BuildArguments {
118118
#[deprecated(since = "0.1.7", note = "Use -- --load instead")]
119119
pub load: bool,
120120

121+
#[arg(long, help_heading = "Build Options", group = "cache")]
122+
pub cache_registry: Option<HostPort>,
123+
124+
#[arg(long, help_heading = "Build Options", requires = "cache")]
125+
pub cache_namespace: Option<String>,
126+
121127
/// Dry run. This does not build the image(s) but instead prints out the bakefile.
122128
#[arg(short, long, alias = "dry")]
123129
pub dry_run: bool,

rust/boil/src/core/bakefile.rs

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -340,26 +340,46 @@ impl Bakefile {
340340

341341
for (image_name, (image_config, is_entry)) in targets.into_iter() {
342342
for (image_version, image_options) in image_config.versions {
343-
let image_repository_uri = utils::format_image_repository_uri(
343+
let (
344+
image_repository_uri,
345+
image_index_manifest_tag,
346+
image_manifest_tag,
347+
image_manifest_uri,
348+
) = utils::format_image_tag_parts(
344349
image_registry,
345350
&cli_args.registry_namespace,
346351
&image_name,
347-
);
348-
349-
let image_index_manifest_tag = utils::format_image_index_manifest_tag(
350-
&image_version,
352+
&cli_args.image_version,
351353
&metadata.vendor_tag_prefix,
352354
&cli_args.image_version,
353-
);
354-
355-
let image_manifest_tag = utils::format_image_manifest_tag(
356-
&image_index_manifest_tag,
357355
cli_args.target_platform.architecture(),
358356
cli_args.strip_architecture,
359357
);
360358

361-
let image_manifest_uri =
362-
utils::format_image_manifest_uri(&image_repository_uri, &image_manifest_tag);
359+
let cache_image_manifest_uri =
360+
cli_args.cache_registry.as_ref().map(|cache_registry| {
361+
let uri = utils::format_image_cache_repository_uri(
362+
cache_registry,
363+
cli_args.cache_namespace.as_deref(),
364+
&cli_args.registry_namespace,
365+
&image_name,
366+
);
367+
368+
let cache_image_index_manifest_tag =
369+
utils::format_image_cache_index_manifest_tag(
370+
&image_version,
371+
&metadata.vendor_tag_prefix,
372+
&cli_args.image_version,
373+
);
374+
375+
let cache_image_manifest_tag = utils::format_image_manifest_tag(
376+
&cache_image_index_manifest_tag,
377+
cli_args.target_platform.architecture(),
378+
cli_args.strip_architecture,
379+
);
380+
381+
utils::format_image_manifest_uri(&uri, &cache_image_manifest_tag)
382+
});
363383

364384
// TODO (@Techassi): Clean this up
365385
// TODO (@Techassi): Move the arg formatting into functions
@@ -449,6 +469,23 @@ impl Bakefile {
449469
&cli_args.image_version,
450470
);
451471

472+
let cache_to =
473+
cache_image_manifest_uri
474+
.clone()
475+
.map_or_else(Vec::new, |reference| {
476+
vec![CacheStorageBackend::Registry {
477+
reference,
478+
mode: Some(CacheMode::Max),
479+
}]
480+
});
481+
482+
let cache_from = cache_image_manifest_uri.map_or_else(Vec::new, |reference| {
483+
vec![CacheStorageBackend::Registry {
484+
reference,
485+
mode: None,
486+
}]
487+
});
488+
452489
let target = BakefileTarget {
453490
tags: vec![image_manifest_uri],
454491
arguments: build_arguments,
@@ -459,6 +496,8 @@ impl Bakefile {
459496
inherits: vec![COMMON_TARGET_NAME.to_owned()],
460497
annotations,
461498
contexts,
499+
cache_from,
500+
cache_to,
462501
..Default::default()
463502
};
464503

@@ -577,6 +616,12 @@ pub struct BakefileTarget {
577616
/// Image names and tags to use for the build target.
578617
#[serde(skip_serializing_if = "Vec::is_empty")]
579618
pub tags: Vec<String>,
619+
620+
#[serde(skip_serializing_if = "Vec::is_empty")]
621+
pub cache_from: Vec<CacheStorageBackend>,
622+
623+
#[serde(skip_serializing_if = "Vec::is_empty")]
624+
pub cache_to: Vec<CacheStorageBackend>,
580625
}
581626

582627
impl BakefileTarget {
@@ -668,3 +713,23 @@ impl BakefileTarget {
668713
pub struct BakefileGroup {
669714
targets: Vec<String>,
670715
}
716+
717+
#[derive(Clone, Debug, Serialize)]
718+
#[serde(tag = "type", rename_all = "lowercase")]
719+
pub enum CacheStorageBackend {
720+
Registry {
721+
/// Full name of the cache image to import.
722+
#[serde(rename = "ref")]
723+
reference: String,
724+
725+
#[serde(skip_serializing_if = "Option::is_none")]
726+
mode: Option<CacheMode>,
727+
},
728+
}
729+
730+
#[derive(Clone, Copy, Debug, Serialize)]
731+
#[serde(rename_all = "lowercase")]
732+
pub enum CacheMode {
733+
// We currently only support max, because we want to cache every layer
734+
Max,
735+
}

rust/boil/src/utils.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@ pub fn format_image_repository_uri(
1111
format!("{image_registry}/{registry_namespace}/{image_name}")
1212
}
1313

14+
pub fn format_image_cache_repository_uri(
15+
image_cache_registry: &HostPort,
16+
cache_registry_namespace: Option<&str>,
17+
registry_namespace: &str,
18+
image_name: &str,
19+
) -> String {
20+
// We don't use .map here because we are unable to borrow the formatted string long enough
21+
if let Some(cache_registry_namespace) = cache_registry_namespace {
22+
format_image_repository_uri(image_cache_registry, cache_registry_namespace, image_name)
23+
} else {
24+
format_image_repository_uri(
25+
image_cache_registry,
26+
&format!("{}-cache", registry_namespace),
27+
image_name,
28+
)
29+
}
30+
}
31+
1432
/// Formats and returns the image manifest URI, eg. `oci.stackable.tech/sdp/opa:1.4.2-stackable25.7.0-amd64`.
1533
pub fn format_image_manifest_uri(image_repository_uri: &str, image_manifest_tag: &str) -> String {
1634
format!("{image_repository_uri}:{image_manifest_tag}")
@@ -25,6 +43,16 @@ pub fn format_image_index_manifest_tag(
2543
format!("{image_version}-{vendor_tag_prefix}{vendor_image_version}")
2644
}
2745

46+
pub fn format_image_cache_index_manifest_tag(
47+
image_version: &str,
48+
vendor_tag_prefix: &str,
49+
vendor_image_version: &str,
50+
) -> String {
51+
let tag =
52+
format_image_index_manifest_tag(image_version, vendor_tag_prefix, vendor_image_version);
53+
format!("{tag}-cache")
54+
}
55+
2856
/// Formats and returns the image manifest tag, eg. `1.4.2-stackable25.7.0-amd64`.
2957
///
3058
/// The `strip_architecture` parameter controls if the architecture is included in the tag.
@@ -41,6 +69,33 @@ pub fn format_image_manifest_tag(
4169
}
4270
}
4371

72+
#[allow(clippy::too_many_arguments)]
73+
pub fn format_image_tag_parts(
74+
image_registry: &HostPort,
75+
registry_namespace: &str,
76+
image_name: &str,
77+
image_version: &str,
78+
vendor_tag_prefix: &str,
79+
vendor_image_version: &str,
80+
architecture: &Architecture,
81+
strip_architecture: bool,
82+
) -> (String, String, String, String) {
83+
let image_repository_uri =
84+
format_image_repository_uri(image_registry, registry_namespace, image_name);
85+
let image_index_manifest_tag =
86+
format_image_index_manifest_tag(image_version, vendor_tag_prefix, vendor_image_version);
87+
let image_manifest_tag =
88+
format_image_manifest_tag(&image_index_manifest_tag, architecture, strip_architecture);
89+
let image_manifest_uri = format_image_manifest_uri(&image_repository_uri, &image_manifest_tag);
90+
91+
(
92+
image_repository_uri,
93+
image_index_manifest_tag,
94+
image_manifest_tag,
95+
image_manifest_uri,
96+
)
97+
}
98+
4499
/// Formats and returns the registry-specific env var name, eg. `BOIL_REGISTRY_TOKEN_OCI_STACKABLE_TECH`.
45100
pub fn format_registry_token_env_var_name(registry_uri: &str) -> String {
46101
format!(

0 commit comments

Comments
 (0)