Skip to content

Commit 5ed7ca8

Browse files
committed
Separate PGAdminReconciler client concerns
We want to be mindful of our interactions with the Kubernetes API, and these interfaces will help keep functions focused. These interfaces are also narrower than client.Reader and client.Writer and may help us keep RBAC markers accurate. A new constructor populates these fields with a single client.Client. The client.WithFieldOwner constructor allows us to drop our Owner field and patch method.
1 parent fac1a15 commit 5ed7ca8

File tree

12 files changed

+72
-92
lines changed

12 files changed

+72
-92
lines changed

cmd/postgres-operator/main.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ func main() {
250250

251251
// add all PostgreSQL Operator controllers to the runtime manager
252252
addControllersToManager(manager, log, registrar)
253+
must(standalone_pgadmin.ManagedReconciler(manager))
253254

254255
if features.Enabled(feature.BridgeIdentifiers) {
255256
url := os.Getenv("PGO_BRIDGE_URL")
@@ -329,17 +330,6 @@ func addControllersToManager(mgr runtime.Manager, log logging.Logger, reg regist
329330
os.Exit(1)
330331
}
331332

332-
pgAdminReconciler := &standalone_pgadmin.PGAdminReconciler{
333-
Client: mgr.GetClient(),
334-
Owner: naming.ControllerPGAdmin,
335-
Recorder: mgr.GetEventRecorderFor(naming.ControllerPGAdmin),
336-
}
337-
338-
if err := pgAdminReconciler.SetupWithManager(mgr); err != nil {
339-
log.Error(err, "unable to create PGAdmin controller")
340-
os.Exit(1)
341-
}
342-
343333
constructor := func() bridge.ClientInterface {
344334
client := bridge.NewClient(os.Getenv("PGO_BRIDGE_URL"), versionString)
345335
client.Transport = otelTransportWrapper()(http.DefaultTransport)

internal/controller/standalone_pgadmin/apply.go

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,9 @@ import (
1111
"sigs.k8s.io/controller-runtime/pkg/client"
1212
)
1313

14-
// patch sends patch to object's endpoint in the Kubernetes API and updates
15-
// object with any returned content. The fieldManager is set to r.Owner, but
16-
// can be overridden in options.
17-
// - https://docs.k8s.io/reference/using-api/server-side-apply/#managers
18-
//
19-
// TODO(tjmoore4): This function is duplicated from a version that takes a PostgresCluster object.
20-
func (r *PGAdminReconciler) patch(
21-
ctx context.Context, object client.Object,
22-
patch client.Patch, options ...client.PatchOption,
23-
) error {
24-
options = append([]client.PatchOption{r.Owner}, options...)
25-
return r.Patch(ctx, object, patch, options...)
26-
}
27-
2814
// apply sends an apply patch to object's endpoint in the Kubernetes API and
29-
// updates object with any returned content. The fieldManager is set to
30-
// r.Owner and the force parameter is true.
15+
// updates object with any returned content. The fieldManager is set by
16+
// r.Writer and the force parameter is true.
3117
// - https://docs.k8s.io/reference/using-api/server-side-apply/#managers
3218
// - https://docs.k8s.io/reference/using-api/server-side-apply/#conflicts
3319
//
@@ -40,7 +26,7 @@ func (r *PGAdminReconciler) apply(ctx context.Context, object client.Object) err
4026

4127
// Send the apply-patch with force=true.
4228
if err == nil {
43-
err = r.patch(ctx, object, apply, client.ForceOwnership)
29+
err = r.Writer.Patch(ctx, object, apply, client.ForceOwnership)
4430
}
4531

4632
return err

internal/controller/standalone_pgadmin/controller.go

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package standalone_pgadmin
66

77
import (
88
"context"
9+
"errors"
910
"io"
1011

1112
appsv1 "k8s.io/api/apps/v1"
@@ -20,18 +21,30 @@ import (
2021

2122
"github.com/crunchydata/postgres-operator/internal/controller/runtime"
2223
"github.com/crunchydata/postgres-operator/internal/logging"
24+
"github.com/crunchydata/postgres-operator/internal/naming"
2325
"github.com/crunchydata/postgres-operator/internal/tracing"
2426
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
2527
)
2628

2729
// PGAdminReconciler reconciles a PGAdmin object
2830
type PGAdminReconciler struct {
29-
client.Client
30-
Owner client.FieldOwner
3131
PodExec func(
3232
ctx context.Context, namespace, pod, container string,
3333
stdin io.Reader, stdout, stderr io.Writer, command ...string,
3434
) error
35+
36+
Reader interface {
37+
Get(context.Context, client.ObjectKey, client.Object, ...client.GetOption) error
38+
List(context.Context, client.ObjectList, ...client.ListOption) error
39+
}
40+
Writer interface {
41+
Delete(context.Context, client.Object, ...client.DeleteOption) error
42+
Patch(context.Context, client.Object, client.Patch, ...client.PatchOption) error
43+
}
44+
StatusWriter interface {
45+
Patch(context.Context, client.Object, client.Patch, ...client.SubResourcePatchOption) error
46+
}
47+
3548
Recorder record.EventRecorder
3649
}
3750

@@ -42,19 +55,21 @@ type PGAdminReconciler struct {
4255
//+kubebuilder:rbac:groups="",resources="configmaps",verbs={list,watch}
4356
//+kubebuilder:rbac:groups="apps",resources="statefulsets",verbs={list,watch}
4457

45-
// SetupWithManager sets up the controller with the Manager.
46-
//
47-
// TODO(tjmoore4): This function is duplicated from a version that takes a PostgresCluster object.
48-
func (r *PGAdminReconciler) SetupWithManager(mgr ctrl.Manager) error {
49-
if r.PodExec == nil {
50-
var err error
51-
r.PodExec, err = runtime.NewPodExecutor(mgr.GetConfig())
52-
if err != nil {
53-
return err
54-
}
58+
// ManagedReconciler creates a [PGAdminReconciler] and adds it to m.
59+
func ManagedReconciler(m ctrl.Manager) error {
60+
exec, err := runtime.NewPodExecutor(m.GetConfig())
61+
kubernetes := client.WithFieldOwner(m.GetClient(), naming.ControllerPGAdmin)
62+
recorder := m.GetEventRecorderFor(naming.ControllerPGAdmin)
63+
64+
reconciler := &PGAdminReconciler{
65+
PodExec: exec,
66+
Reader: kubernetes,
67+
Recorder: recorder,
68+
StatusWriter: kubernetes.Status(),
69+
Writer: kubernetes,
5570
}
5671

57-
return ctrl.NewControllerManagedBy(mgr).
72+
return errors.Join(err, ctrl.NewControllerManagedBy(m).
5873
For(&v1beta1.PGAdmin{}).
5974
Owns(&corev1.ConfigMap{}).
6075
Owns(&corev1.PersistentVolumeClaim{}).
@@ -64,16 +79,16 @@ func (r *PGAdminReconciler) SetupWithManager(mgr ctrl.Manager) error {
6479
Watches(
6580
v1beta1.NewPostgresCluster(),
6681
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, cluster client.Object) []ctrl.Request {
67-
return runtime.Requests(r.findPGAdminsForPostgresCluster(ctx, cluster)...)
82+
return runtime.Requests(reconciler.findPGAdminsForPostgresCluster(ctx, cluster)...)
6883
}),
6984
).
7085
Watches(
7186
&corev1.Secret{},
7287
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, secret client.Object) []ctrl.Request {
73-
return runtime.Requests(r.findPGAdminsForSecret(ctx, client.ObjectKeyFromObject(secret))...)
88+
return runtime.Requests(reconciler.findPGAdminsForSecret(ctx, client.ObjectKeyFromObject(secret))...)
7489
}),
7590
).
76-
Complete(r)
91+
Complete(reconciler))
7792
}
7893

7994
//+kubebuilder:rbac:groups="postgres-operator.crunchydata.com",resources="pgadmins",verbs={get}
@@ -89,7 +104,7 @@ func (r *PGAdminReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
89104
defer span.End()
90105

91106
pgAdmin := &v1beta1.PGAdmin{}
92-
if err := r.Get(ctx, req.NamespacedName, pgAdmin); err != nil {
107+
if err := r.Reader.Get(ctx, req.NamespacedName, pgAdmin); err != nil {
93108
// NotFound cannot be fixed by requeuing so ignore it. During background
94109
// deletion, we receive delete events from pgadmin's dependents after
95110
// pgadmin is deleted.
@@ -100,7 +115,7 @@ func (r *PGAdminReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
100115
before := pgAdmin.DeepCopy()
101116
defer func() {
102117
if !equality.Semantic.DeepEqual(before.Status, pgAdmin.Status) {
103-
statusErr := r.Status().Patch(ctx, pgAdmin, client.MergeFrom(before), r.Owner)
118+
statusErr := r.StatusWriter.Patch(ctx, pgAdmin, client.MergeFrom(before))
104119
if statusErr != nil {
105120
log.Error(statusErr, "Patching PGAdmin status")
106121
}
@@ -166,7 +181,7 @@ func (r *PGAdminReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
166181
func (r *PGAdminReconciler) setControllerReference(
167182
owner *v1beta1.PGAdmin, controlled client.Object,
168183
) error {
169-
return controllerutil.SetControllerReference(owner, controlled, r.Scheme())
184+
return controllerutil.SetControllerReference(owner, controlled, runtime.Scheme)
170185
}
171186

172187
// deleteControlled safely deletes object when it is controlled by pgAdmin.
@@ -178,7 +193,7 @@ func (r *PGAdminReconciler) deleteControlled(
178193
version := object.GetResourceVersion()
179194
exactly := client.Preconditions{UID: &uid, ResourceVersion: &version}
180195

181-
return r.Delete(ctx, object, exactly)
196+
return r.Writer.Delete(ctx, object, exactly)
182197
}
183198

184199
return nil

internal/controller/standalone_pgadmin/controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestDeleteControlled(t *testing.T) {
2424
require.ParallelCapacity(t, 1)
2525

2626
ns := setupNamespace(t, cc)
27-
reconciler := PGAdminReconciler{Client: cc}
27+
reconciler := PGAdminReconciler{Writer: cc}
2828

2929
pgadmin := new(v1beta1.PGAdmin)
3030
pgadmin.Namespace = ns.Name

internal/controller/standalone_pgadmin/related.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (r *PGAdminReconciler) findPGAdminsForPostgresCluster(
3030
// namespace, we can configure the [manager.Manager] field indexer and pass a
3131
// [fields.Selector] here.
3232
// - https://book.kubebuilder.io/reference/watching-resources/externally-managed.html
33-
if r.List(ctx, &pgadmins, &client.ListOptions{
33+
if r.Reader.List(ctx, &pgadmins, &client.ListOptions{
3434
Namespace: cluster.GetNamespace(),
3535
}) == nil {
3636
for i := range pgadmins.Items {
@@ -64,7 +64,7 @@ func (r *PGAdminReconciler) findPGAdminsForSecret(
6464
// namespace, we can configure the [manager.Manager] field indexer and pass a
6565
// [fields.Selector] here.
6666
// - https://book.kubebuilder.io/reference/watching-resources/externally-managed.html
67-
if err := r.List(ctx, &pgadmins, &client.ListOptions{
67+
if err := r.Reader.List(ctx, &pgadmins, &client.ListOptions{
6868
Namespace: secret.Namespace,
6969
}); err == nil {
7070
for i := range pgadmins.Items {
@@ -93,7 +93,7 @@ func (r *PGAdminReconciler) getClustersForPGAdmin(
9393
for _, serverGroup := range pgAdmin.Spec.ServerGroups {
9494
var cluster v1beta1.PostgresCluster
9595
if serverGroup.PostgresClusterName != "" {
96-
err = r.Get(ctx, client.ObjectKey{
96+
err = r.Reader.Get(ctx, client.ObjectKey{
9797
Name: serverGroup.PostgresClusterName,
9898
Namespace: pgAdmin.GetNamespace(),
9999
}, &cluster)
@@ -104,7 +104,7 @@ func (r *PGAdminReconciler) getClustersForPGAdmin(
104104
}
105105
if selector, err = naming.AsSelector(serverGroup.PostgresClusterSelector); err == nil {
106106
var list v1beta1.PostgresClusterList
107-
err = r.List(ctx, &list,
107+
err = r.Reader.List(ctx, &list,
108108
client.InNamespace(pgAdmin.Namespace),
109109
client.MatchingLabelsSelector{Selector: selector},
110110
)

internal/controller/standalone_pgadmin/related_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func TestFindPGAdminsForSecret(t *testing.T) {
2222
require.ParallelCapacity(t, 0)
2323

2424
ns := setupNamespace(t, tClient)
25-
reconciler := &PGAdminReconciler{Client: tClient}
25+
reconciler := &PGAdminReconciler{Reader: tClient}
2626

2727
secret1 := &corev1.Secret{}
2828
secret1.Namespace = ns.Name

internal/controller/standalone_pgadmin/service.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (r *PGAdminReconciler) reconcilePGAdminService(
3636
// need to delete any existing service(s). At the start of every reconcile
3737
// get all services that match the current pgAdmin labels.
3838
services := corev1.ServiceList{}
39-
if err := r.List(ctx, &services,
39+
if err := r.Reader.List(ctx, &services,
4040
client.InNamespace(pgadmin.Namespace),
4141
client.MatchingLabels{
4242
naming.LabelStandalonePGAdmin: pgadmin.Name,
@@ -62,7 +62,7 @@ func (r *PGAdminReconciler) reconcilePGAdminService(
6262
if pgadmin.Spec.ServiceName != "" {
6363
// Look for an existing service with name ServiceName in the namespace
6464
existingService := &corev1.Service{}
65-
err := r.Get(ctx, types.NamespacedName{
65+
err := r.Reader.Get(ctx, types.NamespacedName{
6666
Name: pgadmin.Spec.ServiceName,
6767
Namespace: pgadmin.GetNamespace(),
6868
}, existingService)

internal/controller/standalone_pgadmin/statefulset.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (r *PGAdminReconciler) reconcilePGAdminStatefulSet(
3434
// When we delete the StatefulSet, we will leave its Pods in place. They will be claimed by
3535
// the StatefulSet that gets created in the next reconcile.
3636
existing := &appsv1.StatefulSet{}
37-
if err := errors.WithStack(r.Get(ctx, client.ObjectKeyFromObject(sts), existing)); err != nil {
37+
if err := errors.WithStack(r.Reader.Get(ctx, client.ObjectKeyFromObject(sts), existing)); err != nil {
3838
if !apierrors.IsNotFound(err) {
3939
return err
4040
}
@@ -47,7 +47,7 @@ func (r *PGAdminReconciler) reconcilePGAdminStatefulSet(
4747
exactly := client.Preconditions{UID: &uid, ResourceVersion: &version}
4848
propagate := client.PropagationPolicy(metav1.DeletePropagationOrphan)
4949

50-
return errors.WithStack(client.IgnoreNotFound(r.Delete(ctx, existing, exactly, propagate)))
50+
return errors.WithStack(client.IgnoreNotFound(r.Writer.Delete(ctx, existing, exactly, propagate)))
5151
}
5252
}
5353

internal/controller/standalone_pgadmin/statefulset_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ func TestReconcilePGAdminStatefulSet(t *testing.T) {
2929
require.ParallelCapacity(t, 1)
3030

3131
reconciler := &PGAdminReconciler{
32-
Client: cc,
33-
Owner: client.FieldOwner(t.Name()),
32+
Reader: cc,
33+
Writer: client.WithFieldOwner(cc, t.Name()),
3434
}
3535

3636
ns := setupNamespace(t, cc)

internal/controller/standalone_pgadmin/users.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (r *PGAdminReconciler) reconcilePGAdminUsers(ctx context.Context, pgadmin *
5353
pod := &corev1.Pod{ObjectMeta: naming.StandalonePGAdmin(pgadmin)}
5454
pod.Name += "-0"
5555

56-
err := errors.WithStack(r.Get(ctx, client.ObjectKeyFromObject(pod), pod))
56+
err := errors.WithStack(r.Reader.Get(ctx, client.ObjectKeyFromObject(pod), pod))
5757
if err != nil {
5858
return client.IgnoreNotFound(err)
5959
}
@@ -136,7 +136,7 @@ func (r *PGAdminReconciler) writePGAdminUsers(ctx context.Context, pgadmin *v1be
136136

137137
existingUserSecret := &corev1.Secret{ObjectMeta: naming.StandalonePGAdmin(pgadmin)}
138138
err := errors.WithStack(
139-
r.Get(ctx, client.ObjectKeyFromObject(existingUserSecret), existingUserSecret))
139+
r.Reader.Get(ctx, client.ObjectKeyFromObject(existingUserSecret), existingUserSecret))
140140
if client.IgnoreNotFound(err) != nil {
141141
return err
142142
}
@@ -186,7 +186,7 @@ cd $PGADMIN_DIR
186186
Name: user.PasswordRef.Name,
187187
}}
188188
err := errors.WithStack(
189-
r.Get(ctx, client.ObjectKeyFromObject(userPasswordSecret), userPasswordSecret))
189+
r.Reader.Get(ctx, client.ObjectKeyFromObject(userPasswordSecret), userPasswordSecret))
190190
if err != nil {
191191
log.Error(err, "Could not get user password secret")
192192
continue

0 commit comments

Comments
 (0)