Skip to content

Commit

Permalink
Add optional key to the FieldExport target
Browse files Browse the repository at this point in the history
FieldExport generates the key name with an output structure like
<namespace>.<FieldExport-resource-name>. This default naming structure
creates non-conflicting names as the keys. However, many applications
expect short names as keys. This is important, especially when the
Secret/ConfigMap is mounted as files; the file names are nothing but
the key names.

This PR adds an optional attribute (.spec.to.key) to specify the key
name in the FieldExport resource. If this attribute is specified,
use this key name instead of the default.

Proposal: aws-controllers-k8s/community#1410

Signed-off-by: Baiju Muthukadan <baiju.m.mail@gmail.com>
  • Loading branch information
baijum committed Aug 23, 2022
1 parent 9b4021b commit 8ca1d3d
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 0 deletions.
1 change: 1 addition & 0 deletions apis/core/v1alpha1/field_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type FieldExportTarget struct {
// Namespace is marked as optional, so we cannot compose `NamespacedName`
Namespace *string `json:"namespace,omitempty"`
Kind FieldExportOutputType `json:"kind"`
Key *string `json:"key,omitempty"`
}

// FieldExportSpec defines the desired state of the FieldExport.
Expand Down
10 changes: 10 additions & 0 deletions apis/core/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions config/crd/bases/services.k8s.aws_fieldexports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ spec:
description: FieldExportTarget provides the values necessary to identify
the output path for a field export.
properties:
key:
type: string
kind:
description: FieldExportOutputType represents all types that can
be produced by a field export operation
Expand Down
6 changes: 6 additions & 0 deletions pkg/runtime/field_export_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ func (r *fieldExportReconciler) writeToConfigMap(
) error {
// Construct the data key
key := fmt.Sprintf("%s.%s", desired.Namespace, desired.Name)
if desired.Spec.To.Key != nil {
key = *desired.Spec.To.Key
}

// Get the initial configmap
nsn := types.NamespacedName{
Expand Down Expand Up @@ -340,6 +343,9 @@ func (r *fieldExportReconciler) writeToSecret(
) error {
// Construct the data key
key := fmt.Sprintf("%s.%s", desired.Namespace, desired.Name)
if desired.Spec.To.Key != nil {
key = *desired.Spec.To.Key
}

// Get the initial secret
nsn := types.NamespacedName{
Expand Down
78 changes: 78 additions & 0 deletions pkg/runtime/field_export_reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,36 @@ func fieldExportWithPath(namespace, name string, kind ackv1alpha1.FieldExportOut
}
}

func fieldExportWithKey(namespace, name string, kind ackv1alpha1.FieldExportOutputType, key string) *ackv1alpha1.FieldExport {
path := ".spec.name"
return &ackv1alpha1.FieldExport{
TypeMeta: v1.TypeMeta{},
ObjectMeta: v1.ObjectMeta{
Namespace: namespace,
Name: name,
Finalizers: []string{"finalizers.services.k8s.aws/FieldExport"},
},
Spec: ackv1alpha1.FieldExportSpec{
From: &ackv1alpha1.ResourceFieldSelector{
Path: &path,
Resource: ackv1alpha1.NamespacedResource{
GroupKind: v1.GroupKind{
Group: BookGVK.Group,
Kind: BookGVK.Kind,
},
Name: strPtr(SourceResourceName),
},
},
To: &ackv1alpha1.FieldExportTarget{
Name: strPtr("fake-export-output"),
Kind: kind,
Key: &key,
},
},
Status: ackv1alpha1.FieldExportStatus{},
}
}

func mockFieldExportList() []ackv1alpha1.FieldExport {
// Matching cases
defaultConfigMap := fieldExportConfigMap(FieldExportNamespace, "export-1")
Expand Down Expand Up @@ -452,6 +482,36 @@ func TestSync_HappyCaseResourceNoExports(t *testing.T) {
require.Len(exports, 0)
}

func TestSync_SetKeyNameExplicitly(t *testing.T) {
// Setup
require := require.New(t)
// Mock resource creation
r, kc, apiReader := mockFieldExportReconciler()
descriptor, res, _ := mockDescriptorAndAWSResource()
manager := mockManager()
fieldExport := fieldExportWithKey(FieldExportNamespace, FieldExportName, ackv1alpha1.FieldExportOutputTypeSecret, "new-key")
sourceResource, _, _ := mockSourceResource()
ctx := context.TODO()
statusWriter := &ctrlrtclientmock.StatusWriter{}

//Mock behavior setup
setupMockClientForFieldExport(kc, statusWriter, ctx, fieldExport)
setupMockApiReaderForFieldExport(apiReader, ctx, res)
setupMockManager(manager, ctx, res)
setupMockDescriptor(descriptor, res)
setupMockUnstructuredConverter()

// Call
latest, err := r.Sync(ctx, sourceResource, *fieldExport)

//Assertions
require.Nil(err)
require.NotNil(latest.Status)
require.Len(latest.Status.Conditions, 0)
assertPatchedConfigMap(false, t, ctx, kc)
assertPatchedSecretWithKey(true, t, ctx, kc, "new-key")
}

// Assertions

func assertPatchedConfigMap(expected bool, t *testing.T, ctx context.Context, kc *ctrlrtclientmock.Client) {
Expand Down Expand Up @@ -492,6 +552,24 @@ func assertPatchedSecret(expected bool, t *testing.T, ctx context.Context, kc *c
}
}

func assertPatchedSecretWithKey(expected bool, t *testing.T, ctx context.Context, kc *ctrlrtclientmock.Client, key string) {
dataMatcher := mock.MatchedBy(func(cm *corev1.Secret) bool {
if cm.Data == nil {
return false
}
val, ok := cm.Data[key]
if !ok {
return false
}
return bytes.Equal(val, []byte("test-book-name"))
})
if expected {
kc.AssertCalled(t, "Patch", ctx, dataMatcher, mock.Anything)
} else {
kc.AssertNotCalled(t, "Patch", ctx, dataMatcher, mock.Anything)
}
}

// assertRecoverableCondition asserts that 'ConditionTypeRecoverable' condition
// is present in the resource's status and that it's value is equal to
// 'conditionStatus' parameter
Expand Down

0 comments on commit 8ca1d3d

Please sign in to comment.