diff --git a/Cargo.lock b/Cargo.lock index 01af3817..c6bfffcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -577,12 +577,11 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libcnb" version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc89bfeaef5f43cdee664798e3c0aa36e052a412ab1391f0750aee4df1f407" +source = "git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api#c900b88f8e3cfbeb65031f892201f10e772f416e" dependencies = [ - "libcnb-common", - "libcnb-data", - "libcnb-proc-macros", + "libcnb-common 0.21.0 (git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api)", + "libcnb-data 0.21.0 (git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api)", + "libcnb-proc-macros 0.21.0 (git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api)", "serde", "thiserror", "toml", @@ -599,6 +598,16 @@ dependencies = [ "toml", ] +[[package]] +name = "libcnb-common" +version = "0.21.0" +source = "git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api#c900b88f8e3cfbeb65031f892201f10e772f416e" +dependencies = [ + "serde", + "thiserror", + "toml", +] + [[package]] name = "libcnb-data" version = "0.21.0" @@ -606,7 +615,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfcd102bfb1bf98ee4c18da0b29be6f23a19681937924bf758e9ea8499668b18" dependencies = [ "fancy-regex", - "libcnb-proc-macros", + "libcnb-proc-macros 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "thiserror", + "toml", + "uriparse", +] + +[[package]] +name = "libcnb-data" +version = "0.21.0" +source = "git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api#c900b88f8e3cfbeb65031f892201f10e772f416e" +dependencies = [ + "fancy-regex", + "libcnb-proc-macros 0.21.0 (git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api)", "serde", "thiserror", "toml", @@ -622,8 +644,8 @@ dependencies = [ "cargo_metadata", "ignore", "indoc", - "libcnb-common", - "libcnb-data", + "libcnb-common 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libcnb-data 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph", "thiserror", "uriparse", @@ -642,6 +664,17 @@ dependencies = [ "syn", ] +[[package]] +name = "libcnb-proc-macros" +version = "0.21.0" +source = "git+https://github.com/heroku/libcnb.rs?branch=malax/layer-api#c900b88f8e3cfbeb65031f892201f10e772f416e" +dependencies = [ + "cargo_metadata", + "fancy-regex", + "quote", + "syn", +] + [[package]] name = "libcnb-test" version = "0.21.0" @@ -650,8 +683,8 @@ checksum = "9471152703833b74d565c7f7c910b4d5e084f955c327eba2bdb6658e86bd6dd6" dependencies = [ "fastrand", "fs_extra", - "libcnb-common", - "libcnb-data", + "libcnb-common 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libcnb-data 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "libcnb-package", "tempfile", "thiserror", diff --git a/buildpacks/ruby/Cargo.toml b/buildpacks/ruby/Cargo.toml index f3aff1dd..f8822140 100644 --- a/buildpacks/ruby/Cargo.toml +++ b/buildpacks/ruby/Cargo.toml @@ -16,7 +16,7 @@ glob = "0.3" indoc = "2" # libcnb has a much bigger impact on buildpack behaviour than any other dependencies, # so it's pinned to an exact version to isolate it from lockfile refreshes. -libcnb = "=0.21.0" +libcnb = { git = "https://github.com/heroku/libcnb.rs", branch = "malax/layer-api" } libherokubuildpack = { version = "=0.21.0", default-features = false, features = ["digest"] } rand = "0.8" # TODO: Consolidate on either the regex crate or the fancy-regex crate, since this repo currently uses both. diff --git a/buildpacks/ruby/src/layers/metrics_agent_install.rs b/buildpacks/ruby/src/layers/metrics_agent_install.rs index 6e1f74bb..16676608 100644 --- a/buildpacks/ruby/src/layers/metrics_agent_install.rs +++ b/buildpacks/ruby/src/layers/metrics_agent_install.rs @@ -25,7 +25,7 @@ use tempfile::NamedTempFile; /// ```shell /// $ curl https://agentmon-releases.s3.us-east-1.amazonaws.com/latest /// ``` -const DOWNLOAD_URL: &str = +pub(crate) const DOWNLOAD_URL: &str = "https://agentmon-releases.s3.us-east-1.amazonaws.com/agentmon-0.3.1-linux-amd64.tar.gz"; const DOWNLOAD_SHA: &str = "f9bf9f33c949e15ffed77046ca38f8dae9307b6a0181c6af29a25dec46eb2dac"; @@ -36,7 +36,7 @@ pub(crate) struct MetricsAgentInstall<'a> { #[derive(Deserialize, Serialize, Debug, Clone)] pub(crate) struct Metadata { - download_url: Option, + pub(crate) download_url: Option, } #[derive(thiserror::Error, Debug)] @@ -64,100 +64,7 @@ pub(crate) enum MetricsAgentInstallError { ChecksumFailed(String), } -impl<'a> Layer for MetricsAgentInstall<'a> { - type Buildpack = RubyBuildpack; - type Metadata = Metadata; - - fn types(&self) -> libcnb::data::layer_content_metadata::LayerTypes { - LayerTypes { - build: true, - launch: true, - cache: true, - } - } - - fn create( - &mut self, - _context: &libcnb::build::BuildContext, - layer_path: &std::path::Path, - ) -> Result< - libcnb::layer::LayerResult, - ::Error, - > { - let bin_dir = layer_path.join("bin"); - - let agentmon = log_step_timed("Downloading", || { - install_agentmon(&bin_dir).map_err(RubyBuildpackError::MetricsAgentError) - })?; - - log_step("Writing scripts"); - let execd = write_execd_script(&agentmon, layer_path) - .map_err(RubyBuildpackError::MetricsAgentError)?; - - LayerResultBuilder::new(Metadata { - download_url: Some(DOWNLOAD_URL.to_string()), - }) - .exec_d_program("spawn_metrics_agent", execd) - .build() - } - - fn update( - &mut self, - _context: &libcnb::build::BuildContext, - layer_data: &libcnb::layer::LayerData, - ) -> Result< - libcnb::layer::LayerResult, - ::Error, - > { - let layer_path = &layer_data.path; - - log_step("Writing scripts"); - let execd = write_execd_script(&layer_path.join("bin").join("agentmon"), layer_path) - .map_err(RubyBuildpackError::MetricsAgentError)?; - - LayerResultBuilder::new(Metadata { - download_url: Some(DOWNLOAD_URL.to_string()), - }) - .exec_d_program("spawn_metrics_agent", execd) - .build() - } - - fn existing_layer_strategy( - &mut self, - _context: &libcnb::build::BuildContext, - layer_data: &libcnb::layer::LayerData, - ) -> Result::Error> - { - match &layer_data.content_metadata.metadata.download_url { - Some(url) if url == DOWNLOAD_URL => { - log_step("Using cached metrics agent"); - Ok(ExistingLayerStrategy::Update) - } - Some(url) => { - log_step(format!( - "Using cached metrics agent ({url} to {DOWNLOAD_URL}" - )); - Ok(ExistingLayerStrategy::Recreate) - } - None => Ok(ExistingLayerStrategy::Recreate), - } - } - - fn migrate_incompatible_metadata( - &mut self, - _context: &libcnb::build::BuildContext, - _metadata: &GenericMetadata, - ) -> Result< - libcnb::layer::MetadataMigration, - ::Error, - > { - log_step("Clearing cache (invalid metadata)"); - - Ok(libcnb::layer::MetadataMigration::RecreateLayer) - } -} - -fn write_execd_script( +pub(crate) fn write_execd_script( agentmon: &Path, layer_path: &Path, ) -> Result { @@ -200,7 +107,7 @@ fn write_execd_script( Ok(execd) } -fn install_agentmon(dir: &Path) -> Result { +pub(crate) fn install_agentmon(dir: &Path) -> Result { let agentmon = download_untar(DOWNLOAD_URL, dir).map(|()| dir.join("agentmon"))?; chmod_plus_x(&agentmon).map_err(MetricsAgentInstallError::PermissionError)?; diff --git a/buildpacks/ruby/src/main.rs b/buildpacks/ruby/src/main.rs index fb6b6973..0a4498c1 100644 --- a/buildpacks/ruby/src/main.rs +++ b/buildpacks/ruby/src/main.rs @@ -19,7 +19,8 @@ use libcnb::data::launch::LaunchBuilder; use libcnb::data::layer_name; use libcnb::detect::{DetectContext, DetectResult, DetectResultBuilder}; use libcnb::generic::{GenericMetadata, GenericPlatform}; -use libcnb::layer_env::Scope; +use libcnb::layer::{CachedLayerDefinition, InspectRestoredAction, InvalidMetadataAction}; +use libcnb::layer_env::{LayerEnv, Scope}; use libcnb::Platform; use libcnb::{buildpack_main, Buildpack}; use std::io::stdout; @@ -132,18 +133,74 @@ impl Buildpack for RubyBuildpack { // ## Install metrics agent (logger, env) = { - let section = logger.section("Metrics agent"); + let mut section = logger.section("Metrics agent"); if lockfile_contents.contains("barnes") { - let layer_data = context.handle_layer( + let metrics_layer = context.cached_layer( layer_name!("metrics_agent"), - MetricsAgentInstall { - _in_section: section.as_ref(), + CachedLayerDefinition { + build: true, + launch: true, + invalid_metadata: &|_| { + // TODO, cannot log here + // section = section.step("Clearing invalid metadata"); + InvalidMetadataAction::DeleteLayer + }, + inspect_restored: &|old: &layers::metrics_agent_install::Metadata, _| { + match &old.download_url.as_ref() { + &Some(old_url) => { + if old_url == layers::metrics_agent_install::DOWNLOAD_URL { + InspectRestoredAction::KeepLayer + } else { + // TODO, cannot log here + // section = section.step(&format!( + // "Download URL changed from {old} to {now}", + // old = fmt::value(&old_url), + // now = fmt::value( + // layers::metrics_agent_install::DOWNLOAD_URL + // ) + // )); + InspectRestoredAction::DeleteLayer + } + } + None => InspectRestoredAction::DeleteLayer, + } + }, }, )?; + match metrics_layer.state { + libcnb::layer::LayerState::Restored { .. } => { + section = section.step("Using cached metrics agent"); + } + libcnb::layer::LayerState::Empty { .. } => { + let timer = section.step_timed("Downloading metrics agent"); + layers::metrics_agent_install::install_agentmon( + &metrics_layer.path().join("bin"), + ) + .map_err(RubyBuildpackError::MetricsAgentError)?; + section = timer.finish_timed_step(); + } + }; + + let agentmon_path = metrics_layer.path().join("bin").join("agentmon"); + section = section.step("Writing scripts"); + let execd = layers::metrics_agent_install::write_execd_script( + &agentmon_path, + &metrics_layer.path(), + ) + .map_err(RubyBuildpackError::MetricsAgentError)?; + + metrics_layer.replace_metadata(layers::metrics_agent_install::Metadata { + download_url: Some(layers::metrics_agent_install::DOWNLOAD_URL.to_string()), + })?; + + metrics_layer.replace_exec_d_programs(vec![("spawn_metrics_agent", execd)])?; + ( section.end_section(), - layer_data.env.apply(Scope::Build, &env), + LayerEnv::read_from_layer_dir(metrics_layer.path()) + .expect("read env from a layer") + .apply(Scope::Build, &env), ) } else { ( diff --git a/commons/Cargo.toml b/commons/Cargo.toml index 3e28a5f6..d3ae4cf5 100644 --- a/commons/Cargo.toml +++ b/commons/Cargo.toml @@ -24,7 +24,8 @@ indoc = "2" lazy_static = "1" # libcnb has a much bigger impact on buildpack behaviour than any other dependencies, # so it's pinned to an exact version to isolate it from lockfile refreshes. -libcnb = "=0.21.0" + +libcnb = { git = "https://github.com/heroku/libcnb.rs", branch = "malax/layer-api" } libherokubuildpack = { version = "=0.21.0", default-features = false, features = ["command"] } regex = "1" serde = "1"