-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate
go_build
layer to struct API (#340)
* Drop layer_version functionality from go_build * Update layers/build.rs to struct API * Consume new layers/build.rs struct API * Re-add missing 'existing' from log message * handle_build_layer now returns LayerEnv
- Loading branch information
1 parent
0691480
commit becc7e8
Showing
2 changed files
with
112 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,122 +1,146 @@ | ||
use crate::{GoBuildpack, GoBuildpackError}; | ||
use heroku_go_utils::vrs::GoVersion; | ||
use libcnb::build::BuildContext; | ||
use libcnb::data::layer_content_metadata::LayerTypes; | ||
use libcnb::layer::{ExistingLayerStrategy, Layer, LayerData, LayerResult, LayerResultBuilder}; | ||
use libcnb::data::layer_name; | ||
use libcnb::layer::{ | ||
CachedLayerDefinition, EmptyLayerCause, InvalidMetadataAction, LayerState, RestoredLayerAction, | ||
}; | ||
use libcnb::layer_env::{LayerEnv, Scope}; | ||
use libcnb::{Buildpack, Target}; | ||
use libcnb::Target; | ||
use libherokubuildpack::log::log_info; | ||
use serde::{Deserialize, Serialize}; | ||
use std::fs; | ||
use std::path::Path; | ||
|
||
/// A layer for go incremental build cache artifacts | ||
pub(crate) struct BuildLayer { | ||
pub(crate) go_version: GoVersion, | ||
} | ||
const CACHE_ENV: &str = "GOCACHE"; | ||
const CACHE_DIR: &str = "cache"; | ||
const MAX_CACHE_USAGE_COUNT: f32 = 200.0; | ||
|
||
#[derive(Deserialize, Serialize, Clone, PartialEq)] | ||
pub(crate) struct BuildLayerMetadata { | ||
layer_version: String, | ||
go_major_version: GoVersion, | ||
target_arch: String, | ||
target_distro_name: String, | ||
target_distro_version: String, | ||
cache_usage_count: f32, | ||
} | ||
|
||
impl BuildLayerMetadata { | ||
fn new(version: &GoVersion, target: &Target) -> Self { | ||
Self { | ||
go_major_version: version.major_release_version(), | ||
target_arch: target.arch.to_string(), | ||
target_distro_name: target.distro_name.to_string(), | ||
target_distro_version: target.distro_version.to_string(), | ||
cache_usage_count: 1.0, | ||
} | ||
} | ||
} | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
#[error("Couldn't write to build layer: {0}")] | ||
pub(crate) struct BuildLayerError(std::io::Error); | ||
|
||
const CACHE_ENV: &str = "GOCACHE"; | ||
const CACHE_DIR: &str = "cache"; | ||
const LAYER_VERSION: &str = "1"; | ||
const MAX_CACHE_USAGE_COUNT: f32 = 200.0; | ||
impl From<BuildLayerError> for libcnb::Error<GoBuildpackError> { | ||
fn from(value: BuildLayerError) -> Self { | ||
libcnb::Error::BuildpackError(GoBuildpackError::BuildLayer(value)) | ||
} | ||
} | ||
|
||
impl Layer for BuildLayer { | ||
type Buildpack = GoBuildpack; | ||
type Metadata = BuildLayerMetadata; | ||
enum BuildLayerCacheState { | ||
Expired, | ||
Invalid, | ||
Valid, | ||
} | ||
|
||
fn types(&self) -> LayerTypes { | ||
LayerTypes { | ||
/// Create or restore the layer for cached incremental build artifacts | ||
pub(crate) fn handle_build_layer( | ||
context: &BuildContext<GoBuildpack>, | ||
go_version: &GoVersion, | ||
) -> libcnb::Result<LayerEnv, GoBuildpackError> { | ||
let mut metadata = BuildLayerMetadata::new(go_version, &context.target); | ||
let layer_ref = context.cached_layer( | ||
layer_name!("go_build"), | ||
CachedLayerDefinition { | ||
build: true, | ||
launch: false, | ||
cache: true, | ||
} | ||
} | ||
invalid_metadata_action: &|_| { | ||
( | ||
InvalidMetadataAction::DeleteLayer, | ||
BuildLayerCacheState::Invalid, | ||
) | ||
}, | ||
restored_layer_action: &|restored_metadata: &BuildLayerMetadata, _| { | ||
if restored_metadata.cache_usage_count >= MAX_CACHE_USAGE_COUNT { | ||
return ( | ||
RestoredLayerAction::DeleteLayer, | ||
( | ||
BuildLayerCacheState::Expired, | ||
restored_metadata.cache_usage_count, | ||
), | ||
); | ||
} | ||
if restored_metadata.go_major_version != metadata.go_major_version | ||
|| restored_metadata.target_arch != metadata.target_arch | ||
|| restored_metadata.target_distro_name != metadata.target_distro_name | ||
|| restored_metadata.target_distro_version != metadata.target_distro_version | ||
{ | ||
return ( | ||
RestoredLayerAction::DeleteLayer, | ||
( | ||
BuildLayerCacheState::Invalid, | ||
restored_metadata.cache_usage_count, | ||
), | ||
); | ||
} | ||
( | ||
RestoredLayerAction::KeepLayer, | ||
( | ||
BuildLayerCacheState::Valid, | ||
restored_metadata.cache_usage_count, | ||
), | ||
) | ||
}, | ||
}, | ||
)?; | ||
|
||
fn create( | ||
&mut self, | ||
ctx: &BuildContext<Self::Buildpack>, | ||
layer_path: &Path, | ||
) -> Result<LayerResult<Self::Metadata>, GoBuildpackError> { | ||
log_info("Creating Go build cache"); | ||
let cache_dir = layer_path.join(CACHE_DIR); | ||
fs::create_dir(&cache_dir).map_err(BuildLayerError)?; | ||
LayerResultBuilder::new(self.generate_layer_metadata(&ctx.target, 1.0)) | ||
.env(LayerEnv::new().chainable_insert( | ||
Scope::Build, | ||
libcnb::layer_env::ModificationBehavior::Override, | ||
CACHE_ENV, | ||
cache_dir, | ||
)) | ||
.build() | ||
} | ||
|
||
fn update( | ||
&mut self, | ||
ctx: &BuildContext<Self::Buildpack>, | ||
layer: &LayerData<Self::Metadata>, | ||
) -> Result<LayerResult<Self::Metadata>, GoBuildpackError> { | ||
LayerResultBuilder::new(self.generate_layer_metadata( | ||
&ctx.target, | ||
layer.content_metadata.metadata.cache_usage_count + 1.0, | ||
)) | ||
.env(LayerEnv::new().chainable_insert( | ||
Scope::Build, | ||
libcnb::layer_env::ModificationBehavior::Override, | ||
CACHE_ENV, | ||
layer.path.join(CACHE_DIR), | ||
)) | ||
.build() | ||
} | ||
|
||
fn existing_layer_strategy( | ||
&mut self, | ||
ctx: &BuildContext<Self::Buildpack>, | ||
layer: &LayerData<Self::Metadata>, | ||
) -> Result<ExistingLayerStrategy, <Self::Buildpack as Buildpack>::Error> { | ||
let cached_metadata = &layer.content_metadata.metadata; | ||
if cached_metadata.cache_usage_count >= MAX_CACHE_USAGE_COUNT { | ||
match layer_ref.state { | ||
LayerState::Empty { | ||
cause: EmptyLayerCause::NewlyCreated, | ||
} => (), | ||
LayerState::Empty { | ||
cause: | ||
EmptyLayerCause::RestoredLayerAction { | ||
cause: (BuildLayerCacheState::Expired, _), | ||
}, | ||
} => { | ||
log_info("Discarding expired Go build cache"); | ||
return Ok(ExistingLayerStrategy::Recreate); | ||
} | ||
let new_metadata = | ||
&self.generate_layer_metadata(&ctx.target, cached_metadata.cache_usage_count); | ||
|
||
if cached_metadata != new_metadata { | ||
LayerState::Empty { .. } => { | ||
log_info("Discarding invalid Go build cache"); | ||
return Ok(ExistingLayerStrategy::Recreate); | ||
} | ||
log_info("Reusing existing Go build cache"); | ||
Ok(ExistingLayerStrategy::Update) | ||
LayerState::Restored { .. } => { | ||
log_info("Reusing existing Go build cache"); | ||
} | ||
} | ||
} | ||
|
||
impl BuildLayer { | ||
fn generate_layer_metadata( | ||
&self, | ||
target: &Target, | ||
cache_usage_count: f32, | ||
) -> BuildLayerMetadata { | ||
BuildLayerMetadata { | ||
layer_version: LAYER_VERSION.to_string(), | ||
go_major_version: self.go_version.major_release_version(), | ||
target_arch: target.arch.to_string(), | ||
target_distro_name: target.distro_name.to_string(), | ||
target_distro_version: target.distro_version.to_string(), | ||
cache_usage_count, | ||
match layer_ref.state { | ||
LayerState::Restored { | ||
cause: (_, cache_usage_count), | ||
} => { | ||
metadata.cache_usage_count += cache_usage_count; | ||
} | ||
LayerState::Empty { .. } => { | ||
log_info("Creating Go build cache"); | ||
let cache_dir = layer_ref.path().join(CACHE_DIR); | ||
fs::create_dir(&cache_dir).map_err(BuildLayerError)?; | ||
layer_ref.write_env(LayerEnv::new().chainable_insert( | ||
Scope::Build, | ||
libcnb::layer_env::ModificationBehavior::Override, | ||
CACHE_ENV, | ||
cache_dir, | ||
))?; | ||
} | ||
} | ||
layer_ref.write_metadata(metadata)?; | ||
layer_ref.read_env() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters