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

Convert client.List to use functional options #106

Merged
merged 3 commits into from
Sep 17, 2018

Conversation

grantr
Copy link
Contributor

@grantr grantr commented Aug 8, 2018

Replaces:

List(ctx, ListOptions, list)

with:

List(ctx, list, ...Option)

This matches the functional options signature of client.Delete in #94.

A few examples (I tried formatting these as a table but it wasn't wide enough):

Select all:

// current
List(ctx, nil, list)
// proposed
List(ctx, list)

Filter by namespace:

// current
List(ctx, client.InNamespace(ns), list)
// proposed
List(ctx, list, client.InNamespace(ns))

Filter by namespace and labels:

// current
List(ctx, client.InNamespace(ns).MatchingLabels(labels), list)
// proposed
List(ctx, list, client.InNamespace(ns), client.MatchingLabels(labels))

To assemble a ListOptions with the builder methods or some other strategy, use the UseListOptions functional option:

lo := &client.ListOptions{}
client.List(ctx, list, client.UseListOptions(
  lo.InNamespace(ns).MatchingLabels(labels)
))

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Aug 8, 2018
@DirectXMan12
Copy link
Contributor

DirectXMan12 commented Aug 9, 2018

@pwittrock what's our policy on breaking backwards compat (or incrementing major versions)? This ends up with a much more ergonomic interface, and while I'm generally loathe to break the compatibility, I think it ends up being much less verbose, and follows the "you don't have to care about what you're not using" principle better.

@droot
Copy link
Contributor

droot commented Aug 15, 2018

@grantr new interface is nice UX wise, but can't get break the backward compatibility.

@DirectXMan12 We haven't defined the policy yet and have not been versioning the lib yet (so far, the unsaid rule is not to break the API :)). Are there any good library projects we can take inspiration from wrt versioning/backward-compatibility aspect ?

@grantr
Copy link
Contributor Author

grantr commented Aug 15, 2018

I think the backward compatibility policy dovetails with the dependency versioning story from #111. How much is controller-runtime a standalone library versus a private dependency of kubebuilder?

From my perspective (of course 😸) I'd like kubebuilder to be the semantically versioned umbrella, with each release pinning projects that use it to a specific revision of controller-runtime. Then controller-runtime can remain unversioned or have a different versioning scheme, with the freedom to move quickly knowing that kubebuilder users won't accidentally upgrade. When users install a new version of kubebuilder, an upgrade procedure can point out or automatically fix incompatibilities.

Anyone using controller-runtime directly is on their own fixing breaking changes, which is probably what they expect from an unversioned library. Maybe a release notes file in the repo could have a list of breaking changes to guide them.

In lieu of the above, what about this until the next major "release" (in the semantic versioning sense):

  • Keep the existing List, but mark it deprecated, noting that it will be removed at next major release.
  • Rename this List to ListVariadic (or some other, better name). Encourage users to start using it, and note that it will be renamed to List at next major release.

That gives users a way to proactively migrate, and a clean upgrade story for those who did (s/ListVariadic/List/).

@DirectXMan12
Copy link
Contributor

@grantr I we're aiming to set up a version policy (semver) and have a story mostly ironed out for kubebuilder versioning, so we should be able to tag first semver release, and then hypothetically make a breaking change, and then do a new major version.

@droot a lot of them that I know do one of a few things:

a) add in alternate names, like suggested by @grantr. This tends to result in a confusing mess if those are left to languish for too long (or sometimes even if they're not), from what I've seen.
b) do a number of rapid "major" version revs pre-1.0, and then tag 1.0.0 and move to step c
c) "save up" major changes until you hit critical mass for a major version change, then do a large major revision

Of course, we'll want to avoid breaking changes in general, but I think we'll have a short period where we'll want to make one or two and do a few version revs, simply due to the nature of actually getting user feedback (i.e. option b above).

As for ListVariadic, since we're still early on, we may just want to do a new major version quickly, after releasing an initial stability version, and avoid confusion from that. WDYT @droot ?

@droot
Copy link
Contributor

droot commented Aug 21, 2018

How much is controller-runtime a standalone library versus a private dependency of kubebuilder?

Goal is to have controller-runtime a standalone library following proper semver style versioning scheme so that Kubebuilder generated projects are reliably depend on it. As @DirectXMan12 pointed out, we will soon move to the new world to achieve that goal because we were figuring out the dependency story for kubebuilder generated projects. We sort of have a proposal which will work.

From my perspective (of course 😸) I'd like kubebuilder to be the semantically versioned umbrella, with each release pinning projects that use it to a specific revision of controller-runtime. Then controller-runtime can remain unversioned or have a different versioning scheme, with the freedom to move quickly knowing that kubebuilder users won't accidentally upgrade. When users install a new version of kubebuilder, an upgrade procedure can point out or automatically fix incompatibilities.

Instead of pinning to specific revision (I am assuming you referring to SHA), we can pin the controller-runtime to a minor version (0.1.x say) in Kubebuilder projects so that we don't have to do Kubebuilder release for every patch version on controller-runtime.

In lieu of the above, what about this until the next major "release" (in the semantic versioning sense):
Keep the existing List, but mark it deprecated, noting that it will be removed at next major release.
Rename this List to ListVariadic (or some other, better name). Encourage users to start using it, and note that it will be renamed to List at next major release.
That gives users a way to proactively migrate, and a clean upgrade story for those who did (s/ListVariadic/List/).

Sounds reasonable.

@DirectXMan12 will respond to your post soon.

@droot
Copy link
Contributor

droot commented Sep 4, 2018

@grantr we have sorted out the versioning story for controller-runtime/tools and Kubebuilder now uses versioned pkgs of controller-*, so we can move forward with these changes now.

@DirectXMan12
Copy link
Contributor

looks like it just needs a rebase to fix merge conflicts

@grantr
Copy link
Contributor Author

grantr commented Sep 5, 2018

👍 Hoping to get back to this tomorrow!

@grantr
Copy link
Contributor Author

grantr commented Sep 6, 2018

Rebased.

@DirectXMan12
Copy link
Contributor

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added lgtm "Looks good to me", indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Sep 6, 2018
@grantr
Copy link
Contributor Author

grantr commented Sep 6, 2018

There's an issue with the fake client and this new style.

Currently the fake client's List implementation relies on the provided ListOptions having a TypeMeta in its Raw field. See https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/client/fake/client.go#L81 and here for an example of the required workaround in user code.

This PR removes the ability of the user to provide a Raw field, and thus breaks the fake client's List implementation.

Is there a way to infer the GVK of the resource wrapped by a List object?

@DirectXMan12
Copy link
Contributor

/hold
while we figure that out (although the unit test failure should prevent it anyway)

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Sep 6, 2018
@DirectXMan12
Copy link
Contributor

Is there a way to infer the GVK of the resource wrapped by a List object?

Yeah, you should be able to do similar logic to what we actually do in the client -- the Scheme has logic for converting a runtime.Object to a GVK, and then the Kind can be truncated (remove the List) to find the underlying Kind.

@DirectXMan12
Copy link
Contributor

This PR removes the ability of the user to provide a Raw field

Huh? Can you just call UseListOptions(somethingWithRawSet)?

@grantr
Copy link
Contributor Author

grantr commented Sep 6, 2018

Oh true, that is possible. In practice that would be required for every call to List from (well-tested) user code, which kind of defeats the purpose of this change. I'd still like to fix the fake client before merging, or if someone else wants to try that's fine too.

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed 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. labels Sep 6, 2018
@grantr
Copy link
Contributor Author

grantr commented Sep 6, 2018

I updated the fake client to no longer need Raw.TypeMeta, updated all the implementations of List with the new signature, and updated tests to use the new signature. There's one failing test in certwriter that I haven't figured out yet, but I thought I'd push the new work for review anyway.

@grantr
Copy link
Contributor Author

grantr commented Sep 7, 2018

Rebased to squash the fixup commits.

func (c *fakeClient) List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error {
gvk := opts.Raw.TypeMeta.GroupVersionKind()
func (c *fakeClient) List(ctx context.Context, obj runtime.Object, opts ...client.ListOptionFunc) error {
gvk, err := apiutil.GVKForObject(obj, scheme.Scheme)
Copy link
Contributor

Choose a reason for hiding this comment

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

it should be possible to set the scheme for the fake client, otherwise the fake client might not work for some user resources. We should use this scheme if none is explicitly set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, though the fake client already restricts itself to scheme.Scheme in its constructor

tracker := testing.NewObjectTracker(scheme.Scheme, scheme.Codecs.UniversalDecoder())

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, ok. Let's do that in a follow-up PR then. Can you file an issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#137. The workaround for now is to add custom types to scheme.Scheme in an init function in the test file. See https://github.com/knative/eventing/blob/e6fb7df8f1b83b6e78f6639f7f7461ee75a9b196/pkg/controller/feed/reconcile_test.go.

Copy link
Contributor

Choose a reason for hiding this comment

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

thanks!

@grantr
Copy link
Contributor Author

grantr commented Sep 14, 2018

Oh right, Prow isn't used here so /retest doesn't work. I don't appear to have permission to try another build - can someone else do that?

Locally I'm consistently getting a different error related to the event recorder:

Timeout [1.003 seconds]
recorder
/go/src/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder_integration_test.go:36
  recorder
  /go/src/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder_integration_test.go:48
    should publish events [It]
    /go/src/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder_integration_test.go:49

    Timed out

    /go/src/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder_integration_test.go:49

@DirectXMan12
Copy link
Contributor

@droot @pwittrock we should switch to prow when we get a chance.

@DirectXMan12
Copy link
Contributor

I restarted the test. Let's see if it works, and file a bug about the flakes. Don't want to have any of those.

@DirectXMan12
Copy link
Contributor

/lgtm
/approve

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

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: DirectXMan12, grantr

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

@grantr
Copy link
Contributor Author

grantr commented Sep 14, 2018

/hold cancel
(hold was added to block on versioning strategy which is done now)

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Sep 14, 2018
Replaces:

List(ctx, ListOptions, list)

with:

List(ctx, list, ...option)

This matches the upcoming functional options signature of client.Delete.

To use the previous builder options, use the UseListOptions functional
option:

lo := &client.ListOptions{}
client.List(ctx, list, client.UseListOptions(
  lo.InNamespace(ns).MatchingLabels(labels)
))
The fake client now uses similar GVK detection code as the other
implementations and no longer relies on the Raw object having a
TypeMeta.
Now that the fake client no longer needs Raw, these tests can use typed
instead of untyped List objects.
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Sep 14, 2018
@grantr
Copy link
Contributor Author

grantr commented Sep 14, 2018

Hopefully the final rebase!

@grantr
Copy link
Contributor Author

grantr commented Sep 14, 2018

@DirectXMan12 need another lgtm after rebase, please :)

@DirectXMan12
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Sep 17, 2018
@k8s-ci-robot k8s-ci-robot merged commit 28ba1f2 into kubernetes-sigs:master Sep 17, 2018
@grantr grantr deleted the list-options branch September 17, 2018 17:01
@mengqiy
Copy link
Member

mengqiy commented Sep 17, 2018

Had a offline discussion with @droot, we probably should have something like this to not break existing users.

type Reader interface {
	Get(ctx context.Context, key ObjectKey, obj runtime.Object) error

	// Deprecated
	// This is the old signature.
	List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error

	// This is the new signature. We may want to have a better name.
	ListV2(ctx context.Context, obj runtime.Object, opts ...ListOptionFunc) error 
}

justinsb pushed a commit to justinsb/controller-runtime that referenced this pull request Dec 7, 2018
Convert client.List to use functional options
droot added a commit to droot/controller-runtime that referenced this pull request Jan 15, 2019
Replaces:

List(ctx, ListOptions, list)

with:

List(ctx, list, ...option)

This matches the upcoming functional options signature of client.Delete.

To use the previous builder options, use the UseListOptions functional
option:

lo := &client.ListOptions{}
client.List(ctx, list, client.UseListOptions(
  lo.InNamespace(ns).MatchingLabels(labels)
  ))

Ported kubernetes-sigs#106 PR
droot added a commit to droot/controller-runtime that referenced this pull request Jan 15, 2019
Replaces:

List(ctx, ListOptions, list)

with:

List(ctx, list, ...option)

This matches the upcoming functional options signature of client.Delete.

To use the previous builder options, use the UseListOptions functional
option:

lo := &client.ListOptions{}
client.List(ctx, list, client.UseListOptions(
  lo.InNamespace(ns).MatchingLabels(labels)
  ))

Ported kubernetes-sigs#106 PR
droot added a commit to droot/controller-runtime that referenced this pull request Jan 16, 2019
Replaces:

List(ctx, ListOptions, list)

with:

List(ctx, list, ...option)

This matches the upcoming functional options signature of client.Delete.

To use the previous builder options, use the UseListOptions functional
option:

lo := &client.ListOptions{}
client.List(ctx, list, client.UseListOptions(
  lo.InNamespace(ns).MatchingLabels(labels)
  ))

Ported kubernetes-sigs#106 PR
DirectXMan12 pushed a commit that referenced this pull request Jan 31, 2020
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. 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/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants