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
cmd/snap-recovery-chooser: add recovery chooser #8305
cmd/snap-recovery-chooser: add recovery chooser #8305
Conversation
Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
Add a recovery chooser host, which talks to snapd and invokes the actual UI component from snapd internal tools. Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
…overy-chooser-rfc
The actual communication method needs more investigation. Looks like |
@bboozzoo let me know if you want to chat about that |
I was able to fix the issues in console-conf, the the 'blocked' is dropped 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.
LGTM, but I would like to see the env var renamed and potentially also a check on the existence of the file marker before we do anything as a sanity check (but maybe I'm wrong about the purpose of the file marker)
cmd/snap-recovery-chooser/main.go
Outdated
} | ||
|
||
// for local testing | ||
if os.Getenv("USE_STDOUT") != "" { |
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 prefix this env var with SNAPPY_TESTING
or something like that to make it clear this is only for tests and shouldn't be relied on by anybody else?
}) | ||
|
||
c.Assert(s.markerFile, testutil.FileAbsent) | ||
|
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.
unnecessary whitespace ?
|
||
func chooser(cli *client.Client) error { | ||
// consume the trigger file | ||
defer cleanupTriggerMarker() |
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.
should we also actually check that there is a marker file before we run (and then delete it if there is)?
I think it's unlikely this binary will ever be used except by something which sees the trigger file and then triggers this to run, but still perhaps it's a good idea?
…overy-chooser-rfc
…rtup Forward the response from the UI back to snapd. Also look at SNAPPY_TESTING instead of USE_STDOUT. Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
cmd/snap-recovery-chooser/main.go
Outdated
@@ -119,6 +137,15 @@ func cleanupTriggerMarker() error { | |||
} | |||
|
|||
func chooser(cli *client.Client) error { | |||
snappyTesting := os.Getenv("SNAPPY_TESTING") != "" |
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.
won't this change the behavior during spread tests generally? Or perhaps this binary will only ever be used in spread in an artificial way?
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.
ah, didn't notice setting this top level in spread.yaml 😞
Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
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.
Still LGTM with the actual call to the snapd API at the end
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 really nice.
I left a few comments about packaging and some questions here and there.
func outputForUI(out io.Writer, sys *ChooserSystems) error { | ||
enc := json.NewEncoder(out) | ||
if err := enc.Encode(sys); err != nil { | ||
return fmt.Errorf("cannot serialize chooser options: %v", err) |
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.
For consistency between the message and the type.
return fmt.Errorf("cannot serialize chooser options: %v", err) | |
return fmt.Errorf("cannot serialize chooser systems: %v", err) |
} | ||
|
||
func chooser(cli *client.Client) error { | ||
snappyTesting := os.Getenv("SNAPPY_TESTING_USE_STDOUT") != "" |
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.
We have those "getenv bool" helpers. This looks like a place to use one.
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 SNAPPY_ prefix is historical, we use SNAPD_ these days, please let's not use it for new things
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'll go with SNAPD_TESTING_USE_STDOUT.
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.
You could use osutil.GetenvBool()
here which would make this a bit more natural, the current code will behave unexpectedly when using "SNAPPY_TESTING_USE_STDOUT=false" (but I guess zyga said this already).
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.
We could do something like https://paste.ubuntu.com/p/sXqWJkT7wp/ and check the outputForUI this way (or keeping in a separate test with something like https://paste.ubuntu.com/p/s86HQY8zfn/)
return fmt.Errorf("UI process failed: %v", err) | ||
} | ||
|
||
logger.Noticef("got response: %+v", response) |
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.
What does %+v
do?
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.
%v the value in a default format
when printing structs, the plus flag (%+v) adds field names
}) | ||
c.Assert(err, IsNil) | ||
default: | ||
c.Fatalf("expected to get 1 requests, now on %d", n+1) |
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, the number here is zero-based, which might be surprising if the test ever fails and someone has to debug 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.
It's a pattern we use throughout the client/snap command tests. Still, the message should say at most 2 requests..
, so fixed that.
} | ||
|
||
func (s *mockedClientCmdSuite) TestMainChooserStdout(c *C) { | ||
os.Setenv("SNAPPY_TESTING_USE_STDOUT", "1") |
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 makes me wonder, should the side that reads this be using snapdenv
?
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 flag is only used in the snap-recovery-chooser binary.
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 it's very specific to testing this code I would say no. For the moment the main candidates for snapdenv are things used by more than one package, or well advertised to users
@@ -9,6 +9,7 @@ usr/bin/snapd /usr/lib/snapd/ | |||
usr/bin/snap-seccomp /usr/lib/snapd/ | |||
usr/bin/snap-bootstrap /usr/lib/snapd/ | |||
usr/bin/snap-preseed /usr/lib/snapd/ | |||
usr/bin/snap-recovery-chooser /usr/lib/snapd/ |
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 DM in me would say, please write a manual page.
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 user is expected to ever run that themselves.
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.
Then it should be in libexecdir
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.
Oh, wait, it is. I take that back :)
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.
${libexecdir}/snapd
is /usr/lib/snapd
on Ubuntu, isn't it?
usr/bin/snap-bootstrap | ||
usr/bin/snap-recovery-chooser |
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 need to handle this in packaging/snapd.mk
, namely to remove it like so:
ifeq ($(with_core_bits),0)
# Remove core 20 recovery chooser
install::
rm -f $(DESTDIR)$(bindir)/snap-recovery-chooser
endif
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.
We just don't build it there as it has no use outside of Ubuntu package.
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.
Ah, indeed. I was under the impression we build all go binaries. This is fine though I would prefer to have it for completeness, with the hope that this makefile can be used in all packaging at some point.
The PR in subiquity canonical/subiquity#655 has landed. |
And additional fixes in canonical/subiquity#680 have landed too. We should probably merge this PR and do any tweaks in follow ups. |
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 looks fine, I have some nitpicks inside but all followup material
} | ||
// TODO:UC20 update once upstream console-conf has the right command | ||
// line switches | ||
return exec.Command(tool, "--recovery-chooser-mode"), nil |
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 upstream console-conf changes have landed, so is this still a valid todo?
// reuse stderr | ||
cmd.Stderr = os.Stderr | ||
// the chooser may be invoked via console-conf service which uses | ||
// KillMethod=process, so make sure the UI process dies when we die |
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.
Nice! Also thanks for the comment.
} | ||
|
||
func cleanupTriggerMarker() error { | ||
err := os.Remove(defaultMarkerFile) |
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^2) I think slightly more go-ish would be:
if err := os.Remove(defaultMarkerFile); err != nil && !os.IsNotExit(err) {
return err
}
return nil
but I already feel bad writing this comment as it's sooooooo much a nitpick
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 and some other tweaks are done in #8415
} | ||
|
||
func chooser(cli *client.Client) error { | ||
snappyTesting := os.Getenv("SNAPPY_TESTING_USE_STDOUT") != "" |
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.
You could use osutil.GetenvBool()
here which would make this a bit more natural, the current code will behave unexpectedly when using "SNAPPY_TESTING_USE_STDOUT=false" (but I guess zyga said this already).
} | ||
|
||
// for local testing | ||
if snappyTesting { |
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'm not at the point yet where I see how this is used but it makes me wonder if for testing we could have something like a "testingUI" that capture stdin and gives canned replies
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.
When testing locally it's sometimes useful to do:
$ USE_STDOUT=1 snap-recovery-chooser | console-conf --recovery-chooser-mode
# or
$ USE_STDOUT=1 snap-recovery-chooser > canned-input
$ console-conf --recovery-chooser-mode < canned-input
This is a 2nd version of #8191. The PR implements a recovery chooser that acts as a simple proxy between snapd and the UI tool, currently
console-conf
. The chooser obtains a list of systems over snapd API endpoint, passes that to the UI process over stdin, then waits for the process to finish and print out the choice to its stdout. The coupling allows us to decouple the UI tool from snapd, where eventually the tool could be part of one of the snaps and run confined.The PR requires changes from #8298.