Skip to content

Commit a8bb1e8

Browse files
committed
correctly merge and update existing ResourceSchemas with new ones when reconciling APIExports
On-behalf-of: @SAP [email protected]
1 parent 3c7647a commit a8bb1e8

File tree

2 files changed

+44
-14
lines changed

2 files changed

+44
-14
lines changed

internal/controller/apiexport/controller.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package apiexport
1919
import (
2020
"context"
2121
"fmt"
22+
"slices"
2223

2324
"github.com/kcp-dev/logicalcluster/v3"
2425
"go.uber.org/zap"
@@ -121,12 +122,9 @@ func (r *Reconciler) reconcile(ctx context.Context) error {
121122
}
122123

123124
// filter out those PRs that have not yet been processed into an ARS
124-
filteredPubResources := []syncagentv1alpha1.PublishedResource{}
125-
for i, pubResource := range pubResources.Items {
126-
if pubResource.Status.ResourceSchemaName != "" {
127-
filteredPubResources = append(filteredPubResources, pubResources.Items[i])
128-
}
129-
}
125+
filteredPubResources := slices.DeleteFunc(pubResources.Items, func(pr syncagentv1alpha1.PublishedResource) bool {
126+
return pr.Status.ResourceSchemaName == ""
127+
})
130128

131129
// for each PR, we note down the created ARS and also the GVKs of related resources
132130
arsList := sets.New[string]()

internal/controller/apiexport/reconciler.go

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ limitations under the License.
1717
package apiexport
1818

1919
import (
20-
"cmp"
2120
"slices"
21+
"strings"
2222

2323
"github.com/kcp-dev/api-syncagent/internal/resources/reconciling"
2424
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
@@ -34,16 +34,13 @@ import (
3434
func (r *Reconciler) createAPIExportReconciler(availableResourceSchemas sets.Set[string], claimedResourceKinds sets.Set[string], agentName string, apiExportName string) reconciling.NamedAPIExportReconcilerFactory {
3535
return func() (string, reconciling.APIExportReconciler) {
3636
return apiExportName, func(existing *kcpdevv1alpha1.APIExport) (*kcpdevv1alpha1.APIExport, error) {
37-
known := sets.New(existing.Spec.LatestResourceSchemas...)
38-
3937
if existing.Annotations == nil {
4038
existing.Annotations = map[string]string{}
4139
}
4240
existing.Annotations[syncagentv1alpha1.AgentNameAnnotation] = agentName
4341

44-
// we only ever add new schemas
45-
result := known.Union(availableResourceSchemas)
46-
existing.Spec.LatestResourceSchemas = sets.List(result)
42+
// combine existing schemas with new ones
43+
existing.Spec.LatestResourceSchemas = mergeResourceSchemas(existing.Spec.LatestResourceSchemas, availableResourceSchemas)
4744

4845
// To allow admins to configure additional permission claims, sometimes
4946
// useful for debugging, we do not override the permission claims, but
@@ -73,11 +70,11 @@ func (r *Reconciler) createAPIExportReconciler(availableResourceSchemas sets.Set
7370
// prevent reconcile loops by ensuring a stable order
7471
slices.SortFunc(existing.Spec.PermissionClaims, func(a, b kcpdevv1alpha1.PermissionClaim) int {
7572
if a.Group != b.Group {
76-
return cmp.Compare(a.Group, b.Group)
73+
return strings.Compare(a.Group, b.Group)
7774
}
7875

7976
if a.Resource != b.Resource {
80-
return cmp.Compare(a.Resource, b.Resource)
77+
return strings.Compare(a.Resource, b.Resource)
8178
}
8279

8380
return 0
@@ -87,3 +84,38 @@ func (r *Reconciler) createAPIExportReconciler(availableResourceSchemas sets.Set
8784
}
8885
}
8986
}
87+
88+
func mergeResourceSchemas(existing []string, configured sets.Set[string]) []string {
89+
var result []string
90+
91+
// first we copy all ARS that are coming from the PublishedResources
92+
knownResources := sets.New[string]()
93+
for _, schema := range configured.UnsortedList() {
94+
result = append(result, schema)
95+
knownResources.Insert(parseResourceGroup(schema))
96+
}
97+
98+
// Now we include all other existing ARS that use unknown resources;
99+
// this both allows an APIExport to contain "unmanaged" ARS, and also
100+
// will purposefully leave behind ARS for deleted PublishedResources,
101+
// allowing cleanup to take place outside of the agent's control.
102+
for _, schema := range existing {
103+
if !knownResources.Has(parseResourceGroup(schema)) {
104+
result = append(result, schema)
105+
}
106+
}
107+
108+
// for stability and beauty, sort the schemas
109+
slices.SortFunc(result, func(a, b string) int {
110+
return strings.Compare(parseResourceGroup(a), parseResourceGroup(b))
111+
})
112+
113+
return result
114+
}
115+
116+
func parseResourceGroup(schema string) string {
117+
// <version>.<resource>.<group>
118+
parts := strings.SplitN(schema, ".", 2)
119+
120+
return parts[1]
121+
}

0 commit comments

Comments
 (0)