-
Notifications
You must be signed in to change notification settings - Fork 251
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 message when app needs to be built #884
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've left a bunch of nitty comments but I really like the structure of this - we really should be making much more use of this style of error handling. Good stuff despite the niggles - thank you!
src/lib.rs
Outdated
) | ||
} | ||
|
||
pub(crate) async fn prepare_bindle( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having two prepare_bindle
functions is confusing to me - which do I use where? I'd suggest having just one, the one in spin_publish
, and here having a to_printable_error
function (badly named, sorry). Callers of this would then look like spin_publish::prepare_bindle(...).await.map_err(to_printable_error)
which, although more verbose, makes the additional behaviour easier to see at the call site.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/lib.rs
Outdated
Err(spin_publish::Error::BindleComponentManifest { | ||
source: source_err, | ||
path, | ||
}) if source_err.kind() == std::io::ErrorKind::NotFound => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we detect this in spin_publish::prepare_bindle
and make the error more specific? E.g. Err(PublishError::MissingArtifact(path)) => ...
. That would simplify the pattern match - currently the multiple lines make the flow hard for me to follow (though this is partly down to rustfmt
!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
crates/publish/src/error.rs
Outdated
|
||
/// Describes various errors that can be returned during publishing | ||
#[derive(Debug, thiserror::Error)] | ||
pub enum Error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider a name like PublishError
or BindleError
so it doesn't need to be qualified to distinguish it from anyhow::Error
(was creating noise in the match expression).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to make it consistent with the rest of the spin's codebase:
spin_config::Error
spin_redis::Error
spin_plugins::error::Error
Also, <crate>::Error
looks consistent with the Rust's ecosystem:
std::io::Error
anyhow::Error
std::error::Error
toml::ser::Error
reqwest::Error
On the other hand, I named specialized Result
as PublishResult
but in this case I relied on https://github.com/fermyon/spin/blob/main/crates/plugins/src/error.rs#L1.
All in all, I'm not against any of the variants and can rename it to whatever is considered "the default" for the project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
crates/publish/src/error.rs
Outdated
BindlePushingNotImplemented, | ||
/// IO errors from interacting with the file system | ||
#[error("Error while performing IO operation")] | ||
Io(#[from] std::io::Error), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this relates specifically to the file system then please include a path, the raw Rust io::Error
is sadly unhelpful about this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done. description
field now includes the path. Not added it as a separate field because otherwise it gets tricky when there are more then one paths (like copy
) or none
/// Publishing of components whose sources are already bindles is not supported | ||
#[error("This version of Spin can't publish components whose sources are already bindles")] | ||
BindlePushingNotImplemented, | ||
/// IO errors from interacting with the file system |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this distinct from BindleComponentManifest
which is also an I/O error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropped BindleComponentManifest
crates/publish/src/error.rs
Outdated
/// Malformed bindle id | ||
#[error("App name and version '{0}' do not form a bindle ID")] | ||
BindleId(String), | ||
/// IO error happened during building bindle component manifest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the "bindle component manifest" the munged version of the spin.toml file?
crates/publish/src/error.rs
Outdated
#[error("Failed to build bindle component manifest: {path}")] | ||
BindleComponentManifest { | ||
/// Represents path on a filesystem | ||
path: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given your purpose in this PR, I wonder if it's worth distinguishing "I couldn't find the component.source
file" (Wasm file not built) from "something went wrong getting asset file details" (possibly a build issue, but possibly more generic I/O problem)?
crates/publish/src/error.rs
Outdated
#[error("Error while performing IO operation")] | ||
Io(#[from] std::io::Error), | ||
/// Invalid TOML serialization that can occur when serializing an object to a request | ||
#[error("Failed to write app manifest to TOML")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the "app manifest" the same as the "bindle component manifest"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this description isn't correct. Will update to something more generic like "Failed to serialize to TOML" as it's also used to write invoice.toml
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. If there's any way we can say what we failed to serialise (the invoice, the app manifest, anything else?) that would be super helpful for diagnostics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
crates/publish/src/bindle_writer.rs
Outdated
tokio::fs::copy(&source_file, &dest_file) | ||
.await | ||
.with_context(|| copy_parcel_failed_msg(&source_file, &dest_file))?; | ||
tokio::fs::copy(&source_file, &dest_file).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry about the loss of context here. It looks like if a copy fails, we will no longer get a "parcel copy failed" message, but instead an Io(io::Error)
which will not tell us what was happening when we failed or what file we failed on. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a use-case when copying only one of the parcels will fail?
I was trying to find a balance between wrapping on every level and not giving enough info for debug. Because to my taste, wrapping on every level might be a bit excessive, for ex.:
/tmp/spin bindle push --bindle-server http://localhost:8000/v1
Error: Failed to push bindle to server
Caused by:
0: Failed to push bindle from '/tmp/.tmpaDtdJm' to server at 'http://localhost:8000/v1'
1: Error creating request
2: error sending request for url (http://localhost:8000/v1/_i): error trying to connect: tcp connect error: Connection refused (os error 111)
3: error trying to connect: tcp connect error: Connection refused (os error 111)
4: tcp connect error: Connection refused (os error 111)
5: Connection refused (os error 111)
Here bindle client wraps the error p. 1-5
Anyway, I'll try to figure it out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but there is value in telling the user which parcel failed. For example, if a file is missing or permission denied or something, I need to know which file so I can go in and fix it; I don't want to scrobble through every file in the project.
I agree we sometimes go overboard on error wrapping, and I'm happy to see less of that in the main output (e.g. we could consign the full context to logs, though we'd want a nicer experience for displaying them in this case, a la Fisher's -vvvvvv
). But I don't want to lose information that is critical to diagnosing and remedying a failure. We got burned by "file does not exist (os error 2)" and NOT KNOWING WHICH FILE far too often in the early days of Spin...!
Hope that makes sense!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed
Also thanks heaps for posting the before and after output - this is super useful. It all looks good except "Unable to write into the staging dir" which I have mixed feelings about - I like that it removes the focus on the specific file, but the "error performing I/O operation" drew my attention away from the "permission denied" - could we have something like "failed to save/copy bindle data to temporary directory" maybe? |
9878661
to
5710d0b
Compare
file_sha256_string(&full_path) | ||
.with_context(|| format!("Failed to get parcel id for '{}'", full_path.display()))? | ||
|
||
if let Ok(false) = Path::try_exists(&full_path) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have the min Rust version compiler requirement?
try_exists
is marked stable from 1.63.0 (https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.try_exists)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.63 shouldn't be a problem. I think CI tracks latest stable.
Update
Examples in the PR descriptions were updated to match new behavior |
0d2d6eb
to
5c119ec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good - thanks!
Oh heck, looks like you need to sign/re-sign some commits. Sorry, not sure why GH can't check that at check time instead of 'ready to merge' time. |
Refs: fermyon#859. Signed-off-by: Konstantin Shabanov <mail@etehtsea.me>
Fail deploy with missing build artifact error when app has to be built first. Fixes fermyon#859. Signed-off-by: Konstantin Shabanov <mail@etehtsea.me>
5c119ec
to
81913bc
Compare
I'm forgetting to re-sign commits after rebase. Will check what I can do to avoid this in the future. |
There is a Git config setting you can turn on to GPG sign things (I think VS Code also has some integration for it). If you don't want to always GPG sign, or don't want to use your main GPG key for Spin, it might be possible to set it at a repo level rather than globally, not sure. |
Yeah, previously I temporarily turned it off because I had some issues with P.S. Also, TIL: https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/ |
Context
anyhow
withthiserror
insidespin_publish
library. Tried to preserve the original behavior.Comparison
Project needs to be built
Before:
After (
spin bundle prepare
):After (
spin deploy
):After (
spin bindle push
):Bindle server is unavailable
Before:
After:
Bindle already exists
Before:
Unable to write into the staging dir
Before:
After:
Network error
Before:
After:
Fixes #859.