Skip to content

Commit e8859e6

Browse files
committed
since there is no webhook, validate the PublishedResource at runtime
This isn't great, but the cheapest option at the moment. On-behalf-of: @SAP [email protected]
1 parent 59d91dd commit e8859e6

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package publishedresource
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
8+
"k8s.io/apimachinery/pkg/util/sets"
9+
)
10+
11+
func ValidatePublishedResource(pr *syncagentv1alpha1.PublishedResource) error {
12+
identifiers := sets.New[string]()
13+
14+
for _, rr := range pr.Spec.Related {
15+
if identifiers.Has(rr.Identifier) {
16+
return fmt.Errorf("multiple related resources use the identifier %q", rr.Identifier)
17+
}
18+
19+
if err := validateRelatedResource(rr); err != nil {
20+
return fmt.Errorf("related resource %q is invalid: %w", rr.Identifier, err)
21+
}
22+
23+
identifiers.Insert(rr.Identifier)
24+
}
25+
26+
return nil
27+
}
28+
29+
func validateRelatedResource(rr syncagentv1alpha1.RelatedResourceSpec) error {
30+
if err := validateRelatedResourceSourceSpec(rr.Source.Name); err != nil {
31+
return fmt.Errorf("%s for the name source", err)
32+
}
33+
34+
if rr.Source.Name.Selector != nil && (rr.Naming == nil || rr.Naming.Name == "") {
35+
return errors.New("must configure a naming rule for the related object's name if a label selector is used")
36+
}
37+
38+
if ns := rr.Source.Namespace; ns != nil {
39+
if err := validateRelatedResourceSourceSpec(*ns); err != nil {
40+
return fmt.Errorf("%s for the namespace source", err)
41+
}
42+
43+
if ns.Selector != nil && (rr.Naming == nil || rr.Naming.Namespace == "") {
44+
return errors.New("must configure a naming rule for the related object's namespace if a label selector is used")
45+
}
46+
}
47+
48+
return nil
49+
}
50+
51+
func validateRelatedResourceSourceSpec(ss syncagentv1alpha1.RelatedResourceSourceSpec) error {
52+
if ss.Selector == nil && ss.Reference == nil {
53+
return errors.New("must use either selector or reference")
54+
}
55+
56+
if ss.Selector != nil && ss.Reference != nil {
57+
return errors.New("cannot use both selector and reference")
58+
}
59+
60+
return nil
61+
}

internal/controller/syncmanager/controller.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/kcp-dev/logicalcluster/v3"
2525
"go.uber.org/zap"
2626

27+
"github.com/kcp-dev/api-syncagent/internal/admission/publishedresource"
2728
"github.com/kcp-dev/api-syncagent/internal/controller/sync"
2829
"github.com/kcp-dev/api-syncagent/internal/controller/syncmanager/lifecycle"
2930
"github.com/kcp-dev/api-syncagent/internal/controllerutil"
@@ -274,6 +275,16 @@ func (r *Reconciler) ensureSyncControllers(ctx context.Context, log *zap.Sugared
274275
continue
275276
}
276277

278+
// If the PR is invalid, do not even start a controller for it. Since the $key contains
279+
// the resource version, if the invalidt PR is fixed, a new controller can be spawned.
280+
// In the future, validation issues like this should be caught by a validation webhook
281+
// or more advanced CEL-based validations on the CRD itself, rather than validating in
282+
// a controller.
283+
if err := publishedresource.ValidatePublishedResource(&pubRes); err != nil {
284+
log.Errorw("Not starting controller for published resource because of errors", zap.Error(err), "pr", pubRes.Name)
285+
continue
286+
}
287+
277288
log.Infow("Starting new sync controller…", "key", key)
278289

279290
// create the sync controller;

0 commit comments

Comments
 (0)