Skip to content

Commit

Permalink
Improve error handling (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
runesoerensen authored Jul 20, 2024
1 parent 8a686be commit 9e2b3d4
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 59 deletions.
31 changes: 10 additions & 21 deletions buildpacks/dotnet/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
"determining if the .NET buildpack should be run for this application",
io_error,
),
DotnetBuildpackError::NoDotnetFiles => log_error(
"No .NET solution or project files found",
formatdoc! {"
While determining the .NET file to publish, neither a solution or project file was found.
This should never occur, as the detect phase should only succeed if a publishable .NET file
was found.
If you see this error, please file an issue:
https://github.com/heroku/buildpacks-dotnet/issues/new
"},
),
DotnetBuildpackError::NoSolutionProjects => {
log_error("No project references found in solution", String::new());
}
Expand All @@ -54,7 +43,7 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
The root directory contains multiple .NET project files: {message}"
},
),
DotnetBuildpackError::LoadDotnetSolutionFile(error) => match error {
DotnetBuildpackError::LoadSolutionFile(error) => match error {
solution::LoadError::ReadSolutionFile(io_error) => log_io_error(
"Unable to load solution file",
"reading solution file",
Expand All @@ -64,7 +53,7 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
on_load_dotnet_project_error(load_project_error, "reading solution project files");
}
},
DotnetBuildpackError::LoadDotnetProjectFile(error) => {
DotnetBuildpackError::LoadProjectFile(error) => {
on_load_dotnet_project_error(error, "reading root project file");
}
// TODO: Add the erroneous input values to these error messages
Expand Down Expand Up @@ -112,7 +101,7 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
Details: {error}
"},
),
DotnetBuildpackError::ParseVersionRequirement(error) => log_error(
DotnetBuildpackError::ParseSolutionVersionRequirement(error) => log_error(
"Invalid .NET SDK version requirement",
formatdoc! {"
The inferred .NET SDK version requirement could not be parsed.
Expand All @@ -127,20 +116,20 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
"},
),
DotnetBuildpackError::SdkLayer(error) => match error {
SdkLayerError::DownloadSdk(error) => log_error(
SdkLayerError::DownloadArchive(error) => log_error(
"Couldn't download .NET SDK",
formatdoc! {"
Details: {error}
"},
),
SdkLayerError::ReadSdkArchive(io_error) => {
SdkLayerError::ReadArchive(io_error) => {
log_io_error(
"Couldn't read .NET SDK archive",
"reading downloaded .NET SDK archive",
io_error,
);
}
SdkLayerError::VerifyChecksum { expected, actual } => log_error(
SdkLayerError::VerifyArchiveChecksum { expected, actual } => log_error(
"Corrupted .NET SDK download",
formatdoc! {"
Validation of the downloaded .NET SDK failed due to a checksum mismatch.
Expand All @@ -149,20 +138,20 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
Actual: {actual}
", expected = hex::encode(expected), actual = hex::encode(actual) },
),
SdkLayerError::OpenSdkArchive(io_error) => {
SdkLayerError::OpenArchive(io_error) => {
log_io_error(
"Couldn't open .NET SDK archive",
"opening downloaded .NET SDK archive",
io_error,
);
}
SdkLayerError::UntarSdk(io_error) => log_io_error(
SdkLayerError::DecompressArchive(io_error) => log_io_error(
"Couldn't decompress .NET SDK",
"untarring .NET SDK archive",
io_error,
),
},
DotnetBuildpackError::ParseDotnetBuildpackConfigurationError(error) => match error {
DotnetBuildpackError::ParseBuildpackConfiguration(error) => match error {
DotnetBuildpackConfigurationError::InvalidMsbuildVerbosityLevel(verbosity_level) => {
log_error(
"Invalid MSBuild verbosity level",
Expand Down Expand Up @@ -201,7 +190,7 @@ fn on_buildpack_error(error: &DotnetBuildpackError) {
"},
),
},
DotnetBuildpackError::CopyRuntimeFilesToRuntimeLayer(io_error) => log_io_error(
DotnetBuildpackError::CopyRuntimeFiles(io_error) => log_io_error(
"Error copying .NET runtime files",
"copying .NET runtime files from the sdk layer to the runtime layer",
io_error,
Expand Down
2 changes: 1 addition & 1 deletion buildpacks/dotnet/src/layers/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub(crate) fn handle(

for path in RUNTIME_PATHS {
utils::copy_recursively(sdk_layer_path.join(path), runtime_layer.path().join(path))
.map_err(DotnetBuildpackError::CopyRuntimeFilesToRuntimeLayer)?;
.map_err(DotnetBuildpackError::CopyRuntimeFiles)?;
}

Ok(())
Expand Down
20 changes: 10 additions & 10 deletions buildpacks/dotnet/src/layers/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,17 @@ pub(crate) fn handle(
));

let path = temp_dir().as_path().join("dotnetsdk.tar.gz");
download_file(&artifact.url, path.clone()).map_err(SdkLayerError::DownloadSdk)?;
download_file(&artifact.url, path.clone()).map_err(SdkLayerError::DownloadArchive)?;

log_info("Verifying checksum");
verify_checksum(&artifact.checksum, path.clone())?;

log_info("Installing .NET SDK");
decompress_tarball(
&mut File::open(path.clone()).map_err(SdkLayerError::OpenSdkArchive)?,
&mut File::open(path.clone()).map_err(SdkLayerError::OpenArchive)?,
sdk_layer.path(),
)
.map_err(SdkLayerError::UntarSdk)?;
.map_err(SdkLayerError::DecompressArchive)?;

sdk_layer.write_env(dotnet_layer_env::generate_layer_env(
sdk_layer.path().as_path(),
Expand All @@ -106,12 +106,12 @@ where
{
let calculated_checksum = fs::read(path.as_ref())
.map(|data| D::digest(data).to_vec())
.map_err(SdkLayerError::ReadSdkArchive)?;
.map_err(SdkLayerError::ReadArchive)?;

if calculated_checksum == checksum.value {
Ok(())
} else {
Err(SdkLayerError::VerifyChecksum {
Err(SdkLayerError::VerifyArchiveChecksum {
expected: checksum.value.clone(),
actual: calculated_checksum,
})
Expand All @@ -120,11 +120,11 @@ where

#[derive(Debug)]
pub(crate) enum SdkLayerError {
DownloadSdk(libherokubuildpack::download::DownloadError),
UntarSdk(std::io::Error),
VerifyChecksum { expected: Vec<u8>, actual: Vec<u8> },
OpenSdkArchive(std::io::Error),
ReadSdkArchive(std::io::Error),
DownloadArchive(libherokubuildpack::download::DownloadError),
DecompressArchive(std::io::Error),
VerifyArchiveChecksum { expected: Vec<u8>, actual: Vec<u8> },
OpenArchive(std::io::Error),
ReadArchive(std::io::Error),
}

impl From<SdkLayerError> for libcnb::Error<DotnetBuildpackError> {
Expand Down
50 changes: 23 additions & 27 deletions buildpacks/dotnet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl Buildpack for DotnetBuildpack {

fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
let buildpack_configuration = DotnetBuildpackConfiguration::try_from(&Env::from_current())
.map_err(DotnetBuildpackError::ParseDotnetBuildpackConfigurationError)?;
.map_err(DotnetBuildpackError::ParseBuildpackConfiguration)?;

log_header("Determining .NET version");
let solution = get_solution_to_publish(&context.app_dir)?;
Expand Down Expand Up @@ -161,31 +161,28 @@ fn get_solution_to_publish(app_dir: &Path) -> Result<Solution, DotnetBuildpackEr
);
}
return Solution::load_from_path(solution_file)
.map_err(DotnetBuildpackError::LoadDotnetSolutionFile);
.map_err(DotnetBuildpackError::LoadSolutionFile);
}

let project_file_paths =
detect::project_file_paths(app_dir).expect("function to pass after detection");
if let Some(project_file) = detect::project_file_paths(app_dir)
.expect("function to pass after detection")
.first()
{
if project_file_paths.len() > 1 {
return Err(DotnetBuildpackError::MultipleProjectFiles(
project_file_paths
.iter()
.map(|f| f.to_string_lossy().to_string())
.collect::<Vec<String>>()
.join(", "),
));
}
return Ok(Solution::ephemeral(
Project::load_from_path(project_file)
.map_err(DotnetBuildpackError::LoadDotnetProjectFile)?,
if project_file_paths.len() > 1 {
return Err(DotnetBuildpackError::MultipleProjectFiles(
project_file_paths
.iter()
.map(|f| f.to_string_lossy().to_string())
.collect::<Vec<String>>()
.join(", "),
));
}

Err(DotnetBuildpackError::NoDotnetFiles)
Ok(Solution::ephemeral(
Project::load_from_path(
project_file_paths
.first()
.expect("A project file to be present"),
)
.map_err(DotnetBuildpackError::LoadProjectFile)?,
))
}

fn get_solution_sdk_version_requirement(
Expand Down Expand Up @@ -216,7 +213,7 @@ fn get_solution_sdk_version_requirement(
.last()
.ok_or(DotnetBuildpackError::NoSolutionProjects)?,
)
.map_err(DotnetBuildpackError::ParseVersionRequirement)
.map_err(DotnetBuildpackError::ParseSolutionVersionRequirement)
}

fn detect_global_json_sdk_version_requirement(
Expand All @@ -237,22 +234,21 @@ fn detect_global_json_sdk_version_requirement(
#[derive(Debug)]
enum DotnetBuildpackError {
BuildpackDetection(io::Error),
NoDotnetFiles,
NoSolutionProjects,
MultipleProjectFiles(String),
LoadDotnetSolutionFile(dotnet::solution::LoadError),
LoadDotnetProjectFile(dotnet::project::LoadError),
LoadSolutionFile(dotnet::solution::LoadError),
LoadProjectFile(dotnet::project::LoadError),
ParseTargetFrameworkMoniker(ParseTargetFrameworkError),
ReadGlobalJsonFile(io::Error),
ParseGlobalJson(serde_json::Error),
ParseGlobalJsonVersionRequirement(semver::Error),
ParseInventory(ParseInventoryError),
ParseVersionRequirement(semver::Error),
ParseSolutionVersionRequirement(semver::Error),
ResolveSdkVersion(VersionReq),
SdkLayer(SdkLayerError),
ParseDotnetBuildpackConfigurationError(DotnetBuildpackConfigurationError),
ParseBuildpackConfiguration(DotnetBuildpackConfigurationError),
PublishCommand(StreamedCommandError),
CopyRuntimeFilesToRuntimeLayer(io::Error),
CopyRuntimeFiles(io::Error),
LaunchProcessDetection(LaunchProcessDetectionError),
}

Expand Down

0 comments on commit 9e2b3d4

Please sign in to comment.