Skip to content
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: Update each index instead of just default #588

Merged
merged 8 commits into from
Apr 13, 2020

Conversation

chriskim06
Copy link
Member

Here's the first part to getting update to work with multiple indexes (second part to show updated/new plugins is in progress).

I extracted some of the new code that was added for search into the namingutils.go file. I'm not exactly sure it fits there, it was originally named utils.go where I think it could have made more sense. Let me know if this isn't the right place to put the functionality to get a list of index name/URLs. I also moved the helper function for creating an empty git repo to a method on TempDir to use in the new test.

Related issue: #483

/area multi-index

@k8s-ci-robot k8s-ci-robot added area/multi-index cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Apr 6, 2020
@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 6, 2020
Comment on lines 58 to 76
// allIndexes returns a slice of index name and URL pairs
func allIndexes(p environment.Paths) ([]indexoperations.Index, error) {
indexes := []indexoperations.Index{
{
Name: constants.DefaultIndexName,
URL: constants.DefaultIndexURI,
},
}
if os.Getenv(constants.EnableMultiIndexSwitch) != "" {
out, err := indexoperations.ListIndexes(p)
if err != nil {
return nil, errors.Wrapf(err, "failed to list plugin indexes available")
}
if len(out) != 0 {
indexes = out
}
}
return indexes, nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might've been better if we extracted the feature gate into indexoperations.ListIndexes so we don't reimplement it here.

IIRC there's at least one more place we hardcode this:

[]indexoperations.Index{
		{
			Name: constants.DefaultIndexName,
			URL:  constants.DefaultIndexURI,
		},

you should be able to find very easily.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah if you handle it in one place (for now, until we remove the feature gate saves the following +60 lines in namingutils_test.go for us, and reduces duplication)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good call, I didn't think about that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this was something that we talked about when going over the proposal:

If no indexes are configured, a message is printed explaining how to add krew-index as default

I just want to make sure we're cool with that. The current functionality is that krew-index gets added by default, but once multi-index is out that means that users won't be able to krew install ctx right after install.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're conflating the two issues here:

  1. You should hardcode that index to that method only when X_KREW_MULTI_INDEX is "not" set.

  2. How do we add the default=krew-index (once multi-index work is complete) is another discussion which I'm tracking at Implement custom plugin indexes #566. (Right now we're auto-adding it if it's missing on install/update/upgrade) but this is a side-effect of old code, and it's not about this PR.

Copy link
Member Author

@chriskim06 chriskim06 Apr 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should hardcode that index to that method only when X_KREW_MULTI_INDEX is "not" set.

We decided before that krew index list after a fresh install should work and return an empty list. Do you want me to change that behavior and accompanying tests if we want it to return the hardcoded index when env var isn't set?

How do we add the default=krew-index (once multi-index work is complete) is another discussion which I'm tracking at #566. (Right now we're auto-adding it if it's missing on install/update/upgrade) but this is a side-effect of old code, and it's not about this PR.

I brought it up because that was part of the proposal and it seemed like this would be the PR to take care of the help message when no indexes are configured. If that's not the case then what should I do for now when the env var is set, but no indexes are configured?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided before that krew index list after a fresh install should work and return an empty list. Do you want me to change that behavior and accompanying tests if we want it to return the hardcoded index when env var isn't set?

We should keep this behavior. I only wanted that we call indexoperations.ListIndexes always, but without the feature flag, it returns default index, with the feature flag it lists from filesystem.

I can make this change if you want me to.

I brought it up because that was part of the proposal and it seemed like this would be the PR to take care of the help message when no indexes are configured

That can be a separate PR, let's keep this one small.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I can take care of it here 👍

return errors.Wrap(err, "failed to update the local index")
indexes, err := allIndexes(paths)
if err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap

@@ -105,9 +105,16 @@ func showUpdatedPlugins(out io.Writer, preUpdate, posUpdate []index.Plugin, inst
func ensureIndexUpdated(_ *cobra.Command, _ []string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method needs rename? :)

indexPath := paths.IndexPath(idx.Name)
klog.V(1).Infof("Updating the local copy of plugin index (%s)", indexPath)
if err := gitutil.EnsureUpdated(idx.URL, indexPath); err != nil {
return errors.Wrapf(err, "failed to update the local index %s", idx.Name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

potentially the most interesting part of the PR here:

Do we bail out on the first index failed to update ––OR–– do we go ahead and update all possible indexes, and collect the errors (or at least one of them) + report it at the end.

For example, today, krew install ctx BAD_PLUGIN ns will install ctx & ns, but the overall command will fail. This was intentionally designed for good UX.

With krew install and krew upgrade both calling ensureIndexUpdated every time before they do their job, I think a bad index (e.g. deleted repo) will render the majority of krew user surface unusable ––especially if that index has a name that's alphabetically earlier than "default".

Thoughts @corneliusweig ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the long run, we might want to update indexes in parallel. If doing this in parallel, it is no longer possible to fail on the first index update, because "first" is not a proper concept then. So to avoid behaviour change in the future, we should strive to update everything and collect errors.

Besides, it's just better UX IMO.

@chriskim06 chriskim06 mentioned this pull request Apr 7, 2020
22 tasks
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 7, 2020
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 8, 2020
@chriskim06
Copy link
Member Author

chriskim06 commented Apr 8, 2020

I'm working on adding integration tests for this.

I switched this back to sequentially updating the indexes for now, I ran into an issue with the test that checks for the index failed update. The test hangs when the update fails in a goroutine for some reason and I've been trying to figure out why.

Would it be ok to create an issue and follow up PR for making the update concurrent?

Switch back to sequential updates. The test where gitutil.EnsureUpdated
fails seems to hang when it happens in a goroutine for some reason.
Comment on lines 71 to 73
if len(out) == 0 {
t.Error("no plugins in index foo after update")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't you be also checking what's in default index now?
also you can use something like krew search here to find plugins in all indexes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya good call 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be enough to look for two plugin names in the output. For example, ctx and foo/ctx.

indexPath := paths.IndexPath(idx.Name)
klog.V(1).Infof("Updating the local copy of plugin index (%s)", indexPath)
if err := gitutil.EnsureUpdated(idx.URL, indexPath); err != nil {
klog.V(1).Infof("error updating index %s: %s", idx.Name, err)
Copy link
Member

@ahmetb ahmetb Apr 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should probably do this as klog.Warningf otherwise the user will never know (unless they use -v) what the error is.

See how we handle multiple plugin install failures in install.go.

klog.Warningf("failed to install plugin %q: %v", plugin.Name, err)

@@ -132,6 +145,9 @@ func ensureIndexUpdated(_ *cobra.Command, _ []string) error {
// TODO(chriskim06) consider commenting this out when refactoring for custom indexes
showUpdatedPlugins(os.Stderr, preUpdateIndex, posUpdateIndex, installedPlugins)

if len(failed) != 0 {
return errors.Errorf("failed to update the following indexes: %s\n", strings.Join(failed, ", "))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something else we do in install.go is that we collect the first error as returnErr and wrap+return it.

if returnErr == nil {
returnErr = err
}

if len(failed) > 0 {
return errors.Wrapf(returnErr, "failed to install some plugins: %+v", failed)
}

The reason for this is that when you wrap+return errors, when executed with -v 1 or above, we the retain ability to print full stack trace (in this case, at least for 1 error which is good enough).

Copy link
Contributor

@corneliusweig corneliusweig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you update with the comments by Ahmet, this looks pretty good to me.
Thanks for bringing the multi-index project one step further ahead!

defer cleanup()

test = test.WithEnv(constants.EnableMultiIndexSwitch, 1).WithIndex()
os.Setenv(constants.EnableMultiIndexSwitch, "1")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment like

// to enable new paths in environment.NewPaths()

Comment on lines 71 to 73
if len(out) == 0 {
t.Error("no plugins in index foo after update")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be enough to look for two plugin names in the output. For example, ctx and foo/ctx.

@@ -132,6 +149,9 @@ func ensureIndexUpdated(_ *cobra.Command, _ []string) error {
// TODO(chriskim06) consider commenting this out when refactoring for custom indexes
showUpdatedPlugins(os.Stderr, preUpdateIndex, posUpdateIndex, installedPlugins)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve realized this if (+counting) isnt necessary since errors.Wrap already handles nil err. Maybe remove?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I can do that. Do you think it will look weird if this ends with the return errors.Wrapf(...)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it’s fine. I just realized the check is redundant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll remove the check then.

t.Error("expected update to fail")
}
if !strings.Contains(string(out), "failed to update the following indexes: foo") {
t.Error("expected index update to fail for foo")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try doing this for debuggability:

%q doesn’t contain msg=%q

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the two strings in the example you gave? I'm guessing the output of krew update and foo but want to make sure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out & “failed to ...foo”

You can export latter into a variable.

@ahmetb
Copy link
Member

ahmetb commented Apr 13, 2020

/lgtm
/approve
thanks!

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 13, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ahmetb, chriskim06

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 13, 2020
@k8s-ci-robot k8s-ci-robot merged commit c99749f into kubernetes-sigs:master Apr 13, 2020
@chriskim06 chriskim06 deleted the index-updates branch June 11, 2020 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/multi-index cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants