From 9f5188e0cb8fa21ef5bd06fca9df0021ba1b8006 Mon Sep 17 00:00:00 2001 From: Nic Cope Date: Sat, 12 Oct 2019 20:39:35 -0700 Subject: [PATCH] Assume resources, classes, and providers are cluster scoped Signed-off-by: Nic Cope --- apis/core/v1alpha1/resource.go | 16 ++- apis/core/v1alpha1/zz_generated.deepcopy.go | 6 +- pkg/resource/api.go | 9 +- pkg/resource/api_test.go | 32 ++--- pkg/resource/configurator.go | 17 ++- pkg/resource/configurator_test.go | 3 - pkg/resource/interfaces.go | 15 ++- pkg/resource/interfaces_test.go | 17 ++- pkg/resource/publisher.go | 2 +- pkg/resource/publisher_test.go | 49 +++++++- pkg/resource/resource.go | 36 +++++- pkg/resource/resource_test.go | 129 +++++++++++++++++--- 12 files changed, 264 insertions(+), 67 deletions(-) diff --git a/apis/core/v1alpha1/resource.go b/apis/core/v1alpha1/resource.go index 0bcc03e..3e9960b 100644 --- a/apis/core/v1alpha1/resource.go +++ b/apis/core/v1alpha1/resource.go @@ -78,13 +78,12 @@ type ResourceClaimStatus struct { // A ResourceSpec defines the desired state of a managed resource. type ResourceSpec struct { - // WriteConnectionSecretToReference specifies the name of a Secret, in the - // same namespace as this managed resource, to which any connection details - // for this managed resource should be written. Connection details - // frequently include the endpoint, username, and password required to - // connect to the managed resource. + // WriteConnectionSecretToReference specifies the namespace and name of a + // Secret to which any connection details for this managed resource should + // be written. Connection details frequently include the endpoint, username, + // and password required to connect to the managed resource. // +optional - WriteConnectionSecretToReference corev1.LocalObjectReference `json:"writeConnectionSecretToRef,omitempty"` + WriteConnectionSecretToReference *corev1.ObjectReference `json:"writeConnectionSecretToRef,omitempty"` // ClaimReference specifies the resource claim to which this managed // resource will be bound. ClaimReference is set automatically during @@ -124,6 +123,11 @@ type ResourceStatus struct { // specifications of managed resources dynamically provisioned using a resource // class. type ClassSpecTemplate struct { + // WriteConnectionSecretsToNamespace specifies the namespace in which the + // connection secrets of managed resources dynamically provisioned using + // this claim will be created. + WriteConnectionSecretsToNamespace string `json:"writeConnectionSecretsToNamespace"` + // ProviderReference specifies the provider that will be used to create, // observe, update, and delete managed resources that are dynamically // provisioned using this resource class. diff --git a/apis/core/v1alpha1/zz_generated.deepcopy.go b/apis/core/v1alpha1/zz_generated.deepcopy.go index 3b14ac2..a401af7 100644 --- a/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -143,7 +143,11 @@ func (in *ResourceClaimStatus) DeepCopy() *ResourceClaimStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) { *out = *in - out.WriteConnectionSecretToReference = in.WriteConnectionSecretToReference + if in.WriteConnectionSecretToReference != nil { + in, out := &in.WriteConnectionSecretToReference, &out.WriteConnectionSecretToReference + *out = new(v1.ObjectReference) + **out = **in + } if in.ClaimReference != nil { in, out := &in.ClaimReference, &out.ClaimReference *out = new(v1.ObjectReference) diff --git a/pkg/resource/api.go b/pkg/resource/api.go index 0152fe8..c05aa53 100644 --- a/pkg/resource/api.go +++ b/pkg/resource/api.go @@ -93,11 +93,14 @@ func NewAPIManagedConnectionPropagator(c client.Client, t runtime.ObjectTyper) * func (a *APIManagedConnectionPropagator) PropagateConnection(ctx context.Context, cm Claim, mg Managed) error { // Either this resource does not expose a connection secret, or this claim // does not want one. - if mg.GetWriteConnectionSecretToReference().Name == "" || cm.GetWriteConnectionSecretToReference().Name == "" { + if mg.GetWriteConnectionSecretToReference() == nil || cm.GetWriteConnectionSecretToReference().Name == "" { return nil } - n := types.NamespacedName{Namespace: mg.GetNamespace(), Name: mg.GetWriteConnectionSecretToReference().Name} + n := types.NamespacedName{ + Namespace: mg.GetWriteConnectionSecretToReference().Namespace, + Name: mg.GetWriteConnectionSecretToReference().Name, + } mgcs := &corev1.Secret{} if err := a.client.Get(ctx, n, mgcs); err != nil { return errors.Wrap(err, errGetSecret) @@ -111,7 +114,7 @@ func (a *APIManagedConnectionPropagator) PropagateConnection(ctx context.Context return errors.New(errSecretConflict) } - cmcs := ConnectionSecretFor(cm, MustGetKind(cm, a.typer)) + cmcs := LocalConnectionSecretFor(cm, MustGetKind(cm, a.typer)) if _, err := util.CreateOrUpdate(ctx, a.client, cmcs, func() error { // Inside this anonymous function cmcs could either be unchanged (if // it does not exist in the API server) or updated to reflect its diff --git a/pkg/resource/api_test.go b/pkg/resource/api_test.go index b374928..c665d5f 100644 --- a/pkg/resource/api_test.go +++ b/pkg/resource/api_test.go @@ -187,7 +187,7 @@ func TestPropagateConnection(t *testing.T) { ctx: context.Background(), cm: &MockClaim{}, mg: &MockManaged{ - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, }, want: nil, @@ -196,7 +196,7 @@ func TestPropagateConnection(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockLocalConnectionSecretWriterTo: MockLocalConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, }, mg: &MockManaged{}, }, @@ -209,10 +209,10 @@ func TestPropagateConnection(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, + MockLocalConnectionSecretWriterTo: MockLocalConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, }, mg: &MockManaged{ - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, }, want: errors.Wrap(errBoom, errGetSecret), @@ -248,12 +248,12 @@ func TestPropagateConnection(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ - ObjectMeta: metav1.ObjectMeta{Name: cmname}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, + ObjectMeta: metav1.ObjectMeta{Name: cmname}, + MockLocalConnectionSecretWriterTo: MockLocalConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, }, mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname, UID: uid}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, }, want: errors.Wrap(errors.New(errSecretConflict), errCreateOrUpdateSecret), @@ -285,12 +285,12 @@ func TestPropagateConnection(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ - ObjectMeta: metav1.ObjectMeta{Name: cmname}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, + ObjectMeta: metav1.ObjectMeta{Name: cmname}, + MockLocalConnectionSecretWriterTo: MockLocalConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, }, mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname, UID: uid}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, }, want: errors.Wrap(errors.New(errSecretConflict), errCreateOrUpdateSecret), @@ -331,12 +331,12 @@ func TestPropagateConnection(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ - ObjectMeta: metav1.ObjectMeta{Name: cmname}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, + ObjectMeta: metav1.ObjectMeta{Name: cmname}, + MockLocalConnectionSecretWriterTo: MockLocalConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, }, mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname, UID: uid}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, }, want: errors.Wrap(errBoom, errUpdateSecret), @@ -402,12 +402,12 @@ func TestPropagateConnection(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ - ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: cmname, UID: uid}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, + ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: cmname, UID: uid}, + MockLocalConnectionSecretWriterTo: MockLocalConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: cmcsname}}, }, mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname, UID: uid}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, }, want: nil, diff --git a/pkg/resource/configurator.go b/pkg/resource/configurator.go index b592f3b..f1ebff6 100644 --- a/pkg/resource/configurator.go +++ b/pkg/resource/configurator.go @@ -52,16 +52,21 @@ func NewObjectMetaConfigurator(t runtime.ObjectTyper) *ObjectMetaConfigurator { } // Configure the supplied Managed resource's object metadata. -func (c *ObjectMetaConfigurator) Configure(_ context.Context, cm Claim, cs Class, mg Managed) error { - mg.SetNamespace(cs.GetNamespace()) +func (c *ObjectMetaConfigurator) Configure(_ context.Context, cm Claim, _ Class, mg Managed) error { mg.SetGenerateName(fmt.Sprintf("%s-%s-", cm.GetNamespace(), cm.GetName())) if meta.GetExternalName(cm) != "" { meta.SetExternalName(mg, meta.GetExternalName(cm)) } - // TODO(negz): Don't set this potentially cross-namespace owner reference. - // We probably want to use the resource's reclaim policy, not Kubernetes - // garbage collection, to determine whether to delete a managed resource - // when its claim is deleted per https://github.com/crossplaneio/crossplane/issues/550 + + // TODO(negz): Avoid setting this owner reference? Kubernetes specifies that + // cluster scoped resources cannot have namespaced owners, by design, but + // the owner reference appears to work for cascading deletes. + // https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#owners-and-dependents + + // TODO(negz): We probably want to use the resource's reclaim policy, not + // Kubernetes garbage collection, to determine whether to delete a managed + // resource when its claim is deleted per + // https://github.com/crossplaneio/crossplane/issues/550 mg.SetOwnerReferences([]v1.OwnerReference{meta.AsOwner(meta.ReferenceTo(cm, MustGetKind(cm, c.typer)))}) return nil diff --git a/pkg/resource/configurator_test.go b/pkg/resource/configurator_test.go index 157155a..7633f61 100644 --- a/pkg/resource/configurator_test.go +++ b/pkg/resource/configurator_test.go @@ -100,7 +100,6 @@ func TestConfiguratorChain(t *testing.T) { } func TestConfigureObjectMeta(t *testing.T) { - ns := "namespace" claimName := "myclaim" claimNS := "myclaimns" uid := types.UID("definitely-a-uuid") @@ -127,12 +126,10 @@ func TestConfigureObjectMeta(t *testing.T) { args: args{ ctx: context.Background(), cm: &MockClaim{ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: claimNS, UID: uid}}, - cs: &MockClass{ObjectMeta: metav1.ObjectMeta{Namespace: ns}}, mg: &MockManaged{}, }, want: want{ mg: &MockManaged{ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, GenerateName: claimNS + "-" + claimName + "-", OwnerReferences: []metav1.OwnerReference{{ APIVersion: MockGVK(&MockClaim{}).GroupVersion().String(), diff --git a/pkg/resource/interfaces.go b/pkg/resource/interfaces.go index 0535b7b..b9907ee 100644 --- a/pkg/resource/interfaces.go +++ b/pkg/resource/interfaces.go @@ -56,12 +56,20 @@ type ManagedResourceReferencer interface { GetResourceReference() *corev1.ObjectReference } -// A ConnectionSecretWriterTo may write a connection secret. -type ConnectionSecretWriterTo interface { +// A LocalConnectionSecretWriterTo may write a connection secret to its own +// namespace. +type LocalConnectionSecretWriterTo interface { SetWriteConnectionSecretToReference(r corev1.LocalObjectReference) GetWriteConnectionSecretToReference() corev1.LocalObjectReference } +// A ConnectionSecretWriterTo may write a connection secret to an arbitrary +// namespace. +type ConnectionSecretWriterTo interface { + SetWriteConnectionSecretToReference(r *corev1.ObjectReference) + GetWriteConnectionSecretToReference() *corev1.ObjectReference +} + // A Reclaimer may specify a ReclaimPolicy. type Reclaimer interface { SetReclaimPolicy(p v1alpha1.ReclaimPolicy) @@ -75,9 +83,10 @@ type Claim interface { runtime.Object metav1.Object + ClassSelector ClassReferencer ManagedResourceReferencer - ConnectionSecretWriterTo + LocalConnectionSecretWriterTo Conditioned Bindable diff --git a/pkg/resource/interfaces_test.go b/pkg/resource/interfaces_test.go index 83d29a3..be4dff7 100644 --- a/pkg/resource/interfaces_test.go +++ b/pkg/resource/interfaces_test.go @@ -55,12 +55,21 @@ type MockManagedResourceReferencer struct{ Ref *corev1.ObjectReference } func (m *MockManagedResourceReferencer) SetResourceReference(r *corev1.ObjectReference) { m.Ref = r } func (m *MockManagedResourceReferencer) GetResourceReference() *corev1.ObjectReference { return m.Ref } -type MockConnectionSecretWriterTo struct{ Ref corev1.LocalObjectReference } +type MockLocalConnectionSecretWriterTo struct{ Ref corev1.LocalObjectReference } -func (m *MockConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r corev1.LocalObjectReference) { +func (m *MockLocalConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r corev1.LocalObjectReference) { m.Ref = r } -func (m *MockConnectionSecretWriterTo) GetWriteConnectionSecretToReference() corev1.LocalObjectReference { +func (m *MockLocalConnectionSecretWriterTo) GetWriteConnectionSecretToReference() corev1.LocalObjectReference { + return m.Ref +} + +type MockConnectionSecretWriterTo struct{ Ref *corev1.ObjectReference } + +func (m *MockConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r *corev1.ObjectReference) { + m.Ref = r +} +func (m *MockConnectionSecretWriterTo) GetWriteConnectionSecretToReference() *corev1.ObjectReference { return m.Ref } @@ -77,7 +86,7 @@ type MockClaim struct { metav1.ObjectMeta MockClassReferencer MockManagedResourceReferencer - MockConnectionSecretWriterTo + MockLocalConnectionSecretWriterTo MockConditioned MockBindable } diff --git a/pkg/resource/publisher.go b/pkg/resource/publisher.go index 9045662..0b22fb4 100644 --- a/pkg/resource/publisher.go +++ b/pkg/resource/publisher.go @@ -68,7 +68,7 @@ func NewAPISecretPublisher(c client.Client, ot runtime.ObjectTyper) *APISecretPu // exists with the supplied ConnectionDetails. func (a *APISecretPublisher) PublishConnection(ctx context.Context, mg Managed, c ConnectionDetails) error { // This resource does not want to expose a connection secret. - if mg.GetWriteConnectionSecretToReference().Name == "" { + if mg.GetWriteConnectionSecretToReference() == nil { return nil } diff --git a/pkg/resource/publisher_test.go b/pkg/resource/publisher_test.go index 532e4b2..299d82b 100644 --- a/pkg/resource/publisher_test.go +++ b/pkg/resource/publisher_test.go @@ -122,6 +122,7 @@ func TestAPISecretPublisher(t *testing.T) { } mgname := "coolmanaged" + mgcsnamespace := "coolnamespace" mgcsname := "coolmanagedsecret" mgcsdata := map[string][]byte{ "cool": []byte("data"), @@ -163,7 +164,7 @@ func TestAPISecretPublisher(t *testing.T) { args: args{ ctx: context.Background(), mg: &MockManaged{ - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, c: ConnectionDetails{}, }, @@ -183,7 +184,7 @@ func TestAPISecretPublisher(t *testing.T) { args: args{ ctx: context.Background(), mg: &MockManaged{ - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, c: ConnectionDetails{}, }, @@ -195,6 +196,7 @@ func TestAPISecretPublisher(t *testing.T) { MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, "")), MockCreate: test.NewMockCreateFn(nil, func(got runtime.Object) error { want := &corev1.Secret{} + want.SetNamespace(mgcsnamespace) want.SetName(mgcsname) want.SetOwnerReferences([]metav1.OwnerReference{{ Name: mgname, @@ -215,7 +217,40 @@ func TestAPISecretPublisher(t *testing.T) { ctx: context.Background(), mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Namespace: mgcsnamespace, Name: mgcsname}}, + }, + c: ConnectionDetails(cddata), + }, + want: nil, + }, + "SuccessfulCreateInDefaultNamespace": { + fields: fields{ + client: &test.MockClient{ + MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, "")), + MockCreate: test.NewMockCreateFn(nil, func(got runtime.Object) error { + want := &corev1.Secret{} + want.SetNamespace(corev1.NamespaceDefault) + want.SetName(mgcsname) + want.SetOwnerReferences([]metav1.OwnerReference{{ + Name: mgname, + APIVersion: MockGVK(&MockManaged{}).GroupVersion().String(), + Kind: MockGVK(&MockManaged{}).Kind, + Controller: &controller, + }}) + want.Data = cddata + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + typer: MockSchemeWith(&MockManaged{}), + }, + args: args{ + ctx: context.Background(), + mg: &MockManaged{ + ObjectMeta: metav1.ObjectMeta{Name: mgname}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, c: ConnectionDetails(cddata), }, @@ -226,6 +261,7 @@ func TestAPISecretPublisher(t *testing.T) { client: &test.MockClient{ MockGet: func(_ context.Context, n types.NamespacedName, o runtime.Object) error { s := &corev1.Secret{} + s.SetNamespace(corev1.NamespaceDefault) s.SetName(mgcsname) s.SetOwnerReferences([]metav1.OwnerReference{{ Name: mgname, @@ -238,6 +274,7 @@ func TestAPISecretPublisher(t *testing.T) { }, MockUpdate: test.NewMockUpdateFn(nil, func(got runtime.Object) error { want := &corev1.Secret{} + want.SetNamespace(corev1.NamespaceDefault) want.SetName(mgcsname) want.SetOwnerReferences([]metav1.OwnerReference{{ Name: mgname, @@ -258,7 +295,7 @@ func TestAPISecretPublisher(t *testing.T) { ctx: context.Background(), mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, c: ConnectionDetails(cddata), }, @@ -269,6 +306,7 @@ func TestAPISecretPublisher(t *testing.T) { client: &test.MockClient{ MockGet: func(_ context.Context, n types.NamespacedName, o runtime.Object) error { s := &corev1.Secret{} + s.SetNamespace(corev1.NamespaceDefault) s.SetName(mgcsname) s.SetOwnerReferences([]metav1.OwnerReference{{ Name: mgname, @@ -282,6 +320,7 @@ func TestAPISecretPublisher(t *testing.T) { }, MockUpdate: test.NewMockUpdateFn(nil, func(got runtime.Object) error { want := &corev1.Secret{} + want.SetNamespace(corev1.NamespaceDefault) want.SetName(mgcsname) want.SetOwnerReferences([]metav1.OwnerReference{{ Name: mgname, @@ -306,7 +345,7 @@ func TestAPISecretPublisher(t *testing.T) { ctx: context.Background(), mg: &MockManaged{ ObjectMeta: metav1.ObjectMeta{Name: mgname}, - MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: corev1.LocalObjectReference{Name: mgcsname}}, + MockConnectionSecretWriterTo: MockConnectionSecretWriterTo{Ref: &corev1.ObjectReference{Name: mgcsname}}, }, c: ConnectionDetails(cddata), }, diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 2e704ce..330c226 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -28,18 +28,46 @@ import ( "github.com/crossplaneio/crossplane-runtime/pkg/meta" ) -// A ConnectionSecretOwner may create and manage a connection secret. +// A LocalConnectionSecretOwner may create and manage a connection secret in its +// own namespace. +type LocalConnectionSecretOwner interface { + metav1.Object + LocalConnectionSecretWriterTo +} + +// LocalConnectionSecretFor creates a connection secret in the namespace of the +// supplied LocalConnectionSecretOwner, assumed to be of the supplied kind. +func LocalConnectionSecretFor(o LocalConnectionSecretOwner, kind schema.GroupVersionKind) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: o.GetNamespace(), + Name: o.GetWriteConnectionSecretToReference().Name, + OwnerReferences: []metav1.OwnerReference{meta.AsController(meta.ReferenceTo(o, kind))}, + }, + Data: make(map[string][]byte), + } +} + +// A ConnectionSecretOwner may create and manage a connection secret in an +// arbitrary namespace. type ConnectionSecretOwner interface { metav1.Object ConnectionSecretWriterTo } -// ConnectionSecretFor the supplied ConnectionSecretOwner, assumed to be of the -// supplied kind. +// ConnectionSecretFor creates a connection for the supplied +// ConnectionSecretOwner, assumed to be of the supplied kind. The secret is +// written to 'default' namespace if the ConnectionSecretOwner does not specify +// a namespace. func ConnectionSecretFor(o ConnectionSecretOwner, kind schema.GroupVersionKind) *corev1.Secret { + ns := o.GetWriteConnectionSecretToReference().Namespace + if ns == "" { + ns = corev1.NamespaceDefault + } + return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Namespace: o.GetNamespace(), + Namespace: ns, Name: o.GetWriteConnectionSecretToReference().Name, OwnerReferences: []metav1.OwnerReference{meta.AsController(meta.ReferenceTo(o, kind))}, }, diff --git a/pkg/resource/resource_test.go b/pkg/resource/resource_test.go index b39f7d2..70ab5d6 100644 --- a/pkg/resource/resource_test.go +++ b/pkg/resource/resource_test.go @@ -32,10 +32,9 @@ import ( ) const ( - namespace = "coolns" - name = "cool" - secretName = "coolsecret" - uid = types.UID("definitely-a-uuid") + namespace = "coolns" + name = "cool" + uid = types.UID("definitely-a-uuid") ) var MockOwnerGVK = schema.GroupVersionKind{ @@ -44,19 +43,24 @@ var MockOwnerGVK = schema.GroupVersionKind{ Kind: "MockOwner", } -type MockOwner struct { +type MockLocalOwner struct { metav1.ObjectMeta + Ref corev1.LocalObjectReference } -func (m *MockOwner) GetWriteConnectionSecretToReference() corev1.LocalObjectReference { - return corev1.LocalObjectReference{Name: secretName} +func (m *MockLocalOwner) GetWriteConnectionSecretToReference() corev1.LocalObjectReference { + return m.Ref } -func (m *MockOwner) SetWriteConnectionSecretToReference(_ corev1.LocalObjectReference) {} +func (m *MockLocalOwner) SetWriteConnectionSecretToReference(r corev1.LocalObjectReference) { + m.Ref = r +} + +func TestLocalConnectionSecretFor(t *testing.T) { + secretName := "coolsecret" -func TestConnectionSecretFor(t *testing.T) { type args struct { - o ConnectionSecretOwner + o LocalConnectionSecretOwner kind schema.GroupVersionKind } @@ -68,11 +72,14 @@ func TestConnectionSecretFor(t *testing.T) { }{ "Success": { args: args{ - o: &MockOwner{ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - UID: uid, - }}, + o: &MockLocalOwner{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + UID: uid, + }, + Ref: corev1.LocalObjectReference{Name: secretName}, + }, kind: MockOwnerGVK, }, want: &corev1.Secret{ @@ -91,6 +98,98 @@ func TestConnectionSecretFor(t *testing.T) { }, }, } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + got := LocalConnectionSecretFor(tc.args.o, tc.args.kind) + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("LocalConnectionSecretFor(): -want, +got:\n%s", diff) + } + }) + } +} + +type MockOwner struct { + metav1.ObjectMeta + Ref *corev1.ObjectReference +} + +func (m *MockOwner) GetWriteConnectionSecretToReference() *corev1.ObjectReference { + return m.Ref +} + +func (m *MockOwner) SetWriteConnectionSecretToReference(r *corev1.ObjectReference) { + m.Ref = r +} + +func TestConnectionSecretFor(t *testing.T) { + secretName := "coolsecret" + + type args struct { + o ConnectionSecretOwner + kind schema.GroupVersionKind + } + + controller := true + + cases := map[string]struct { + args args + want *corev1.Secret + }{ + "SpecifiedNamespace": { + args: args{ + o: &MockOwner{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + UID: uid, + }, + Ref: &corev1.ObjectReference{Namespace: namespace, Name: secretName}, + }, + kind: MockOwnerGVK, + }, + want: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: secretName, + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: MockOwnerGVK.GroupVersion().String(), + Kind: MockOwnerGVK.Kind, + Name: name, + UID: uid, + Controller: &controller, + }}, + }, + Data: map[string][]byte{}, + }, + }, + "DefaultNamespace": { + args: args{ + o: &MockOwner{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + UID: uid, + }, + Ref: &corev1.ObjectReference{Name: secretName}, + }, + kind: MockOwnerGVK, + }, + want: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: corev1.NamespaceDefault, + Name: secretName, + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: MockOwnerGVK.GroupVersion().String(), + Kind: MockOwnerGVK.Kind, + Name: name, + UID: uid, + Controller: &controller, + }}, + }, + Data: map[string][]byte{}, + }, + }, + } for name, tc := range cases { t.Run(name, func(t *testing.T) { got := ConnectionSecretFor(tc.args.o, tc.args.kind)