Skip to content

Commit 92d0af9

Browse files
committed
add logic to apiresourceschema ctrl to maintain a matching APIConversion object in kcp
On-behalf-of: @SAP [email protected]
1 parent 50e77d1 commit 92d0af9

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

internal/controller/apiresourceschema/controller.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/kcp-dev/api-syncagent/internal/crypto"
2929
"github.com/kcp-dev/api-syncagent/internal/discovery"
3030
"github.com/kcp-dev/api-syncagent/internal/projection"
31+
"github.com/kcp-dev/api-syncagent/internal/resources/reconciling"
3132
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
3233

3334
kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
@@ -135,16 +136,28 @@ func (r *Reconciler) reconcile(ctx context.Context, log *zap.SugaredLogger, pubR
135136
}
136137

137138
// project the CRD (i.e. strip unwanted versions, rename values etc.)
138-
projectedCRD, err := projection.ApplyProjection(crd, pubResource)
139+
projectedCRD, err := projection.ProjectCRD(crd, pubResource)
139140
if err != nil {
140141
return nil, fmt.Errorf("failed to apply projection rules: %w", err)
141142
}
142143

143144
// generate a unique name for this exact state of the CRD
144145
arsName := r.getAPIResourceSchemaName(projectedCRD)
146+
wsCtx := kontext.WithCluster(ctx, r.lcName)
147+
148+
projectedConversions, err := projection.ProjectConversionRules(pubResource)
149+
if err != nil {
150+
return nil, fmt.Errorf("failed to apply projection rules to conversions: %w", err)
151+
}
152+
153+
// only reconcile if there are rules because APIConversions must contain at least one conversion
154+
if len(projectedConversions) > 0 {
155+
if err := r.reconcileConversions(wsCtx, arsName, projectedConversions); err != nil {
156+
return nil, fmt.Errorf("failed to reconcile APIConversions: %w", err)
157+
}
158+
}
145159

146160
// ensure ARS exists (don't try to reconcile it, it's basically entirely immutable)
147-
wsCtx := kontext.WithCluster(ctx, r.lcName)
148161
ars := &kcpdevv1alpha1.APIResourceSchema{}
149162
err = r.kcpClient.Get(wsCtx, types.NamespacedName{Name: arsName}, ars, &ctrlruntimeclient.GetOptions{})
150163

@@ -169,6 +182,8 @@ func (r *Reconciler) reconcile(ctx context.Context, log *zap.SugaredLogger, pubR
169182
}
170183
}
171184

185+
// reconcile a matching APIConversion object
186+
172187
return nil, nil
173188
}
174189

@@ -206,3 +221,11 @@ func (r *Reconciler) getAPIResourceSchemaName(crd *apiextensionsv1.CustomResourc
206221
// include a leading "v" to prevent SHA-1 hashes with digits to break the name
207222
return fmt.Sprintf("v%s.%s.%s", checksum[:8], crd.Spec.Names.Plural, crd.Spec.Group)
208223
}
224+
225+
func (r *Reconciler) reconcileConversions(ctx context.Context, arsName string, rules []kcpdevv1alpha1.APIVersionConversion) error {
226+
factories := []reconciling.NamedAPIConversionReconcilerFactory{
227+
r.createAPIConversionReconciler(arsName, r.agentName, rules),
228+
}
229+
230+
return reconciling.ReconcileAPIConversions(ctx, factories, "", r.kcpClient)
231+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Copyright 2025 The KCP Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package apiresourceschema
18+
19+
import (
20+
"github.com/kcp-dev/api-syncagent/internal/resources/reconciling"
21+
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
22+
23+
kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
24+
kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
25+
)
26+
27+
func (r *Reconciler) createAPIConversionReconciler(name string, agentName string, rules []kcpapisv1alpha1.APIVersionConversion) reconciling.NamedAPIConversionReconcilerFactory {
28+
return func() (string, reconciling.APIConversionReconciler) {
29+
return name, func(existing *kcpdevv1alpha1.APIConversion) (*kcpdevv1alpha1.APIConversion, error) {
30+
if existing.Annotations == nil {
31+
existing.Annotations = map[string]string{}
32+
}
33+
existing.Annotations[syncagentv1alpha1.AgentNameAnnotation] = agentName
34+
35+
existing.Spec = kcpdevv1alpha1.APIConversionSpec{
36+
Conversions: rules,
37+
}
38+
39+
return existing, nil
40+
}
41+
}
42+
}

internal/projection/projection.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424

2525
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
2626

27+
kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
28+
2729
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2830
"k8s.io/apimachinery/pkg/runtime/schema"
2931
"k8s.io/apimachinery/pkg/util/sets"
@@ -67,7 +69,7 @@ func getStorageVersion(crd *apiextensionsv1.CustomResourceDefinition) string {
6769
// PublishedResourceProjectedGVK returns the effective GVK after the projection
6870
// rules have been applied according to the PublishedResource.
6971
func PublishedResourceProjectedGVK(originalCRD *apiextensionsv1.CustomResourceDefinition, pubRes *syncagentv1alpha1.PublishedResource) (schema.GroupVersionKind, error) {
70-
projectedCRD, err := ApplyProjection(originalCRD, pubRes)
72+
projectedCRD, err := ProjectCRD(originalCRD, pubRes)
7173
if err != nil {
7274
return schema.GroupVersionKind{}, fmt.Errorf("failed to project CRD: %w", err)
7375
}
@@ -84,7 +86,7 @@ func PublishedResourceProjectedGVK(originalCRD *apiextensionsv1.CustomResourceDe
8486
}, nil
8587
}
8688

87-
func ApplyProjection(crd *apiextensionsv1.CustomResourceDefinition, pubRes *syncagentv1alpha1.PublishedResource) (*apiextensionsv1.CustomResourceDefinition, error) {
89+
func ProjectCRD(crd *apiextensionsv1.CustomResourceDefinition, pubRes *syncagentv1alpha1.PublishedResource) (*apiextensionsv1.CustomResourceDefinition, error) {
8890
result := crd.DeepCopy()
8991

9092
// reduce the CRD down to the selected versions
@@ -100,7 +102,7 @@ func ApplyProjection(crd *apiextensionsv1.CustomResourceDefinition, pubRes *sync
100102
}
101103

102104
// now we get to actually project something, if desired
103-
result, err = projectCRD(result, pubRes)
105+
result, err = projectCRDNames(result, pubRes)
104106
if err != nil {
105107
return nil, err
106108
}
@@ -168,7 +170,7 @@ func adjustStorageVersion(crd *apiextensionsv1.CustomResourceDefinition) (*apiex
168170
return crd, nil
169171
}
170172

171-
func projectCRD(crd *apiextensionsv1.CustomResourceDefinition, pubRes *syncagentv1alpha1.PublishedResource) (*apiextensionsv1.CustomResourceDefinition, error) {
173+
func projectCRDNames(crd *apiextensionsv1.CustomResourceDefinition, pubRes *syncagentv1alpha1.PublishedResource) (*apiextensionsv1.CustomResourceDefinition, error) {
172174
projection := pubRes.Spec.Projection
173175
if projection == nil {
174176
return crd, nil
@@ -237,3 +239,22 @@ func projectCRD(crd *apiextensionsv1.CustomResourceDefinition, pubRes *syncagent
237239

238240
return crd, nil
239241
}
242+
243+
func ProjectConversionRules(pubRes *syncagentv1alpha1.PublishedResource) ([]kcpapisv1alpha1.APIVersionConversion, error) {
244+
result := pubRes.DeepCopy().Spec.Conversions
245+
246+
if proj := pubRes.Spec.Projection; proj != nil {
247+
for idx, conversion := range result {
248+
if projectedFrom := proj.Versions[conversion.From]; projectedFrom != "" {
249+
conversion.From = projectedFrom
250+
}
251+
if projectedTo := proj.Versions[conversion.To]; projectedTo != "" {
252+
conversion.To = projectedTo
253+
}
254+
255+
result[idx] = conversion
256+
}
257+
}
258+
259+
return result, nil
260+
}

0 commit comments

Comments
 (0)