Support for having circular dependencies while using referencers

Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
This commit is contained in:
Sergen Yalçın 2022-04-14 14:56:17 +03:00
parent 988c9ba9c2
commit ac7cf2045e
No known key found for this signature in database
GPG Key ID: 1EC8D6C7CC79DB6B
3 changed files with 72 additions and 5 deletions

View File

@ -77,10 +77,27 @@ type SecretKeySelector struct {
Key string `json:"key"`
}
// A ReferenceResolutionPolicy is a value for resolution policy for reference.
type ReferenceResolutionPolicy string
const (
// ReferencePolicyOptional is a resolution option.
// When the ReferenceResolutionPolicy is set to ReferencePolicyOptional the
// execution could continue even if the reference cannot be resolved.
ReferencePolicyOptional ReferenceResolutionPolicy = "Optional"
)
// A Reference to a named object.
type Reference struct {
// Name of the referenced object.
Name string `json:"name"`
// Policy of the referenced object.
Policy ReferenceResolutionPolicy `json:"policy,omitempty"`
}
// IsReferenceResolutionPolicyOptional checks whether the resolution policy of relevant reference is Optional.
func (in *Reference) IsReferenceResolutionPolicyOptional() bool {
return in.Policy == ReferencePolicyOptional
}
// A TypedReference refers to an object by Name, Kind, and APIVersion. It is

View File

@ -182,9 +182,9 @@ func (rr MultiResolutionResponse) Validate() error {
return errors.New(errNoMatches)
}
for _, v := range rr.ResolvedValues {
for i, v := range rr.ResolvedValues {
if v == "" {
return errors.New(errNoValue)
return getReferenceError(&rr.ResolvedReferences[i], errors.New(errNoValue))
}
}
@ -216,11 +216,11 @@ func (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (Resol
// The reference is already set - resolve it.
if req.Reference != nil {
if err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name}, req.To.Managed); err != nil {
return ResolutionResponse{}, errors.Wrap(err, errGetManaged)
return ResolutionResponse{}, getReferenceError(req.Reference, errors.Wrap(err, errGetManaged))
}
rsp := ResolutionResponse{ResolvedValue: req.Extract(req.To.Managed), ResolvedReference: req.Reference}
return rsp, rsp.Validate()
return rsp, getReferenceError(req.Reference, rsp.Validate())
}
// The reference was not set, but a selector was. Select a reference.
@ -255,7 +255,7 @@ func (r *APIResolver) ResolveMultiple(ctx context.Context, req MultiResolutionRe
vals := make([]string, len(req.References))
for i := range req.References {
if err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name}, req.To.Managed); err != nil {
return MultiResolutionResponse{}, errors.Wrap(err, errGetManaged)
return MultiResolutionResponse{}, getReferenceError(&req.References[i], errors.Wrap(err, errGetManaged))
}
vals[i] = req.Extract(req.To.Managed)
}
@ -285,6 +285,13 @@ func (r *APIResolver) ResolveMultiple(ctx context.Context, req MultiResolutionRe
return rsp, rsp.Validate()
}
func getReferenceError(ref *xpv1.Reference, err error) error {
if !ref.IsReferenceResolutionPolicyOptional() {
return err
}
return nil
}
// ControllersMustMatch returns true if the supplied Selector requires that a
// reference be to a managed resource whose controller reference matches the
// referencing resource.

View File

@ -91,6 +91,7 @@ func TestResolve(t *testing.T) {
now := metav1.Now()
value := "coolv"
ref := &xpv1.Reference{Name: "cool"}
optionalRef := &xpv1.Reference{Name: "cool", Policy: xpv1.ReferencePolicyOptional}
controlled := &fake.Managed{}
controlled.SetName(value)
@ -204,6 +205,26 @@ func TestResolve(t *testing.T) {
},
},
},
"OptionalPolicy": {
reason: "No error should be returned when the resolution policy is Optional",
c: &test.MockClient{
MockGet: test.NewMockGetFn(nil),
},
from: &fake.Managed{},
args: args{
req: ResolutionRequest{
Reference: optionalRef,
To: To{Managed: &fake.Managed{}},
Extract: func(resource.Managed) string { return "" },
},
},
want: want{
rsp: ResolutionResponse{
ResolvedReference: optionalRef,
},
err: nil,
},
},
"ListError": {
reason: "Should return errors encountered while listing potential referenced resources",
c: &test.MockClient{
@ -282,6 +303,7 @@ func TestResolveMultiple(t *testing.T) {
now := metav1.Now()
value := "coolv"
ref := xpv1.Reference{Name: "cool"}
optionalRef := xpv1.Reference{Name: "cool", Policy: xpv1.ReferencePolicyOptional}
controlled := &fake.Managed{}
controlled.SetName(value)
@ -396,6 +418,27 @@ func TestResolveMultiple(t *testing.T) {
},
},
},
"OptionalPolicy": {
reason: "No error should be returned when the resolution policy is Optional",
c: &test.MockClient{
MockGet: test.NewMockGetFn(nil),
},
from: &fake.Managed{},
args: args{
req: MultiResolutionRequest{
References: []xpv1.Reference{optionalRef},
To: To{Managed: &fake.Managed{}},
Extract: func(resource.Managed) string { return "" },
},
},
want: want{
rsp: MultiResolutionResponse{
ResolvedValues: []string{""},
ResolvedReferences: []xpv1.Reference{optionalRef},
},
err: nil,
},
},
"ListError": {
reason: "Should return errors encountered while listing potential referenced resources",
c: &test.MockClient{