Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boil.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ DELETE_CACHES = "true"
documentation = "https://docs.stackable.tech/home/stable/"
source = "https://github.com/stackabletech/docker-images/"
authors = "Stackable GmbH <info@stackable.tech>"
vendor-tag-prefix = "stackable"
vendor = "Stackable GmbH"
licenses = "Apache-2.0"

Expand Down
165 changes: 112 additions & 53 deletions rust/boil/src/build/bakefile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use crate::{
image::{Image, ImageConfig, ImageConfigError, ImageOptions, VersionOptionsPair},
platform::TargetPlatform,
},
config::{self, Config},
utils::{format_image_manifest_uri, format_image_repository_uri},
config::{self, Config, Metadata},
utils,
};

pub const COMMON_TARGET_NAME: &str = "common--target";
Expand Down Expand Up @@ -272,23 +272,28 @@ impl Bakefile {
}

/// Creates the common target, containing shared data, which will be inherited by other targets.
fn common_target(args: &cli::BuildArguments, config: Config) -> Result<BakefileTarget, Error> {
fn common_target(
args: &cli::BuildArguments,
Comment thread
NickLarsenNZ marked this conversation as resolved.
Outdated
build_arguments: BuildArguments,
metadata: &Metadata,
) -> Result<BakefileTarget, Error> {
let revision = Self::git_head_revision().context(GetRevisionSnafu)?;
let date_time = Self::now()?;

// Load build arguments from a file if the user requested it
let mut build_arguments = args.build_arguments.clone();
let mut user_build_arguments = args.build_arguments.clone();
if let Some(path) = &args.build_arguments_file {
let build_arguments_from_file =
BuildArguments::from_file(path).context(ParseBuildArgumentsSnafu)?;
build_arguments.extend(build_arguments_from_file);
user_build_arguments.extend(build_arguments_from_file);
}

let target = BakefileTarget::common(
date_time,
revision,
config,
build_arguments,
metadata,
user_build_arguments,
args.image_version.base_prerelease(),
);

Expand All @@ -303,12 +308,46 @@ impl Bakefile {
let mut bakefile_targets = BTreeMap::new();
let mut groups: BTreeMap<String, BakefileGroup> = BTreeMap::new();

// Destructure config so that we can move and borrow fields separately.
let Config {
build_arguments,
metadata,
} = config;

// Create a common target, which contains shared data, like annotations, arguments, labels, etc...
let common_target = Self::common_target(args, config)?;
let common_target = Self::common_target(args, build_arguments, &metadata)?;
bakefile_targets.insert(COMMON_TARGET_NAME.to_owned(), common_target);

// The image registry, eg. `oci.stackable.tech` or `localhost`
let image_registry = if args.use_localhost_registry {
&HostPort::localhost()
} else {
&args.registry
};

for (image_name, image_versions) in targets.into_iter() {
for (image_version, (image_options, is_entry)) in image_versions {
let image_repository_uri = utils::format_image_repository_uri(
image_registry,
&args.registry_namespace,
&image_name,
);

let image_index_manifest_tag = utils::format_image_index_manifest_tag(
&image_version,
&metadata.vendor_tag_prefix,
&args.image_version,
);

let image_manifest_tag = utils::format_image_manifest_tag(
&image_index_manifest_tag,
args.target_platform.architecture(),
args.strip_architecture,
);

let image_manifest_uri =
utils::format_image_manifest_uri(&image_repository_uri, &image_manifest_tag);

// TODO (@Techassi): Clean this up
// TODO (@Techassi): Move the arg formatting into functions
let mut build_arguments = BuildArguments::new();
Expand All @@ -317,11 +356,8 @@ impl Bakefile {
.local_images
.iter()
.map(|(image_name, image_version)| {
BuildArgument::new(
format!(
"{image_name}_VERSION",
image_name = image_name.to_uppercase().replace('-', "_")
),
BuildArgument::local_image_version(
image_name.to_string(),
image_version.to_string(),
)
})
Expand All @@ -334,27 +370,22 @@ impl Bakefile {
"PRODUCT_VERSION".to_owned(),
image_version.to_string(),
));

// The image registry, eg. `oci.stackable.tech` or `localhost`
let image_registry = if args.use_localhost_registry {
&HostPort::localhost()
} else {
&args.registry
};

let image_repository_uri = format_image_repository_uri(
image_registry,
&args.registry_namespace,
&image_name,
);

let image_manifest_uri = format_image_manifest_uri(
&image_repository_uri,
&image_version,
&args.image_version,
args.target_platform.architecture(),
args.strip_architecture,
);
build_arguments.insert(BuildArgument::new(
"IMAGE_REPOSITORY_URI".to_owned(),
image_repository_uri,
));
build_arguments.insert(BuildArgument::new(
"IMAGE_INDEX_MANIFEST_TAG".to_owned(),
image_index_manifest_tag,
));
build_arguments.insert(BuildArgument::new(
"IMAGE_MANIFEST_TAG".to_owned(),
image_manifest_tag,
));
build_arguments.insert(BuildArgument::new(
"IMAGE_MANIFEST_URI".to_owned(),
image_manifest_uri.clone(),
));

// By using a cap-std Dir, we can ensure that the paths provided must be relative to
// the appropriate image folder and wont escape it by providing absolute or relative
Expand Down Expand Up @@ -399,8 +430,11 @@ impl Bakefile {
})
.collect();

let annotations =
BakefileTarget::image_version_annotation(&image_version, &args.image_version);
let annotations = BakefileTarget::image_version_annotation(
&image_version,
&metadata.vendor_tag_prefix,
&args.image_version,
);

let target = BakefileTarget {
tags: vec![image_manifest_uri],
Expand Down Expand Up @@ -533,31 +567,50 @@ impl BakefileTarget {
fn common(
date_time: String,
revision: String,
config: Config,
build_arguments: Vec<BuildArgument>,
build_arguments: BuildArguments,
metadata: &Metadata,
user_build_arguments: Vec<BuildArgument>,
release_version: String,
) -> Self {
let config::Metadata {
documentation,
documentation: docs,
licenses,
authors,
source,
vendor,
} = config.metadata;
..
} = metadata;

// Annotations describe OCI image components.
let annotations = vec![
// Add annotations which are always present.
let mut annotations = vec![
format!("{ANNOTATION_CREATED}={date_time}"),
format!("{ANNOTATION_AUTHORS}={authors}"),
format!("{ANNOTATION_DOCUMENTATION}={documentation}"),
format!("{ANNOTATION_SOURCE}={source}"),
format!("{ANNOTATION_REVISION}={revision}"),
format!("{ANNOTATION_VENDOR}={vendor}"),
format!("{ANNOTATION_LICENSES}={licenses}"),
];

let mut arguments = config.build_arguments;
arguments.extend(build_arguments);
// Add optional annotations.
if let Some(authors) = authors {
annotations.push(format!("{ANNOTATION_AUTHORS}={authors}"));
}

if let Some(docs) = docs {
annotations.push(format!("{ANNOTATION_DOCUMENTATION}={docs}"));
}

if let Some(source) = source {
annotations.push(format!("{ANNOTATION_SOURCE}={source}"));
}

if let Some(licenses) = licenses {
annotations.push(format!("{ANNOTATION_LICENSES}={licenses}"));
}

if let Some(vendor) = vendor {
annotations.push(format!("{ANNOTATION_VENDOR}={vendor}"));
}

let mut arguments = build_arguments;
arguments.extend(user_build_arguments);
arguments.insert(BuildArgument::new(
"RELEASE_VERSION".to_owned(),
release_version,
Expand All @@ -580,12 +633,18 @@ impl BakefileTarget {
}
}

fn image_version_annotation(image_version: &str, sdp_image_version: &Version) -> Vec<String> {
vec![
// TODO (@Techassi): Move this version formatting into a function
// TODO (@Techassi): Make this vendor agnostic, don't hard-code stackable here
format!("{ANNOTATION_VERSION}={image_version}-stackable{sdp_image_version}"),
]
fn image_version_annotation(
image_version: &str,
vendor_tag_prefix: &str,
vendor_image_version: &Version,
) -> Vec<String> {
let image_index_manifest_tag = utils::format_image_index_manifest_tag(
image_version,
vendor_tag_prefix,
vendor_image_version,
);

vec![format!("{ANNOTATION_VERSION}={image_index_manifest_tag}")]
}
}

Expand Down
4 changes: 1 addition & 3 deletions rust/boil/src/build/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ pub struct BuildArguments {
#[arg(help_heading = "Image Options", required = true)]
pub images: Vec<Image>,

// The action currently does the wrong thing here. It includes the
// architecture even though it should come from the --target-platform arg.
// The release arg is NOT needed, because this version IS the release version.
// NOTE (@Techassi): Should this maybe be renamed to vendor_version?
/// The image version being built.
#[arg(
short, long,
Expand Down
4 changes: 4 additions & 0 deletions rust/boil/src/build/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ impl BuildArgument {
Self((key, value))
}

pub fn local_image_version(image_name: String, image_version: String) -> Self {
Self::new(format!("{image_name}_VERSION"), image_version)
}

fn format_key(key: impl AsRef<str>) -> String {
key.as_ref().replace(['-', '/'], "_").to_uppercase()
}
Expand Down
28 changes: 23 additions & 5 deletions rust/boil/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,28 @@ impl Config {
// NOTE (@Techassi): Think about if these metadata fields should be required or optional. If they
// are optional, the appropriate annotations are only emitted if set.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Metadata {
pub documentation: Url,
pub licenses: String,
pub authors: String,
pub vendor: String,
pub source: Url,
/// The URL to the documentation page.
pub documentation: Option<Url>,

/// One ore more licenses used for images using the SPDX format.
pub licenses: Option<String>,

/// One or more authors of images.
///
/// It is recommended to use the "NAME <EMAIL>" format.
pub authors: Option<String>,

/// The vendor who builds the images.
pub vendor: Option<String>,

/// The vendor prefix used in the image (index) manifest tag.
///
/// Defaults to an empty string.
#[serde(default)]
pub vendor_tag_prefix: String,

/// The version control source of the images.
pub source: Option<Url>,
}
27 changes: 20 additions & 7 deletions rust/boil/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,32 @@ pub fn format_image_repository_uri(
}

/// Formats and returns the image manifest URI, eg. `oci.stackable.tech/sdp/opa:1.4.2-stackable25.7.0-amd64`.
pub fn format_image_manifest_uri(
image_repository_uri: &str,
pub fn format_image_manifest_uri(image_repository_uri: &str, image_manifest_tag: &str) -> String {
format!("{image_repository_uri}:{image_manifest_tag}")
}

/// Formats and returns the image index manifest tag, eg. `1.4.2-stackable25.7.0`.
pub fn format_image_index_manifest_tag(
image_version: &str,
sdp_image_version: &Version,
vendor_tag_prefix: &str,
vendor_image_version: &Version,
) -> String {
format!("{image_version}-{vendor_tag_prefix}{vendor_image_version}")
}

/// Formats and returns the image manifest tag, eg. `1.4.2-stackable25.7.0-amd64`.
///
/// The `strip_architecture` parameter controls if the architecture is included in the tag.
pub fn format_image_manifest_tag(
image_index_manifest_tag: &str,
// TODO (@Techassi): Maybe turn this into an Option to get rid of the bool
architecture: &Architecture,
strip_architecture: bool,
) -> String {
if strip_architecture {
format!("{image_repository_uri}:{image_version}-stackable{sdp_image_version}")
image_index_manifest_tag.to_owned()
} else {
format!(
"{image_repository_uri}:{image_version}-stackable{sdp_image_version}-{architecture}"
)
format!("{image_index_manifest_tag}-{architecture}")
}
}

Expand Down