-
Notifications
You must be signed in to change notification settings - Fork 562
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/snapstate: refactor Install functions to use a singular implementation that operates on a Target #13949
base: master
Are you sure you want to change the base?
Conversation
…s follow correct pattern when returning no error
…terfaces, which controls how snaps are found
InstallPathMany is omitted for now, since it is more of an update rather than an installation.
…ality in InstallWithDeviceContext
ca87521
to
6a240ff
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.
Thanks for this. I left a few comments but this looks much better
overlord/snapstate/target.go
Outdated
// All of these fields are optional and can be left unset. The options in this | ||
// struct apply to all snaps that are part of an operation. Options that apply | ||
// to individual snaps can be found in RevisionOptions. | ||
type SnapstateOptions struct { |
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.
Since this is already in snapstate I don't think we need to repeat it in the name. From the outside it looks like snapstate.SnapstateOptions
vs snapstate.Options
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.
Good call, I had the same thought when I started using it to implement installing components from the store. Although, Samuele suggested that this type might not be something we want at all.
overlord/snapstate/target.go
Outdated
return nil | ||
} | ||
|
||
func InstallOne(ctx context.Context, st *state.State, target Target, opts SnapstateOptions) (*snap.Info, *state.TaskSet, 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.
Is the goal to fully replace the previous public functions with these later or to keep those as wrappers around these? Because if it's the latter, then these can be unexported
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 the goal will be to replace it eventually. For example, when installing components (the motivator for this change), the old API won't get that functionality.
overlord/snapstate/target.go
Outdated
// verify that StoreTarget implements the Target interface | ||
var _ Target = &StoreTarget{} |
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 we need these explicit checks? My impression is that this goes against the point of implicit interface implementation. We have them in other places so other people may disagree but I'd say this check is done just by using StoreTarget and if it's not it's because we're not using the interface in which case it doesn't matter
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 don't really need them, no. The main reason I added them in the first place was to verify that &PathTarget{}
is an OptionInitializer
, which wouldn't be compile-time checked anywhere else.
overlord/snapstate/target.go
Outdated
return nil | ||
} | ||
|
||
func InstallOne(ctx context.Context, st *state.State, target Target, opts SnapstateOptions) (*snap.Info, *state.TaskSet, 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.
Comparing InstallOne
with InstallTarget
by their names, it's not clear which one does what. Especially since Target
can be one or many snaps
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.
Hrm, you're right. I'll think about names. In an ideal world, InstallTarget
will become Install
one day, once the old API stops being used.
Co-authored-by: Miguel Pires <miguelpires94@gmail.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.
thank you, did a pass, direction looks good, there is some cleaning up to do though
flags.Lane = lanes[0] | ||
} else { | ||
flags.Transaction = client.TransactionPerSnap | ||
} |
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 logic replaces the one below using joinLane?
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.
Correct, now that InstallWithDeviceContext
supports lanes, we can use that rather than manually doing it here.
overlord/snapstate/target.go
Outdated
|
||
// Installables returns the data needed to setup the snaps from the store for | ||
// installation. | ||
func (s *StoreTarget) Installables(ctx context.Context, st *state.State, installedSnaps map[string]*SnapState, opts Options) ([]Installable, 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.
this is mostly replacing installCandidates?
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, that is correct.
…tErr when only one result is expected
0851b53
to
667d4f6
Compare
…olean flag in Options that allows caller to skip seeded check
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 the changes, did another quick pass with some comments on target.go
overlord/snapstate/target.go
Outdated
if len(infos) != 1 || len(tasksets) != 1 { | ||
return nil, nil, errors.New("internal error: expected exactly one snap and taskset") | ||
} |
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 is a bit strange because one would expect that we could check whether goal return one target or not earlier
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, this is a bit annoying. I was trying to avoid a flag being passed into InstallWithGoal
to check that. One option is some implementation of InstallGoal
that wraps another InstallGoal
that makes sure that we return a singular Target
.
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.
Might be a bit too complex though for something that really could just be an if
.
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 can also be handled with the RequireOneSnap option introduced in 938cd26.
overlord/snapstate/target.go
Outdated
} | ||
|
||
// Target represents the data needed to setup a snap for installation. | ||
type Target struct { |
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.
as we discussed, should we make more things unexported ?
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 is unexported, then the interface should be unexported as well.
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 in bda59b7.
overlord/snapstate/target.go
Outdated
|
||
results, _, err := str.SnapAction(context.TODO(), curSnaps, actions, nil, user, refreshOpts) | ||
if err != nil { | ||
if len(actions) == 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.
I'm not sure this is quite the right approach, passing 1 snap to a Many function previously has different error behavior than passing it to a single target function. We might need to have a Single flag in options or something to control this better
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 was really trying to avoid having to do that, since it'd be really nice if the single case is just a single iteration of the many case. Are there more differences than this error string right here?
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.
Added in 938cd26.
…assing in empty list of actions
…hen using snapstate.Install vs snapstate.InstallMany
… are only installing one snap AND allow us to choose how to generate errors
…Many (#14027) Install and InstallMany have subtle differences in how they handle the case where the store provides an empty response. Install -> returns a snapstate.ErrMissingExpectedResult InstallMany -> returns a store.SnapActionError Add tests to define this behavior, and thus requiring #13949 to conform to this behavior.
@andrewphelpsj there is a conflict 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 for the changes, couple quick comments
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.
thank you, did another pass, some final minor things
…efault to SideInfo.RealName
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.
thank you
} | ||
|
||
// PathInstallGoal creates a new InstallGoal to install a snap from a given from | ||
// a path on disk. If instanceName is not provided, si.RealName will be used. |
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.
would "empty" be clearer than "is not provided"?
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.
Thank you, LGTM. Just some minor questions
// | ||
// TODO: rename this to Install once the API is settled, and we can rename or | ||
// remove the old Install function. | ||
func InstallWithGoal(ctx context.Context, st *state.State, goal InstallGoal, opts Options) ([]*snap.Info, []*state.TaskSet, 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.
Not really a problem but I'm just wondering if the plan is to keep the Install*
functions in target.go
? They feel a bit out of place. I expected target.go
to have functions and interfaces that create installation goals but snapstate.go
to have the functions actually install (handling tasksets and etc). Or is this more to split new functions from old for 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.
I discussed with @andrewphelpsj to create an install.go file as well, maybe it could an immediate follow-up
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.
Yeah, I'll move things around in a follow-up.
ce46744
to
f73a573
Compare
The
Install
family of functions in thesnapstate
has grown, and there are a handful of functions now that increase our API's surface area.Since a lot of these functions look really similar, we can change up the public API a bit to allow for a single
InstallTarget
function that operates on aTarget
, which controls which snaps are installed, and where they come from. In this PR, I've implemented this for installing snaps from the store and local snaps.I was able to implement
Install
,InstallMany
,InstallPath
,InstallWithDeviceContext
, andInstallPathWithDeviceContext
using the newInstallTarget
function. There are two target implementations in this PR,StoreTarget
andPathTarget
.StoreTarget
allows the installation of one or many snaps from the store.PathTarget
allows the installation of one local snap. Note thatPathTarget
does not support installing many local snaps at once, since that looks much more like an update, and should be included in a potential refactor of theUpdate
family.There is very little change to the current public API of the package, as I was able to implement all of the functions mentioned in terms of the new functions. The only minor change needed, outside of the functions themselves, are the changes inside of functions called from the
doPrerequisites
task handler. This handler manually set up some lanes for the installed snaps, sinceInstallWithDeviceContext
did not support lanes. This change enablesInstallWithDeviceContext
to support lanes.Also something that I took note of while I was in here that may not need to be addressed. We don't currently care about validation sets right now when installing local snaps. Should that happen? If so, I could move that code from the
StoreTarget
implementation to theInstallTarget
function so that all installs always care about validation sets.