Skip to content

Commit

Permalink
Move config into context (#209)
Browse files Browse the repository at this point in the history
Reconcilers and sub reconcilers use the config object to centralize
common api operations. Previously this config was passed to each
reconciler when it was initialized. However, this makes it harder to
dynamically swap the config at runtime based on the content of a
resource. For example, a resource may specify a service account to use
when creating other resources with least privilege.

The ChildReconciler and SyncReconciler no long allow the Config to be
specified directly. It must be passed in via the context. So that the
RetrieveConfig method can return the Config.

SubReconcilers that need to interact with the parent resource should use
the parent config via RetrieveParentConfig method. In many cases the
config and parent config are one in the same, however, the WithConfig
sub reconciler can inject an alternate config for the nested sub
reconcilers.

Signed-off-by: Scott Andrews <andrewssc@vmware.com>
  • Loading branch information
scothis committed Apr 21, 2022
1 parent d6e4c78 commit bb1d838
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 106 deletions.
39 changes: 28 additions & 11 deletions README.md
Expand Up @@ -16,6 +16,7 @@
- [Higher-order Reconcilers](#higher-order-reconcilers)
- [CastParent](#castparent)
- [Sequence](#sequence)
- [WithConfig](#withconfig)
- [Testing](#testing)
- [ReconcilerTestSuite](#reconcilertestsuite)
- [SubReconcilerTestSuite](#subreconcilertestsuite)
Expand Down Expand Up @@ -93,8 +94,6 @@ func FunctionTargetImageReconciler(c reconcilers.Config) reconcilers.SubReconcil
parent.Status.TargetImage = targetImage
return nil
},

Config: c,
}
}
```
Expand Down Expand Up @@ -192,8 +191,6 @@ func FunctionChildImageReconciler(c reconcilers.Config) reconcilers.SubReconcile
// up in our logs
return child.Spec
},

Config: c,
}
}
```
Expand Down Expand Up @@ -224,7 +221,6 @@ func FunctionReconciler(c reconcilers.Config) *reconcilers.ParentReconciler {
// do something with the duckv1alpha1.ImageRef instead of a buildv1alpha1.Function
return nil
},
Config: c,
},
},
FunctionChildImageReconciler(c),
Expand Down Expand Up @@ -259,6 +255,31 @@ func FunctionReconciler(c reconcilers.Config) *reconcilers.ParentReconciler {
```
[full source](https://github.com/projectriff/system/blob/4c3b75327bf99cc37b57ba14df4c65d21dc79d28/pkg/controllers/build/function_reconciler.go#L39-L51)

#### WithConfig

[`WithConfig`](https://pkg.go.dev/github.com/vmware-labs/reconciler-runtime/reconcilers#WithConfig) overrides the config that nested reconcilers consume. The config can be retrieved from the context via [`RetrieveConfig`](https://pkg.go.dev/github.com/vmware-labs/reconciler-runtime/reconcilers#RetrieveConfig). The config used to load the parent resource should be used for interactions with the parent resource, which can be retrieved from the context via [`RetrieveParentConfig`](https://pkg.go.dev/github.com/vmware-labs/reconciler-runtime/reconcilers#RetrieveParentConfig).

**Example:**

`WithConfig` can be used to change the REST Config backing the clients. This could be to make requests to the same cluster with a user defined service account, or target an entirely different Kubernetes cluster.

```go
func SwapRESTConfig(c reconciler.Config, rc *rest.Config) (*reconcilers.SubReconciler, error) {
cl, err := clusters.New(rc)
if err != nil {
return nil, err
}

return &reconcilers.WithConfig{
Reconciler: reconcilers.Sequence{
LookupReferenceDataReconciler(),
DoSomethingChildReconciler(),
},

Config: c.WithCluster(cl),
}, nil
}
```

## Testing

Expand Down Expand Up @@ -395,8 +416,6 @@ func StashExampleSubReconciler(c reconcilers.Config) reconcilers.SubReconciler {
reconcilers.StashValue(ctx, exampleStashKey, *value)
return nil
},

Config: c,
}
}

Expand All @@ -411,8 +430,6 @@ func StashExampleSubReconciler(c reconcilers.Config) reconcilers.SubReconciler {
}
... // do something with the value
},

Config: c,
}
}
```
Expand All @@ -431,6 +448,7 @@ func InMemoryGatewaySyncConfigReconciler(c reconcilers.Config, namespace string)
Name: "SyncConfig",
Sync: func(ctx context.Context, parent *streamingv1alpha1.InMemoryGateway) error {
log := logr.FromContextOrDiscard(ctx)
c := reconciler.RetrieveConfig(ctx)

var config corev1.ConfigMap
key := types.NamespacedName{Namespace: namespace, Name: inmemoryGatewayImages}
Expand All @@ -451,14 +469,13 @@ func InMemoryGatewaySyncConfigReconciler(c reconcilers.Config, namespace string)
return nil
},

Config: c,
Setup: func(ctx context.Context, mgr reconcilers.Manager, bldr *reconcilers.Builder) error {
// enqueue the tracking resource for reconciliation from changes to
// tracked ConfigMaps. Internally `EnqueueTracked` sets up an
// Informer to watch to changes of the target resource. When the
// informer emits an event, the tracking resources are looked up
// from the tracker and enqueded for reconciliation.
bldr.Watches(&source.Kind{Type: &corev1.ConfigMap{}}, reconcilers.EnqueueTracked(ctx, &corev1.ConfigMap{}, c.Tracker, c.Scheme))
bldr.Watches(&source.Kind{Type: &corev1.ConfigMap{}}, reconcilers.EnqueueTracked(ctx, &corev1.ConfigMap{}))
return nil
},
}
Expand Down
8 changes: 4 additions & 4 deletions reconcilers/enqueuer.go
Expand Up @@ -8,7 +8,6 @@ package reconcilers
import (
"context"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
Expand All @@ -17,12 +16,13 @@ import (
"github.com/vmware-labs/reconciler-runtime/tracker"
)

func EnqueueTracked(ctx context.Context, by client.Object, t tracker.Tracker, s *runtime.Scheme) handler.EventHandler {
func EnqueueTracked(ctx context.Context, by client.Object) handler.EventHandler {
c := RetrieveConfigOrDie(ctx)
return handler.EnqueueRequestsFromMapFunc(
func(a client.Object) []reconcile.Request {
var requests []reconcile.Request

gvks, _, err := s.ObjectKinds(by)
gvks, _, err := c.Scheme().ObjectKinds(by)
if err != nil {
panic(err)
}
Expand All @@ -31,7 +31,7 @@ func EnqueueTracked(ctx context.Context, by client.Object, t tracker.Tracker, s
gvks[0],
types.NamespacedName{Namespace: a.GetNamespace(), Name: a.GetName()},
)
for _, item := range t.Lookup(ctx, key) {
for _, item := range c.Tracker.Lookup(ctx, key) {
requests = append(requests, reconcile.Request{NamespacedName: item})
}

Expand Down
2 changes: 2 additions & 0 deletions reconcilers/patch.go
Expand Up @@ -62,5 +62,7 @@ func (p *Patch) Apply(rebase client.Object) error {
if err != nil {
return err
}
// reset rebase to its empty value before unmarshaling into it
replaceWithEmpty(rebase)
return json.Unmarshal(patchedBytes, rebase)
}

0 comments on commit bb1d838

Please sign in to comment.