1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00
external-secrets/apis/externalsecrets/v1alpha1/externalsecret_conversion_test.go
Scott Andrews 2174a67575
Make ExternalSecret a provisioned service (#2263)
The Service Binding for Kubernetes project (servicebinding.io) is a spec
to make it easier for workloads to consume services. At runtime, the
ServiceBinding resource references a service resources and workload
resource to connect to the service. The Secret for a service is
projected into a workload resource at a well known path.

Services can advertise the name of the Secret representing the service
on it's status at `.status.binding.name`. Hosting the name of a Secret
at this location is the Provisioned Service duck type. It has the effect
of decoupling the logical consumption of a service from the physical
Secret holding state.

Using ServiceBindings with ExternalSecrets today requires the user to
directly know and reference the Secret created by the ExternalSecret as
the service reference. This PR adds the name of the Secret to the status
of the ExternalSecret at a well known location where it is be discovered
by a ServiceBinding. With this change, user can reference an
ExternalSecret from a ServiceBinding.

A ClusterRole is also added with a well known label for the
ServiceBinding controller to have permission to watch ExternalSecrets
and read the binding Secret.

ClusterExternalSecret was not modified as ServiceBindings are limited to
the scope of a single namespace.

Signed-off-by: Scott Andrews <andrewssc@vmware.com>
2023-05-16 22:06:55 +02:00

227 lines
5.4 KiB
Go

/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
)
const (
keyName = "my-key"
)
func newExternalSecretV1Alpha1() *ExternalSecret {
return &ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Name: "full-es",
Namespace: "my-ns",
},
Status: ExternalSecretStatus{
SyncedResourceVersion: "123",
Conditions: []ExternalSecretStatusCondition{
{
Type: ExternalSecretReady,
Status: corev1.ConditionTrue,
Reason: "it's a mock, it's always ready",
Message: "...why wouldn't it be?",
},
},
Binding: corev1.LocalObjectReference{
Name: "test-target",
},
},
Spec: ExternalSecretSpec{
SecretStoreRef: SecretStoreRef{
Name: "test-secret-store",
Kind: "ClusterSecretStore",
},
Target: ExternalSecretTarget{
Name: "test-target",
CreationPolicy: Owner,
Immutable: false,
Template: &ExternalSecretTemplate{
Type: corev1.SecretTypeOpaque,
Metadata: ExternalSecretTemplateMetadata{
Annotations: map[string]string{
"foo": "bar",
},
Labels: map[string]string{
"foolbl": "barlbl",
},
},
Data: map[string]string{
keyName: "{{.data | toString}}",
},
TemplateFrom: []TemplateFrom{
{
ConfigMap: &TemplateRef{
Name: "test-configmap",
Items: []TemplateRefItem{
{
Key: keyName,
},
},
},
Secret: &TemplateRef{
Name: "test-secret",
Items: []TemplateRefItem{
{
Key: keyName,
},
},
},
},
},
},
},
Data: []ExternalSecretData{
{
SecretKey: keyName,
RemoteRef: ExternalSecretDataRemoteRef{
Key: "datakey",
Property: "dataproperty",
Version: "dataversion",
},
},
},
DataFrom: []ExternalSecretDataRemoteRef{
{
Key: "key",
Property: "property",
Version: "version",
},
},
},
}
}
func newExternalSecretV1Beta1() *esv1beta1.ExternalSecret {
return &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Name: "full-es",
Namespace: "my-ns",
},
Status: esv1beta1.ExternalSecretStatus{
SyncedResourceVersion: "123",
Conditions: []esv1beta1.ExternalSecretStatusCondition{
{
Type: esv1beta1.ExternalSecretReady,
Status: corev1.ConditionTrue,
Reason: "it's a mock, it's always ready",
Message: "...why wouldn't it be?",
},
},
Binding: corev1.LocalObjectReference{
Name: "test-target",
},
},
Spec: esv1beta1.ExternalSecretSpec{
SecretStoreRef: esv1beta1.SecretStoreRef{
Name: "test-secret-store",
Kind: "ClusterSecretStore",
},
Target: esv1beta1.ExternalSecretTarget{
Name: "test-target",
CreationPolicy: esv1beta1.CreatePolicyOwner,
Immutable: false,
Template: &esv1beta1.ExternalSecretTemplate{
Type: corev1.SecretTypeOpaque,
Metadata: esv1beta1.ExternalSecretTemplateMetadata{
Annotations: map[string]string{
"foo": "bar",
},
Labels: map[string]string{
"foolbl": "barlbl",
},
},
Data: map[string]string{
keyName: "{{.data | toString}}",
},
TemplateFrom: []esv1beta1.TemplateFrom{
{
ConfigMap: &esv1beta1.TemplateRef{
Name: "test-configmap",
Items: []esv1beta1.TemplateRefItem{
{
Key: keyName,
},
},
},
Secret: &esv1beta1.TemplateRef{
Name: "test-secret",
Items: []esv1beta1.TemplateRefItem{
{
Key: keyName,
},
},
},
},
},
},
},
Data: []esv1beta1.ExternalSecretData{
{
SecretKey: keyName,
RemoteRef: esv1beta1.ExternalSecretDataRemoteRef{
Key: "datakey",
Property: "dataproperty",
Version: "dataversion",
},
},
},
DataFrom: []esv1beta1.ExternalSecretDataFromRemoteRef{
{
Extract: &esv1beta1.ExternalSecretDataRemoteRef{
Key: "key",
Property: "property",
Version: "version",
},
},
},
},
}
}
func TestExternalSecretConvertFrom(t *testing.T) {
given := newExternalSecretV1Beta1()
want := newExternalSecretV1Alpha1()
got := &ExternalSecret{}
err := got.ConvertFrom(given)
if err != nil {
t.Errorf("test failed with error: %v", err)
}
if !assert.Equal(t, want, got) {
t.Errorf("test failed, expected: %v, got: %v", want, got)
}
}
func TestExternalSecretConvertTo(t *testing.T) {
want := newExternalSecretV1Beta1()
given := newExternalSecretV1Alpha1()
got := &esv1beta1.ExternalSecret{}
err := given.ConvertTo(got)
if err != nil {
t.Errorf("test failed with error: %v", err)
}
if !assert.Equal(t, want, got) {
t.Errorf("test failed, expected: %v, got: %v", want, got)
}
}