Skip to content

Commit 53d0500

Browse files
committed
improve controllers
On-behalf-of: @SAP christoph.mewes@sap.com
1 parent cb3ed23 commit 53d0500

File tree

4 files changed

+79
-30
lines changed

4 files changed

+79
-30
lines changed

internal/controller/apiexport/controller.go

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626

2727
"github.com/kcp-dev/api-syncagent/internal/controllerutil"
2828
predicateutil "github.com/kcp-dev/api-syncagent/internal/controllerutil/predicate"
29+
"github.com/kcp-dev/api-syncagent/internal/discovery"
30+
"github.com/kcp-dev/api-syncagent/internal/kcp"
2931
"github.com/kcp-dev/api-syncagent/internal/projection"
3032
"github.com/kcp-dev/api-syncagent/internal/resources/reconciling"
3133
"github.com/kcp-dev/api-syncagent/internal/validation"
@@ -53,14 +55,15 @@ const (
5355
)
5456

5557
type Reconciler struct {
56-
localClient ctrlruntimeclient.Client
57-
kcpClient ctrlruntimeclient.Client
58-
log *zap.SugaredLogger
59-
recorder record.EventRecorder
60-
lcName logicalcluster.Name
61-
apiExportName string
62-
agentName string
63-
prFilter labels.Selector
58+
localClient ctrlruntimeclient.Client
59+
kcpClient ctrlruntimeclient.Client
60+
discoveryClient *discovery.Client
61+
log *zap.SugaredLogger
62+
recorder record.EventRecorder
63+
lcName logicalcluster.Name
64+
apiExportName string
65+
agentName string
66+
prFilter labels.Selector
6467
}
6568

6669
// Add creates a new controller and adds it to the given manager.
@@ -73,15 +76,21 @@ func Add(
7376
agentName string,
7477
prFilter labels.Selector,
7578
) error {
79+
discoveryClient, err := discovery.NewClient(mgr.GetConfig())
80+
if err != nil {
81+
return fmt.Errorf("failed to create discovery client: %w", err)
82+
}
83+
7684
reconciler := &Reconciler{
77-
localClient: mgr.GetClient(),
78-
kcpClient: kcpCluster.GetClient(),
79-
lcName: lcName,
80-
log: log.Named(ControllerName),
81-
recorder: kcpCluster.GetEventRecorderFor(ControllerName),
82-
apiExportName: apiExportName,
83-
agentName: agentName,
84-
prFilter: prFilter,
85+
localClient: mgr.GetClient(),
86+
kcpClient: kcpCluster.GetClient(),
87+
discoveryClient: discoveryClient,
88+
lcName: lcName,
89+
log: log.Named(ControllerName),
90+
recorder: kcpCluster.GetEventRecorderFor(ControllerName),
91+
apiExportName: apiExportName,
92+
agentName: agentName,
93+
prFilter: prFilter,
8594
}
8695

8796
hasARS := predicate.NewPredicateFuncs(func(object ctrlruntimeclient.Object) bool {
@@ -93,7 +102,7 @@ func Add(
93102
return publishedResource.Status.ResourceSchemaName != ""
94103
})
95104

96-
_, err := builder.ControllerManagedBy(mgr).
105+
_, err = builder.ControllerManagedBy(mgr).
97106
Named(ControllerName).
98107
WithOptions(controller.Options{
99108
// we reconcile a single object in kcp, no need for parallel workers
@@ -151,7 +160,12 @@ func (r *Reconciler) reconcile(ctx context.Context, apiExport *kcpdevv1alpha1.AP
151160
// for each PR, we note down the created ARS and also the GVKs of related resources
152161
newARSList := sets.New[string]()
153162
for _, pubResource := range filteredPubResources {
154-
newARSList.Insert(pubResource.Status.ResourceSchemaName)
163+
schemaName, err := r.getSchemaName(ctx, &pubResource)
164+
if err != nil {
165+
return fmt.Errorf("failed to determine schema name for PublishedResource %s: %w", pubResource.Name, err)
166+
}
167+
168+
newARSList.Insert(schemaName)
155169
}
156170

157171
// To determine if the GVR of a related resource needs to be listed as a permission claim,
@@ -259,3 +273,22 @@ func (r *Reconciler) reconcile(ctx context.Context, apiExport *kcpdevv1alpha1.AP
259273

260274
return nil
261275
}
276+
277+
func (r *Reconciler) getSchemaName(ctx context.Context, pubRes *syncagentv1alpha1.PublishedResource) (string, error) {
278+
if pubRes.Status.ResourceSchemaName != "" {
279+
return pubRes.Status.ResourceSchemaName, nil
280+
}
281+
282+
// Technically we *could* wait and let the apiresourceschema controller do its
283+
// job and provide the status field above. But this would mean we potentially
284+
// temporarily misidentify related resources, adding unnecessary and invalid
285+
// permission claims in the APIExport. To avoid these it's worth it to basically
286+
// do the same projection logic as the other controller here, just to calculate
287+
// what the name of the schema would/will be.
288+
projectedCRD, err := projection.ProjectPublishedResource(ctx, r.discoveryClient, pubRes)
289+
if err != nil {
290+
return "", fmt.Errorf("failed to apply projection rules: %w", err)
291+
}
292+
293+
return kcp.GetAPIResourceSchemaName(projectedCRD), nil
294+
}

internal/controller/apiexport/reconciler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func parseSchemaName(name string) (schema.GroupVersionResource, error) {
191191
// <version>.<resource>.<group>
192192
parts := strings.SplitN(name, ".", 3)
193193
if len(parts) != 3 {
194-
return schema.GroupVersionResource{}, fmt.Errorf("invalid schema name %q, must consist of version.resource.group.", name)
194+
return schema.GroupVersionResource{}, fmt.Errorf("invalid schema name %q, must consist of version.resource.group", name)
195195
}
196196

197197
return schema.GroupVersionResource{

internal/controller/apiresourceschema/controller.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,22 +144,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
144144
}
145145

146146
func (r *Reconciler) reconcile(ctx context.Context, log *zap.SugaredLogger, pubResource *syncagentv1alpha1.PublishedResource) (*reconcile.Result, error) {
147-
// find the resource that the PublishedResource is referring to
148-
localGK := projection.PublishedResourceSourceGK(pubResource)
149-
147+
// get the projected CRD (i.e. strip unwanted versions, rename values etc.)
150148
client, err := discovery.NewClient(r.restConfig)
151149
if err != nil {
152150
return nil, fmt.Errorf("failed to create discovery client: %w", err)
153151
}
154152

155-
// fetch the original, full CRD from the cluster
156-
crd, err := client.RetrieveCRD(ctx, localGK)
157-
if err != nil {
158-
return nil, fmt.Errorf("failed to discover resource defined in PublishedResource: %w", err)
159-
}
160-
161-
// project the CRD (i.e. strip unwanted versions, rename values etc.)
162-
projectedCRD, err := projection.ProjectCRD(crd, pubResource)
153+
projectedCRD, err := projection.ProjectPublishedResource(ctx, client, pubResource)
163154
if err != nil {
164155
return nil, fmt.Errorf("failed to apply projection rules: %w", err)
165156
}

internal/projection/projection.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ limitations under the License.
1717
package projection
1818

1919
import (
20+
"context"
2021
"errors"
2122
"fmt"
2223
"slices"
2324
"strings"
2425

26+
"github.com/kcp-dev/api-syncagent/internal/discovery"
2527
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
2628

2729
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -113,6 +115,25 @@ func ProjectCRD(crd *apiextensionsv1.CustomResourceDefinition, pubRes *syncagent
113115
return result, nil
114116
}
115117

118+
func ProjectPublishedResource(ctx context.Context, client *discovery.Client, pubRes *syncagentv1alpha1.PublishedResource) (*apiextensionsv1.CustomResourceDefinition, error) {
119+
// find the resource that the PublishedResource is referring to
120+
localGK := PublishedResourceSourceGK(pubRes)
121+
122+
// fetch the original, full CRD from the cluster
123+
crd, err := client.RetrieveCRD(ctx, localGK)
124+
if err != nil {
125+
return nil, fmt.Errorf("failed to discover resource defined in PublishedResource: %w", err)
126+
}
127+
128+
// project the CRD (i.e. strip unwanted versions, rename values etc.)
129+
projectedCRD, err := ProjectCRD(crd, pubRes)
130+
if err != nil {
131+
return nil, fmt.Errorf("failed to apply projection rules: %w", err)
132+
}
133+
134+
return projectedCRD, nil
135+
}
136+
116137
func RelatedResourceGVR(rr *syncagentv1alpha1.RelatedResourceSpec) schema.GroupVersionResource {
117138
resultGVR := schema.GroupVersionResource{
118139
Group: rr.Group,
@@ -124,8 +145,12 @@ func RelatedResourceGVR(rr *syncagentv1alpha1.RelatedResourceSpec) schema.GroupV
124145
//nolint:staticcheck
125146
switch rr.Kind {
126147
case "ConfigMap":
148+
resultGVR.Group = ""
149+
resultGVR.Version = "v1"
127150
resultGVR.Resource = "configmaps"
128151
case "Secret":
152+
resultGVR.Group = ""
153+
resultGVR.Version = "v1"
129154
resultGVR.Resource = "secrets"
130155
}
131156

0 commit comments

Comments
 (0)