From 67ef84e90fa76fe511764c2b3b1bc61484645833 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sun, 2 Jun 2024 14:19:58 -0400 Subject: [PATCH] feat: add AddReference method to References Signed-off-by: Yordis Prieto --- pkg/config/resource.go | 16 +++ pkg/config/resource_test.go | 221 ++++++++++++++++++++---------------- 2 files changed, 142 insertions(+), 95 deletions(-) diff --git a/pkg/config/resource.go b/pkg/config/resource.go index e7002629..5ca4d53d 100644 --- a/pkg/config/resource.go +++ b/pkg/config/resource.go @@ -165,10 +165,26 @@ type ExternalName struct { IdentifierFields []string } +var ErrReferenceAlreadyExists = errors.New("reference for field already exists") + // References represents reference resolver configurations for the fields of a // given resource. Key should be the field path of the field to be referenced. type References map[string]Reference +// AddReference adds a reference configuration for the given field path. +// The fieldPath is the Terraform field path of the field to be referenced. +// +// Example: "vpc_id" or "forwarding_rule.certificate_name" in case of nested +// in another object. +func (r References) AddReference(fieldPath string, ref Reference) error { + if _, ok := r[fieldPath]; ok { + return ErrReferenceAlreadyExists + } + + r[fieldPath] = ref + return nil +} + // Reference represents the Crossplane options used to generate // reference resolvers for fields type Reference struct { diff --git a/pkg/config/resource_test.go b/pkg/config/resource_test.go index a94366c7..09dfe692 100644 --- a/pkg/config/resource_test.go +++ b/pkg/config/resource_test.go @@ -5,110 +5,141 @@ package config import ( - "context" - "fmt" - "testing" + "context" + "errors" + "fmt" + "testing" - "github.com/crossplane/crossplane-runtime/pkg/errors" - "github.com/crossplane/crossplane-runtime/pkg/fieldpath" - xpresource "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" - "github.com/google/go-cmp/cmp" - "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/crossplane/crossplane-runtime/pkg/fieldpath" + xpresource "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/crossplane-runtime/pkg/resource/fake" + "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/controller-runtime/pkg/client" ) const ( - kind = "ACoolService" - name = "example-service" - provider = "ACoolProvider" + kind = "ACoolService" + name = "example-service" + provider = "ACoolProvider" ) func TestTagger_Initialize(t *testing.T) { - errBoom := errors.New("boom") + errBoom := errors.New("boom") - type args struct { - mg xpresource.Managed - kube client.Client - } - type want struct { - err error - } - cases := map[string]struct { - args - want - }{ - "Successful": { - args: args{ - mg: &fake.Managed{}, - kube: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)}, - }, - want: want{ - err: nil, - }, - }, - "Failure": { - args: args{ - mg: &fake.Managed{}, - kube: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)}, - }, - want: want{ - err: errBoom, - }, - }, - } - for n, tc := range cases { - t.Run(n, func(t *testing.T) { - tagger := NewTagger(tc.kube, "tags") - gotErr := tagger.Initialize(context.TODO(), tc.mg) - if diff := cmp.Diff(tc.want.err, gotErr, test.EquateErrors()); diff != "" { - t.Fatalf("generateTypeName(...): -want error, +got error: %s", diff) - } - }) - } + type args struct { + mg xpresource.Managed + kube client.Client + } + type want struct { + err error + } + cases := map[string]struct { + args + want + }{ + "Successful": { + args: args{ + mg: &fake.Managed{}, + kube: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)}, + }, + want: want{ + err: nil, + }, + }, + "Failure": { + args: args{ + mg: &fake.Managed{}, + kube: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)}, + }, + want: want{ + err: errBoom, + }, + }, + } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + tagger := NewTagger(tc.kube, "tags") + gotErr := tagger.Initialize(context.TODO(), tc.mg) + if diff := cmp.Diff(tc.want.err, gotErr, test.EquateErrors()); diff != "" { + t.Fatalf("generateTypeName(...): -want error, +got error: %s", diff) + } + }) + } } func TestSetExternalTagsWithPaved(t *testing.T) { - type args struct { - externalTags map[string]string - paved *fieldpath.Paved - fieldName string - } - type want struct { - pavedString string - err error - } - cases := map[string]struct { - args - want - }{ - "Successful": { - args: args{ - externalTags: map[string]string{ - xpresource.ExternalResourceTagKeyKind: kind, - xpresource.ExternalResourceTagKeyName: name, - xpresource.ExternalResourceTagKeyProvider: provider, - }, - paved: fieldpath.Pave(map[string]any{}), - fieldName: "tags", - }, - want: want{ - pavedString: fmt.Sprintf(`{"spec":{"forProvider":{"tags":{"%s":"%s","%s":"%s","%s":"%s"}}}}`, - xpresource.ExternalResourceTagKeyKind, kind, - xpresource.ExternalResourceTagKeyName, name, - xpresource.ExternalResourceTagKeyProvider, provider), - }, - }, - } - for n, tc := range cases { - t.Run(n, func(t *testing.T) { - gotByte, gotErr := setExternalTagsWithPaved(tc.externalTags, tc.paved, tc.fieldName) - if diff := cmp.Diff(tc.want.err, gotErr, test.EquateErrors()); diff != "" { - t.Fatalf("generateTypeName(...): -want error, +got error: %s", diff) - } - if diff := cmp.Diff(tc.want.pavedString, string(gotByte), test.EquateErrors()); diff != "" { - t.Fatalf("generateTypeName(...): -want gotByte, +got gotByte: %s", diff) - } - }) - } + type args struct { + externalTags map[string]string + paved *fieldpath.Paved + fieldName string + } + type want struct { + pavedString string + err error + } + cases := map[string]struct { + args + want + }{ + "Successful": { + args: args{ + externalTags: map[string]string{ + xpresource.ExternalResourceTagKeyKind: kind, + xpresource.ExternalResourceTagKeyName: name, + xpresource.ExternalResourceTagKeyProvider: provider, + }, + paved: fieldpath.Pave(map[string]any{}), + fieldName: "tags", + }, + want: want{ + pavedString: fmt.Sprintf(`{"spec":{"forProvider":{"tags":{"%s":"%s","%s":"%s","%s":"%s"}}}}`, + xpresource.ExternalResourceTagKeyKind, kind, + xpresource.ExternalResourceTagKeyName, name, + xpresource.ExternalResourceTagKeyProvider, provider), + }, + }, + } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + gotByte, gotErr := setExternalTagsWithPaved(tc.externalTags, tc.paved, tc.fieldName) + if diff := cmp.Diff(tc.want.err, gotErr, test.EquateErrors()); diff != "" { + t.Fatalf("generateTypeName(...): -want error, +got error: %s", diff) + } + if diff := cmp.Diff(tc.want.pavedString, string(gotByte), test.EquateErrors()); diff != "" { + t.Fatalf("generateTypeName(...): -want gotByte, +got gotByte: %s", diff) + } + }) + } +} + +func TestReferences_AddReference(t *testing.T) { + t.Run("Adding a reference", func(t *testing.T) { + r := &Resource{References: References{}} + err := r.References.AddReference("forwarding_rule.certificate_name", Reference{ + TerraformName: "digitalocean_certificate", + }) + if err != nil { + t.Fatalf("AddReference() got error: %v", err) + } + if len(r.References) != 1 { + t.Fatalf("AddReference() got error: %v", err) + } + }) + t.Run("Adding twice a reference for a given field", func(t *testing.T) { + r := &Resource{References: References{}} + err := r.References.AddReference("forwarding_rule.certificate_name", Reference{ + TerraformName: "digitalocean_certificate", + }) + if err != nil { + t.Fatalf("AddReference() got error: %v", err) + } + + err = r.References.AddReference("forwarding_rule.certificate_name", Reference{ + TerraformName: "digitalocean_certificate", + }) + if !errors.Is(err, ErrReferenceAlreadyExists) { + t.Fatalf("AddReference() should have returned an error") + } + }) }