Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error handling #84

Merged
merged 6 commits into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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