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
feat(*): add installation delete options/flags to relevant commands #1189
feat(*): add installation delete options/flags to relevant commands #1189
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 wasn't able to finish reviewing but here are some initial comments
@@ -45,6 +45,8 @@ func (t *TestRuntime) LoadBundle(bundleFile string) (bundle.Bundle, error) { | |||
} | |||
|
|||
func (t *TestRuntime) Execute(args ActionArguments) error { | |||
args.Driver = debugDriver | |||
if args.Driver == "" { |
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.
👍
42f8439
to
fbf316c
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.
Okay! Reviewed all the files now
pkg/porter/dependencies.go
Outdated
|
||
if depArgs.Action == claim.ActionUninstall { | ||
if e.parentOpts.Delete && !e.parentOpts.ForceDelete { | ||
executeErrs = multierror.Append(executeErrs, fmt.Errorf("not deleting installation %s as uninstall was not successful; use --force-delete to override", depArgs.Installation)) |
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.
Can you update this error message to match the other as well that you just updated?
fmt.Fprintf(p.Out, "uninstalling %s...\n", opts.Name) | ||
err = p.CNAB.Execute(opts.ToActionArgs(deperator)) | ||
if err != nil { | ||
uninstallErrs = multierror.Append(uninstallErrs, err) | ||
if strings.Contains(err.Error(), claim.ErrInstallationNotFound.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.
Can you add a comment on why this special handling for ErrInstallationNotFound is needed?
pkg/porter/uninstall.go
Outdated
} | ||
|
||
if opts.Delete && !opts.ForceDelete { | ||
uninstallErrs = multierror.Append(uninstallErrs, fmt.Errorf("not deleting installation %s as uninstall was not successful; use --force-delete to override", opts.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.
Sorry looks like that error message I had you update was used here as well.
pkg/porter/uninstall.go
Outdated
if !opts.ForceDelete { | ||
return uninstallErrs | ||
} | ||
// else, we swallow uninstallErrs as opts.ForceDelete is true |
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 it would be helpful to no matter what, print out the errors even on --force-delete. We just will have a 0 exit code for --force-delete even when there are errors. That way you know what happened.
} | ||
|
||
// TODO: See https://github.com/deislabs/porter/issues/465 for flag to allow keeping around the dependencies | ||
return deperator.Execute() | ||
err = deperator.Execute() | ||
if err != 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.
Right now it's confusing how the ForceDelete/Delete is handled by both this function and the Deperator, and then they have all sorts of conditionals for handling the error, printing that warning about using --force, etc. I am concerned that it's going to be difficult to maintain without introducing bugs or fixing a bug accidentally in just one place (and forgetting to fix it in the dependencies.go for example).
🤔 Looking at it in a github code review it's hard to say "just change X" to simplify it. But I do wish there was a way to do so. Maybe we can take a second pass at it and see if there's a way to either extract the logic for dealing with it, or consolidate the logic for checking and printing the messages? I'm not sure what to suggest which is why I think trying again in a follow-up or whenever we next feel clever or inspired is fine.
One idea is to have a function takes in the flags (delete, force delete and the installation status) and it handles deciding if we should delete the installation and printing the warning. Then at least we could reuse that in 2+ places?
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're right, this area warrants further thought. Thank you for the initial suggestions. I'll spend some more time here and hopefully have an update w/ less fragility and duplication.
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 working on this 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.
Ok, refactored for a bit of simplification... or at least some shared logic. Not sure if better, but perhaps it might generate some further ideas: dd5dd04
tests/dependencies_test.go
Outdated
@@ -31,6 +31,8 @@ func TestDependenciesLifecycle(t *testing.T) { | |||
invokeWordpressBundle(p, namespace) | |||
|
|||
uninstallWordpressBundle(p, namespace) | |||
|
|||
noReallyUninstallWordpressBundle(p, namespace) |
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.
😂
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, I just realized, should I just place this uninstall+delete logic into cleanupWordpressBundle
? Or keep it separate?
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 updated cleanupWordpressBundle
w/ uninstall+delete logic in 6af718a)
tests/uninstall_test.go
Outdated
installationExists bool | ||
wantError string | ||
}{ | ||
{"not yet installed", false, false, false, false, false, "1 error occurred:\n\t* could not load installation HELLO: Installation does not exist\n\n"}, |
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 suggest test cases like this, which are really long, from setting every field without using the field name (which is quite hard to read without the support of an editor) to using the field names and only setting the ones that are non-default values,
{name: "not yet installed", wantError: "1 error occurred..."},
ccebc29
to
29d0a32
Compare
func cleanupWordpressBundle(p *porter.TestPorter, namespace string) { | ||
uninstallOptions := porter.UninstallOptions{} | ||
uninstallOptions.CredentialIdentifiers = []string{"ci"} | ||
uninstallOptions.Delete = true |
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.
👍
pkg/porter/lifecycle.go
Outdated
@@ -9,6 +9,7 @@ type BundleLifecycleOpts struct { | |||
sharedOptions | |||
BundlePullOptions | |||
AllowAccessToDockerHost bool | |||
UninstallDeleteOptions |
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 isn't the right place for these options as it applies to a bunch of commands: install, upgrade, invoke, etc. Can you move it to UninstallOptions
as I think these flags only apply to the uninstall command, right?
pkg/porter/dependencies.go
Outdated
|
||
dep.outputs = outputs | ||
if e.parentOpts.shouldDelete() { |
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 you put the UninstallDeleteOptions
on the generic BundleLifecycleOptions
so that you could make this check here, right?
Instead, since I'm asking further down to move that to the UninstallOptions
type would be to do a type check
if uninstOpts, ok := e.parentOpts.(UninstallOptions); ok {
...
}
Right now parentOpts
is just a BundleLifecycleOptions
but we could make it an interface (pseudo code, not tested, but something like below), so that we could cast and get at the original options as needed while still having access to the other data that we were using on the parentOpts the rest of the time.
What do you think?
type BundleAction interface {
// Implemented for free because of the embedded BundleLifecycleOpts
ToActionArgs()
// Let us grab and get direct access to the bundle opts fields like we need in some funcs
GetBundleLifecycleOptions()
}
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.
Great suggestion! Thank you for providing the blueprint on this approach. Here's what I've updated: 4b2b647
How does it look?
return opts.Delete && !opts.ForceDelete | ||
} | ||
|
||
func (opts *UninstallDeleteOptions) handleUninstallErrs(out io.Writer, err error) 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.
👍
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
Co-authored-by: Carolyn Van Slyck <me@carolynvanslyck.com>
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
…cuting dependencies Signed-off-by: Vaughn Dice <vadice@microsoft.com>
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
6af718a
to
353dfd5
Compare
Signed-off-by: Vaughn Dice <vadice@microsoft.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.
Looks good, just had a question that I'm not sure matters yet or not
if executeErrs != nil { | ||
return executeErrs | ||
} | ||
} | ||
|
||
if e.parentOpts.shouldDelete() { | ||
if uninstallOpts.shouldDelete() { |
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 uninstallOpts is an empty struct (because parentOpts was some other action), what does this call 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.
I think all methods on an empty struct (when action not uninstall) return the uninitialized/default values for the struct fields... so mostly boolean false
values, which is what we'd want here. The tests seem to agree.
However, I can see this approach/code being fragile... even if the assumptions are true. What do we think? Should we refactor the areas where we rely on uninstallOpts to run only if we are sure the struct is actually that type (and not empty)?
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 it would get pretty gnarly to conditionally call those, so the empty struct is fine. I was just double checking. Maybe let's just add a comment about if it's not uninstall, it's a no-op handler?
pkg/porter/lifecycle.go
Outdated
// when the implementation contains further, action-specific options beyond | ||
// the stock BundleLifecycleOptions. | ||
type BundleAction interface { | ||
ToActionArgs(*dependencyExecutioner) cnabprovider.ActionArguments |
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 like this isn't used anywhere? I thought it may be used depending on how the implementation worked out but guess not.
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 is used here: https://github.com/deislabs/porter/blob/4b2b64746636e6997ee1f3dae8c87953c8b0cddc/pkg/porter/dependencies.go#L86
But, to your point, it could just as easily be removed from this interface and exist as it once was (a method on BundleLifecycleOpts
), since at this time we don't see a need to have BundleAction implementations overriding its behavior. Shall I change this back and remove the method from the interface?
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, thanks I obviously missed that. Either way is fine, up to you!
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, removed from interface 👍
@@ -28,8 +39,12 @@ func (o *BundleLifecycleOpts) Validate(args []string, porter *Porter) error { | |||
return nil | |||
} | |||
|
|||
func (o BundleLifecycleOpts) GetBundleLifecycleOptions() BundleLifecycleOpts { |
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.
Brilliant! I wouldn't have thought to implement this here, and would have put it on each of the actions but this is sneakier. 💯
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
Signed-off-by: Vaughn Dice <vadice@microsoft.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.
Thanks this is going to be super helpful for cleaning up my dev machine! 😂
Signed-off-by: Vaughn Dice <vadice@microsoft.com>
docs(cli): missed doc updates from PR #1189
What does this change
porter uninstall
orporter installation delete
(as designed in Do we need a purge? #1163)What issue does it fix
Closes #1163
Notes for the reviewer
Checklist
If this is your first pull request, please add your name to the bottom of our Contributors list. Thank you for making Porter better! 🙇♀️