From 33d4f33f14787fbd8218044fc708c85de64d945b Mon Sep 17 00:00:00 2001 From: Michael Kanchuker Date: Thu, 16 Jul 2026 21:38:37 +0300 Subject: [PATCH 1/2] feat(runtime): support TLS secret references Add name-only secret references and allow public secret resolution for Kubernetes TLS secrets. --- apis/core/v1alpha1/secret.go | 9 +++++ apis/core/v1alpha1/secret_test.go | 33 ++++++++++++++++++ apis/core/v1alpha1/zz_generated.deepcopy.go | 36 +++++++++++++++++++ pkg/runtime/reconciler.go | 6 ++-- pkg/runtime/reconciler_test.go | 38 ++++++++++++++++++--- 5 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 apis/core/v1alpha1/secret_test.go diff --git a/apis/core/v1alpha1/secret.go b/apis/core/v1alpha1/secret.go index 75fdb44d..5bd83064 100644 --- a/apis/core/v1alpha1/secret.go +++ b/apis/core/v1alpha1/secret.go @@ -26,3 +26,12 @@ type SecretKeyReference struct { // Key is the key within the secret Key string `json:"key"` } + +// SecretReference identifies a Secret without selecting a specific data key. +// It is used when the consumer owns the key convention, such as +// kubernetes.io/tls Secrets with tls.crt and tls.key entries. +type SecretReference struct { + // Empty JSON tag with "inline" attribute is required to properly inline the + // field in JSON output due to k8s apimachinery constraints. + k8scorev1.SecretReference `json:",inline"` +} diff --git a/apis/core/v1alpha1/secret_test.go b/apis/core/v1alpha1/secret_test.go new file mode 100644 index 00000000..45787657 --- /dev/null +++ b/apis/core/v1alpha1/secret_test.go @@ -0,0 +1,33 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 ( + "encoding/json" + "strings" + "testing" +) + +func TestSecretReferenceDoesNotContainKey(t *testing.T) { + ref := SecretReference{} + ref.Name = "tls-secret" + + data, err := json.Marshal(ref) + if err != nil { + t.Fatal(err) + } + if strings.Contains(string(data), `"key"`) { + t.Fatalf("secret reference must not expose a key: %s", data) + } +} diff --git a/apis/core/v1alpha1/zz_generated.deepcopy.go b/apis/core/v1alpha1/zz_generated.deepcopy.go index c21775ee..6450e3dc 100644 --- a/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,20 @@ //go:build !ignore_autogenerated +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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. + +// Code generated by ack-generate. DO NOT EDIT. + // Code generated by controller-gen. DO NOT EDIT. package v1alpha1 @@ -500,6 +515,11 @@ func (in *ResourceMetadata) DeepCopyInto(out *ResourceMetadata) { *out = new(AWSRegion) **out = **in } + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(AWSPartition) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceMetadata. @@ -548,3 +568,19 @@ func (in *SecretKeyReference) DeepCopy() *SecretKeyReference { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretReference) DeepCopyInto(out *SecretReference) { + *out = *in + out.SecretReference = in.SecretReference +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. +func (in *SecretReference) DeepCopy() *SecretReference { + if in == nil { + return nil + } + out := new(SecretReference) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/runtime/reconciler.go b/pkg/runtime/reconciler.go index 625776fb..1e5d590f 100644 --- a/pkg/runtime/reconciler.go +++ b/pkg/runtime/reconciler.go @@ -213,8 +213,10 @@ func (r *reconciler) SecretValueFromReference( return "", ackerr.SecretNotFound } - // Currently we have only Opaque secrets in scope. - if secret.Type != corev1.SecretTypeOpaque { + // ACK secret references support generic Opaque secrets and the standard + // kubernetes.io/tls Secret type. Callers remain responsible for selecting + // an appropriate data key (for example, tls.crt or tls.key). + if secret.Type != corev1.SecretTypeOpaque && secret.Type != corev1.SecretTypeTLS { return "", ackerr.SecretTypeNotSupported } diff --git a/pkg/runtime/reconciler_test.go b/pkg/runtime/reconciler_test.go index b401ba14..acaa309d 100644 --- a/pkg/runtime/reconciler_test.go +++ b/pkg/runtime/reconciler_test.go @@ -2524,11 +2524,12 @@ func secretReconciler( return r, apiReader } -// expectSecretGet wires the mocked apiReader to return an Opaque secret with -// the supplied key/value for a Get against the given namespace/name. -func expectSecretGet( +// expectSecretGet wires the mocked apiReader to return a secret with the +// supplied type and key/value for a Get against the given namespace/name. +func expectSecretGetOfType( apiReader *ctrlrtclientmock.Reader, namespace, name, key, value string, + secretType corev1.SecretType, ) { apiReader.On( "Get", mock.Anything, @@ -2536,11 +2537,22 @@ func expectSecretGet( mock.AnythingOfType("*v1.Secret"), ).Run(func(args mock.Arguments) { secret := args.Get(2).(*corev1.Secret) - secret.Type = corev1.SecretTypeOpaque + secret.Type = secretType secret.Data = map[string][]byte{key: []byte(value)} }).Return(nil) } +func expectSecretGet( + apiReader *ctrlrtclientmock.Reader, + namespace, name, key, value string, +) { + expectSecretGetOfType( + apiReader, + namespace, name, key, value, + corev1.SecretTypeOpaque, + ) +} + func ctxWithNamespace(ns string) context.Context { return context.WithValue(context.Background(), "resourceNamespace", ns) } @@ -2591,6 +2603,24 @@ func TestSecretValueFromReference_ExplicitSameNamespace(t *testing.T) { assert.Equal(t, "value", val) } +func TestSecretValueFromReference_TLSSecret(t *testing.T) { + r, apiReader := secretReconciler(false) + ctx := ctxWithNamespace("ns-a") + expectSecretGetOfType( + apiReader, + "ns-a", "sec", corev1.TLSCertKey, "certificate", + corev1.SecretTypeTLS, + ) + + val, err := r.SecretValueFromReference( + ctx, + newSecretRef("", "sec", corev1.TLSCertKey), + ) + + require.NoError(t, err) + assert.Equal(t, "certificate", val) +} + func TestSecretValueFromReference_CrossNamespace_FlagDisabled(t *testing.T) { r, _ := secretReconciler(false) ctx := ctxWithNamespace("ns-a") From d0eda6d4e1948f4331f531f43e387cb10a1097a3 Mon Sep 17 00:00:00 2001 From: Michael Kanchuker Date: Thu, 27 Aug 2026 14:24:12 +0300 Subject: [PATCH 2/2] Rename SecretReference to TlsSecretReference --- apis/core/v1alpha1/secret.go | 8 ++++---- apis/core/v1alpha1/secret_test.go | 4 ++-- apis/core/v1alpha1/zz_generated.deepcopy.go | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apis/core/v1alpha1/secret.go b/apis/core/v1alpha1/secret.go index 5bd83064..f4c69599 100644 --- a/apis/core/v1alpha1/secret.go +++ b/apis/core/v1alpha1/secret.go @@ -27,10 +27,10 @@ type SecretKeyReference struct { Key string `json:"key"` } -// SecretReference identifies a Secret without selecting a specific data key. -// It is used when the consumer owns the key convention, such as -// kubernetes.io/tls Secrets with tls.crt and tls.key entries. -type SecretReference struct { +// TlsSecretReference identifies a kubernetes.io/tls Secret without selecting +// a specific data key. The consumer owns the key convention (tls.crt and +// tls.key). +type TlsSecretReference struct { // Empty JSON tag with "inline" attribute is required to properly inline the // field in JSON output due to k8s apimachinery constraints. k8scorev1.SecretReference `json:",inline"` diff --git a/apis/core/v1alpha1/secret_test.go b/apis/core/v1alpha1/secret_test.go index 45787657..79067133 100644 --- a/apis/core/v1alpha1/secret_test.go +++ b/apis/core/v1alpha1/secret_test.go @@ -19,8 +19,8 @@ import ( "testing" ) -func TestSecretReferenceDoesNotContainKey(t *testing.T) { - ref := SecretReference{} +func TestTlsSecretReferenceDoesNotContainKey(t *testing.T) { + ref := TlsSecretReference{} ref.Name = "tls-secret" data, err := json.Marshal(ref) diff --git a/apis/core/v1alpha1/zz_generated.deepcopy.go b/apis/core/v1alpha1/zz_generated.deepcopy.go index 6450e3dc..b11c8beb 100644 --- a/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -570,17 +570,17 @@ func (in *SecretKeyReference) DeepCopy() *SecretKeyReference { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SecretReference) DeepCopyInto(out *SecretReference) { +func (in *TlsSecretReference) DeepCopyInto(out *TlsSecretReference) { *out = *in out.SecretReference = in.SecretReference } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. -func (in *SecretReference) DeepCopy() *SecretReference { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TlsSecretReference. +func (in *TlsSecretReference) DeepCopy() *TlsSecretReference { if in == nil { return nil } - out := new(SecretReference) + out := new(TlsSecretReference) in.DeepCopyInto(out) return out }