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
o/hookstate/ctlcmd: unify the error message when context is missing #10606
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 noticed there's a few usages of context()
that pass the context into runServiceCommand
(which then performs this check), could we use ensureContext()
there as well? It'd have to be slightly refactored though
I noticed that, but then I forgot to change that. I now added a commit which changes the error message for the service commands too, but I cannot think of a smart way to refactor 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.
LGTM, thanks!
overlord/hookstate/ctlcmd/ctlcmd.go
Outdated
@@ -66,6 +68,13 @@ func (c *baseCommand) context() *hookstate.Context { | |||
return c.c | |||
} | |||
|
|||
func (c *baseCommand) ensureContext() (context *hookstate.Context, err error) { | |||
if c.c == nil { | |||
err = errors.New(i18n.G(`The "snapctl" command can only be invoked from within a snap`)) |
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.
Thanks for working on this, the change itself is great and a nice idea!
Just a quick drive-by highlevel comment - I think we should not have go error types localized. Instead I would suggest the pattern is something like:
var ErrNoContext = errors.New("cannot use snapctl without a context")
func (c *baseCommand) ensureContext() (context *hookstate.Context, err error) {
...
return ErrNoContext
}
and then in the relevant "main" (cmd/snapctl/main.go I presume) we check for ErrNoContext and if we see that we print the localized error and exit.
I hope this makes sense, sorry that it's a bit terse, I'm a bit short on time right now.
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.
Mmm... I do recognize the problem, but I think that a proper solution cannot be done in the context of this PR. The problem is far from trivial: we have a process boundary between snapctl (which knows the user's locale) and snapd (which doesn't). There are (at least) two possible approaches:
- Have snapd return errors in machine form, either as an untranslated string (as you suggest), or as error codes. Then the client will translate them properly when presenting them to the user.
- Have the client pass locale information to snapd over the REST API, and make
i18n.G()
(or some other function) aware of the context so that it would translate the messages to the locale encoded in the context.
The first option has the big downside that it would only work for fixed messages (a %s
in them would immediately make the message non translatable), unless we design a way to pass more context variables back to the client process and manage to build a sensible error message there.
Anyway, regardless of the path we choose, both require a substantial amount of work, as both snapctl and snapd are not ready for either solution. That's why I propose to leave this for later. :-)
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.
Indeed this is more complicated due to how snapctl is implemented, we already have similar case with ctlcmd.UnsuccessfulError type and respective client.ErrorKindUnsuccessful which requires changes in api_snapctl.go, cmd/snapctl/main.go (as well as overlord/hookstate/ctlcmd/..); I'm not sure it's worth doing atm, but if we want it localized, then we should follow this pattern.
But I agree with the high level remark from @mvo5, the error itself shouldn't be localized.
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.
and nitpick^2, the error isn't strictly true because we deliberately allow 'snapctl --help' invocation from the shell; I think the error message I suggested below in the other comment would solve it.
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.
OK, I removed the i18n call. However, there are still many of such calls in this module; whould I remove all of them (maybe in a separate PR)?
@@ -60,9 +60,9 @@ func init() { | |||
} | |||
|
|||
func (c *fdeSetupRequestCommand) Execute(args []string) error { | |||
context := c.context() | |||
if context == nil { | |||
return fmt.Errorf("cannot run fde-setup-request without a context") |
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 including snapctl subcommand name in the message was nice/useful; maybe "'snapctl %s' can only be invoked from within a snap"?
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!
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.
The new error message is a bit confusing. I need to think of a suggestion
overlord/hookstate/ctlcmd/ctlcmd.go
Outdated
@@ -66,7 +71,16 @@ func (c *baseCommand) context() *hookstate.Context { | |||
return c.c | |||
} | |||
|
|||
func (c *baseCommand) ensureContext() (context *hookstate.Context, err error) { | |||
if c.c == nil { | |||
err = fmt.Errorf(`The "snapctl %s" command can only be invoked from within a snap`, c.name) |
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.
the slight problem with this new error is that it makes almost sound like there are snapctl commands that can be invoked without a snap, but there are none.
The old messages weren't good but din't have this problem
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 would use:
"cannot invoke snapctl operation commands (here %q) from outside of a snap", c.name
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.
Updated.
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.
my comments on error messages
overlord/hookstate/ctlcmd/helpers.go
Outdated
@@ -132,7 +132,7 @@ func queueCommand(context *hookstate.Context, tts []*state.TaskSet) error { | |||
func runServiceCommand(context *hookstate.Context, inst *servicestate.Instruction) error { | |||
if context == nil { | |||
// this message is reused in health.go | |||
return fmt.Errorf(i18n.G("cannot %s without a context"), inst.Action) | |||
return fmt.Errorf(i18n.G(`The snapctl %s action can only be invoked from within a snap`), inst.Action) |
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.
this should use the same message as well but command name here is inst.Action
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.
@mardy was this part addressed?
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.
Yes, it now uses the same error message as in other parts: https://github.com/mardy/snapd/blob/snapctl-context-err/overlord/hookstate/ctlcmd/helpers.go#L133-L135
(if you are merging this yourself please remember to squash :-) )
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.
LGTM
overlord/hookstate/ctlcmd/ctlcmd.go
Outdated
@@ -32,10 +32,19 @@ import ( | |||
"github.com/snapcore/snapd/strutil" | |||
) | |||
|
|||
func createMissingContextError(subcommand string) error { | |||
return fmt.Errorf(`Cannot invoke snapctl operation commands (here %q) from outside of a snap`, subcommand) |
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.
AFAICT errors in go lang shouldn't be capitalized (because they can be appended/wrapped in another error message and it then looks weird); I'm slightly surprised it's not caught by our static checks, I could swear I saw an error about such issue before...
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.
It's because I used a cyrillic "С" instead ;-)
Fixed now.
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.
Thanks
Codecov Report
@@ Coverage Diff @@
## master #10606 +/- ##
==========================================
- Coverage 80.71% 78.30% -2.41%
==========================================
Files 727 882 +155
Lines 58158 99422 +41264
==========================================
+ Hits 46940 77856 +30916
- Misses 7544 16665 +9121
- Partials 3674 4901 +1227
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report at Codecov.
|
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.
It nicely simplified these checks, thank you! +1
overlord/hookstate/ctlcmd/ctlcmd.go
Outdated
@@ -32,10 +32,19 @@ import ( | |||
"github.com/snapcore/snapd/strutil" | |||
) | |||
|
|||
func createMissingContextError(subcommand string) 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.
(nitpick/bike-shed) I wonder if something like:
index 20b8b09067..76bac68240 100644
--- a/overlord/hookstate/ctlcmd/ctlcmd.go
+++ b/overlord/hookstate/ctlcmd/ctlcmd.go
@@ -32,8 +32,12 @@ import (
"github.com/snapcore/snapd/strutil"
)
-func createMissingContextError(subcommand string) error {
- return fmt.Errorf(`cannot invoke snapctl operation commands (here %q) from outside of a snap`, subcommand)
+type MissingContextError struct {
+ subcommand string
+}
+
+func (e *MissingContextError) Error() string {
+ return fmt.Sprintf(`cannot invoke snapctl operation commands (here %q) from outside of a snap`, e.subcommand)
}
type baseCommand struct {
@@ -77,7 +81,7 @@ func (c *baseCommand) context() *hookstate.Context {
func (c *baseCommand) ensureContext() (context *hookstate.Context, err error) {
if c.c == nil {
- err = createMissingContextError(c.name)
+ err = &MissingContextError{c.name}
}
return c.c, err
}
diff --git a/overlord/hookstate/ctlcmd/helpers.go b/overlord/hookstate/ctlcmd/helpers.go
index 5260027224..595d99560b 100644
--- a/overlord/hookstate/ctlcmd/helpers.go
+++ b/overlord/hookstate/ctlcmd/helpers.go
@@ -131,7 +131,7 @@ func queueCommand(context *hookstate.Context, tts []*state.TaskSet) error {
func runServiceCommand(context *hookstate.Context, inst *servicestate.Instruction) error {
if context == nil {
- return createMissingContextError(inst.Action)
+ return &MissingContextError{inst.Action}
}
st := context.State()
is a bit more idiomatic go. I.e. having an error type for this instead of creating different error strings. The end result is the same so it's not really a big deal but I could not not mention it :)
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.
Thanks, applied!
Make the error message more user friendly, since the concept of a "context" is not obvious to the end user.
1a6ebbb
to
a23cc21
Compare
@mvo5, I'm afraid I need your magic powers to merge this. :-) |
Make the error message more user friendly, since the concept of a
"context" is not obvious to the end user. Also let it be translated, and
unified across the various snapctl subcommands.