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

(c-r v0.2.0) *: bump controller-runtime to v0.2.0, update APIs #1839

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
- CSV config field `role-path` is now `role-paths` and takes a list of strings. Users can now specify multiple `Role` and `ClusterRole` manifests using `role-paths`. ([#1704](https://github.com/operator-framework/operator-sdk/pull/1704))
- Upgrade Kubernetes version from `kubernetes-1.13.4` to `kubernetes-1.14.1`
- Upgrade `github.com/operator-framework/operator-lifecycle-manager` version from `b8a4faf68e36feb6d99a6aec623b405e587b17b1` to `0.10.1`
- Upgrade [`controller-runtime`](https://github.com/kubernetes-sigs/controller-runtime) version from `v0.1.12` to `v0.2.0-beta.3`
- Upgrade [`controller-runtime`](https://github.com/kubernetes-sigs/controller-runtime) version from `v0.1.12` to `v0.2.0`
- The package `sigs.k8s.io/controller-runtime/pkg/runtime/scheme` is deprecated, and contains no code. Replace this import with `sigs.k8s.io/controller-runtime/pkg/scheme` where relevant.
- The package `sigs.k8s.io/controller-runtime/pkg/runtime/log` is deprecated. Replace this import with `sigs.k8s.io/controller-runtime/pkg/log` where relevant.
- The package `sigs.k8s.io/controller-runtime/pkg/runtime/signals` is deprecated. Replace this import with `sigs.k8s.io/controller-runtime/pkg/manager/signals` where relevant.
- [`sigs.k8s.io/controller-runtime/pkg/client.Client`](https://github.com/kubernetes-sigs/controller-runtime/blob/aaddbd9d9a89d8ff329a084aece23be0406e6467/pkg/client/interfaces.go#L101)'s `List()` method signature has been updated: `List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error` is now [`List(ctx context.Context, list runtime.Object, opts ...client.ListOptionFunc) error`](https://github.com/kubernetes-sigs/controller-runtime/blob/aaddbd9d9a89d8ff329a084aece23be0406e6467/pkg/client/interfaces.go#L61). To migrate:
- All methods on [`sigs.k8s.io/controller-runtime/pkg/client.Client`](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.2.0/pkg/client/interfaces.go#L104) (except for `Get()`) have been updated. Instead of each using a `struct`-typed or variadic functional option parameter, or having no option parameter, each now uses a variadic interface option parameter typed for each method. See `List()` below for an example.
- [`sigs.k8s.io/controller-runtime/pkg/client.Client`](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.2.0/pkg/client/interfaces.go#L104)'s `List()` method signature has been updated: `List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error` is now [`List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error`](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.2.0/pkg/client/interfaces.go#L61). To migrate:
estroz marked this conversation as resolved.
Show resolved Hide resolved
```go
import (
"context"
Expand All @@ -30,12 +31,17 @@

...

listOpts := &client.ListOptions{}
// Old
listOpts := &client.ListOptions{}
listOpts.InNamespace("namespace")
err = r.client.List(context.TODO(), listOps, podList)
// New
err = r.client.List(context.TODO(), podList, client.UseListOptions(listOps))
listOpts := []client.ListOption{
client.InNamespace("namespace"),
}
err = r.client.List(context.TODO(), podList, listOpts...)
```
- [`pkg/test.FrameworkClient`](https://github.com/operator-framework/operator-sdk/blob/master/pkg/test/client.go#L33) methods `List()` and `Delete()` have new signatures corresponding to the homonymous methods of `sigs.k8s.io/controller-runtime/pkg/client.Client`.
- CRD file names were previously of the form `<group>_<version>_<kind>_crd.yaml`. Now that CRD manifest `spec.version` is deprecated in favor of `spec.versions`, i.e. multiple versions can be specified in one CRD, CRD file names have the form `<full group>_<resource>_crd.yaml`. `<full group>` is the full group name of your CRD while `<group>` is the last subdomain of `<full group>`, ex. `foo.bar.com` vs `foo`. `<resource>` is the plural lower-case CRD Kind found at `spec.names.plural`.

### Deprecated
Expand Down
208 changes: 139 additions & 69 deletions doc/user/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,32 +134,10 @@ func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, e
```Go
// List retrieves a list of objects for a given namespace and list options
// and stores the list in obj.
func (c Client) List(ctx context.Context, list runtime.Object, opts ...client.ListOptionFunc) error
func (c Client) List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error
```

A `client.ListOptionFunc` can be created either by using the provided [functional options](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#ListOptionFunc) or using `client.ListOptions`:

```Go
type ListOptions struct {
// LabelSelector filters results by label. Use SetLabelSelector to
// set from raw string form.
LabelSelector labels.Selector

// FieldSelector filters results by a particular field. In order
// to use this with cache-based implementations, restrict usage to
// a single field-value pair that's been added to the indexers.
FieldSelector fields.Selector

// Namespace represents the namespace to list for, or empty for
// non-namespaced objects, or to list across all namespaces.
Namespace string

// Raw represents raw ListOptions, as passed to the API server. Note
// that these may not be respected by all implementations of interface,
// and the LabelSelector and FieldSelector fields are ignored.
Raw *metav1.ListOptions
}
```
A `client.ListOption` is an interface that sets [`client.ListOptions`][list-options] fields. A `client.ListOption` is created by using one of the provided implementations: [`MatchingLabels`][matching-labels], [`MatchingFields`][matching-fields], [`InNamespace`][in-namespace].

Example:

Expand All @@ -176,26 +154,37 @@ func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, e
...

// Return all pods in the request namespace with a label of `app=<name>`
opts := &client.ListOptions{}
opts.SetLabelSelector(fmt.Sprintf("app=%s", request.NamespacedName.Name))
opts.InNamespace(request.NamespacedName.Namespace)

// and phase `Running`.
podList := &v1.PodList{}
opts := []client.ListOption{
client.InNamespace(request.NamespacedName.Namespace),
client.MatchingLabels{"app", request.NamespacedName.Name},
client.MatchingFields{"status.phase": "Running"},
}
ctx := context.TODO()
err := r.client.List(ctx, podList, client.UseListOptions(listOps))
err := r.client.List(ctx, podList, opts...)

...
}
```

[list-options]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#ListOptions
[matching-labels]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#MatchingLabels
[matching-fields]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#MatchingFields
[in-namespace]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#InNamespace

#### Create

```Go
// Create saves the object obj in the Kubernetes cluster.
// Returns an error
func (c Client) Create(ctx context.Context, obj runtime.Object) error
func (c Client) Create(ctx context.Context, obj runtime.Object, opts ...client.CreateOption) error
```

A `client.CreateOption` is an interface that sets [`client.CreateOptions`][create-options] fields. A `client.CreateOption` is created by using one of the provided implementations: [`DryRunAll`][dry-run-all], [`ForceOwnership`][force-ownership]. Generally these options are not needed.

Example:

```Go
import (
"context"
Expand All @@ -216,16 +205,22 @@ func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, e
}
```

[create-options]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#CreateOptions

#### Update

```Go
// Update updates the given obj in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned
// by the API server. Update does *not* update the resource's status
// subresource
func (c Client) Update(ctx context.Context, obj runtime.Object) error
func (c Client) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error
```

A `client.UpdateOption` is an interface that sets [`client.UpdateOptions`][update-options] fields. A `client.UpdateOption` is created by using one of the provided implementations: [`DryRunAll`][dry-run-all], [`ForceOwnership`][force-ownership]. Generally these options are not needed.

Example:

```Go
import (
"context"
Expand All @@ -249,10 +244,55 @@ func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, e
}
```

[update-options]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#UpdateOptions

#### Patch

```Go
// Patch patches the given obj in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned by the Server.
func (c Client) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.UpdateOption) error
```

A `client.PatchOption` is an interface that sets [`client.PatchOptions`][patch-options] fields. A `client.PatchOption` is created by using one of the provided implementations: [`DryRunAll`][dry-run-all], [`ForceOwnership`][force-ownership]. Generally these options are not needed.

Example:

```Go
import (
"context"
"k8s.io/api/apps/v1"
estroz marked this conversation as resolved.
Show resolved Hide resolved
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, error) {
...

dep := &v1.Deployment{}
err := r.client.Get(context.TODO(), request.NamespacedName, dep)

...

ctx := context.TODO()
dep.Spec.Selector.MatchLabels["is_running"] = "true"
// A merge patch will preserve other fields modified at runtime.
patch := client.MergeFrom(dep)
err := r.client.Patch(ctx, dep, patch)

...
}
```

[patch-options]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#PatchOption
[dry-run-all]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#DryRunAll
[force-ownership]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#ForceOwnership

##### Updating Status Subresource

When updating the [status subresource][cr-status-subresource] from the client,
the StatusWriter must be used which can be gotten with `Status()`
When updating the [status subresource][cr-status-subresource] from the client, the [`StatusWriter`][status-writer] must be used. The status subresource is retrieved with `Status()` and updated with `Update()` or patched with `Patch()`.

`Update()` takes variadic `client.UpdateOption`'s, and `Patch()` takes variadic `client.PatchOption`'s. See [`Client.Update()`](#update) and [`Client.Patch()`](#patch) for more details. Generally these options are not needed.

##### Status

Expand All @@ -273,57 +313,39 @@ import (
func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, error) {
...

ctx := context.TODO()
mem := &cachev1alpha1.Memcached{}
err := r.client.Get(context.TODO(), request.NamespacedName, mem)
err := r.client.Get(ctx, request.NamespacedName, mem)

...

ctx := context.TODO()
// Update
mem.Status.Nodes = []string{"pod1", "pod2"}
err := r.client.Status().Update(ctx, mem)

...

// Patch
patch := client.MergeFrom(mem)
err := r.client.Status().Patch(ctx, mem, patch)

...
}
```

[status-writer]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#StatusWriter

#### Delete

```Go
// Delete deletes the given obj from Kubernetes cluster.
func (c Client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error
```
A `client.DeleteOptionFunc` sets fields of `client.DeleteOptions` to configure a `Delete` call:
```Go
// DeleteOptionFunc is a function that mutates a DeleteOptions struct.
type DeleteOptionFunc func(*DeleteOptions)

type DeleteOptions struct {
// GracePeriodSeconds is the duration in seconds before the object should be
// deleted. Value must be non-negative integer. The value zero indicates
// delete immediately. If this value is nil, the default grace period for the
// specified type will be used.
GracePeriodSeconds *int64

// Preconditions must be fulfilled before a deletion is carried out. If not
// possible, a 409 Conflict status will be returned.
Preconditions *metav1.Preconditions

// PropagationPolicy determined whether and how garbage collection will be
// performed. Either this field or OrphanDependents may be set, but not both.
// The default policy is decided by the existing finalizer set in the
// metadata.finalizers and the resource-specific default policy.
// Acceptable values are: 'Orphan' - orphan the dependents; 'Background' -
// allow the garbage collector to delete the dependents in the background;
// 'Foreground' - a cascading policy that deletes all dependents in the
// foreground.
PropagationPolicy *metav1.DeletionPropagation

// Raw represents raw DeleteOptions, as passed to the API server.
Raw *metav1.DeleteOptions
}
func (c Client) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOption) error
```

A `client.DeleteOption` is an interface that sets [`client.DeleteOptions`][delete-opts] fields. A `client.DeleteOption` is created by using one of the provided implementations: [`GracePeriodSeconds`][grace-period-seconds], [`Preconditions`][preconditions], [`PropagationPolicy`][propagation-policy].

Example:

```Go
import (
"context"
Expand Down Expand Up @@ -351,6 +373,52 @@ func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, e
}
```

[delete-opts]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#DeleteOptions
[grace-period-seconds]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#GracePeriodSeconds
[preconditions]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#Preconditions
[propagation-policy]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#PropagationPolicy

#### DeleteAllOf

```Go
// DeleteAllOf deletes all objects of the given type matching the given options.
func (c Client) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...client.DeleteAllOfOption) error
```

A `client.DeleteAllOfOption` is an interface that sets [`client.DeleteAllOfOptions`][deleteallof-opts] fields. A `client.DeleteAllOfOption` wraps a [`client.ListOption`](#list) and [`client.DeleteOption`](#delete).

estroz marked this conversation as resolved.
Show resolved Hide resolved
Example:

```Go
import (
"context"
"fmt"
"k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, error) {
...

// Delete all pods in the request namespace with a label of `app=<name>`
// and phase `Failed`.
pod := &v1.Pod{}
opts := []client.DeleteAllOfOption{
client.InNamespace(request.NamespacedName.Namespace),
client.MatchingLabels{"app", request.NamespacedName.Name},
client.MatchingFields{"status.phase": "Failed"},
client.GracePeriodSeconds(5),
}
ctx := context.TODO()
err := r.client.DeleteAllOf(ctx, pod, opts...)

...
}
```

[deleteallof-opts]:https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client#DeleteAllOfOptions

### Example usage

```Go
Expand Down Expand Up @@ -418,9 +486,11 @@ func (r *ReconcileApp) Reconcile(request reconcile.Request) (reconcile.Result, e
// Update the App status with the pod names.
// List the pods for this app's deployment.
podList := &corev1.PodList{}
labelSelector := labels.SelectorFromSet(labelsForApp(app.Name))
listOps := &client.ListOptions{Namespace: app.Namespace, LabelSelector: labelSelector}
if err = r.client.List(context.TODO(), podList, client.UseListOptions(listOps)); err != nil {
listOpts := []client.ListOption{
client.InNamespace(app.Namespace),
client.MatchingLabels(labelsForApp(app.Name)),
}
if err = r.client.List(context.TODO(), podList, listOpts...); err != nil {
return reconcile.Result{}, err
}

Expand Down
10 changes: 5 additions & 5 deletions example/memcached-operator/memcached_controller.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -142,10 +141,11 @@ func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Res
// Update the Memcached status with the pod names
// List the pods for this memcached's deployment
podList := &corev1.PodList{}
labelSelector := labels.SelectorFromSet(labelsForMemcached(memcached.Name))
listOps := &client.ListOptions{Namespace: memcached.Namespace, LabelSelector: labelSelector}
err = r.client.List(context.TODO(), podList, client.UseListOptions(listOps))
if err != nil {
listOpts := []client.ListOption{
client.InNamespace(memcached.Namespace),
client.MatchingLabels(labelsForMemcached(memcached.Name)),
}
if err = r.client.List(context.TODO(), podList, listOpts...); err != nil {
reqLogger.Error(err, "Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name)
return reconcile.Result{}, err
}
Expand Down
Loading