From 44b49f77d72960f2042ab96b6d110d628df85e9e Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Fri, 1 May 2026 10:34:04 -0700 Subject: [PATCH 1/9] feat: Add cross-namespace reference validation to generated code Replace the legacy unconditional namespace-override block in ResolveReferencesForField with a call to the shared runtime helper ackrt.ValidateCrossNamespaceReference. This centralizes cross-namespace reference validation so that when --enable-cross-namespace is disabled, references targeting a different namespace are rejected with a terminal error. The generated code captures three return values (namespace, isCrossNs, error) from the helper. The isCrossNs signal is suppressed in generated code since warning conditions are handled centrally by the reconciler. Helm chart templates are updated to expose the --enable-cross-namespace flag (default: true for Phase 1 warning behavior) covering resource references, secret references, and field exports. Changes: - resource_reference.go: emit ValidateCrossNamespaceReference call with EnableCrossNamespace field and three return values - resource_reference_test.go: update all expected output strings to match the new helper call shape - deployment.yaml.tpl: add --enable-cross-namespace flag to container args - values.yaml.tpl: add enableCrossNamespace: true with description - values.schema.json.tpl: add enableCrossNamespace schema entry --- pkg/generate/code/resource_reference.go | 26 ++- pkg/generate/code/resource_reference_test.go | 182 ++++++++++++++++--- pkg/generate/code/set_sdk.go | 61 ++++++- pkg/generate/code/set_sdk_test.go | 175 ++++++++++++++++++ templates/helm/templates/deployment.yaml.tpl | 2 + templates/helm/values.schema.json.tpl | 5 + templates/helm/values.yaml.tpl | 11 ++ templates/pkg/resource/references.go.tpl | 10 + templates/pkg/resource/sdk.go.tpl | 28 +++ 9 files changed, 466 insertions(+), 34 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 6bd84ee4..573445af 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -228,9 +228,29 @@ func ResolveReferencesForField(field *model.Field, sourceVarName string, indentL outPrefix += fmt.Sprintf("%s\treturn hasReferences, fmt.Errorf(\"provided resource reference is nil or empty: %s\")\n", innerIndent, refFieldPath) outPrefix += fmt.Sprintf("%s}\n", innerIndent) - outPrefix += fmt.Sprintf("%snamespace := ko.ObjectMeta.GetNamespace()\n", innerIndent) - outPrefix += fmt.Sprintf("%sif arr.Namespace != nil && *arr.Namespace != \"\" {\n", innerIndent) - outPrefix += fmt.Sprintf("%s\tnamespace = *arr.Namespace\n", innerIndent) + outPrefix += fmt.Sprintf("%snamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference(\n", innerIndent) + outPrefix += fmt.Sprintf("%s\trm.cfg.EnableCrossNamespace,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tko.ObjectMeta.GetNamespace(),\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tarr.Namespace,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t*arr.Name,\n", innerIndent) + outPrefix += fmt.Sprintf("%s)\n", innerIndent) + outPrefix += fmt.Sprintf("%sif err != nil {\n", innerIndent) + outPrefix += fmt.Sprintf("%s\treturn hasReferences, err\n", innerIndent) + outPrefix += fmt.Sprintf("%s}\n", innerIndent) + outPrefix += fmt.Sprintf("%sif isCrossNs {\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tackrtlog.FromContext(ctx).Info(\"cross-namespace resource reference detected; \"+\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"this behavior will be disabled by default in a future release. \"+\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"Set --enable-cross-namespace to preserve this behavior.\",\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"ownerNamespace\", ko.ObjectMeta.GetNamespace(),\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"targetNamespace\", *arr.Namespace,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"referenceName\", *arr.Name,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t)\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tcrossNsMsg := fmt.Sprintf(\"Cross-namespace resource reference detected: \"+\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"resource in namespace %%q references %%q in namespace %%q. \"+\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"Cross-namespace behavior will be disabled by default in a future release. \"+\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\t\"Set --enable-cross-namespace=true to preserve this behavior.\",\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\tko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace)\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tsetCrossNamespaceCondition(ko, crossNsMsg)\n", innerIndent) outPrefix += fmt.Sprintf("%s}\n", innerIndent) outPrefix += getReferencedStateForField(field, innerIndentLevel) diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index c801a536..25a14b21 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -144,9 +144,29 @@ func Test_ResolveReferencesForField_SingleReference(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: APIRef") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -180,9 +200,29 @@ func Test_ResolveReferencesForField_ReferencingARN(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PermissionsBoundaryRef") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -217,9 +257,29 @@ func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SecurityGroupRefs") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &ec2apitypes.SecurityGroup{} if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -258,9 +318,29 @@ func Test_ResolveReferencesForField_NestedSingleReference(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: JWTConfiguration.IssuerRef") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -299,9 +379,29 @@ func Test_ResolveReferencesForField_SingleReference_DeeplyNested(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Logging.LoggingEnabled.TargetBucketRef") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -340,9 +440,29 @@ func Test_ResolveReferencesForField_SingleReference_WithinSlice(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Routes.GatewayRef") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.InternetGateway{} if err := getReferencedResourceState_InternetGateway(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -384,9 +504,29 @@ func Test_ResolveReferencesForField_SingleReference_WithinMultipleSlices(t *test if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Notification.LambdaFunctionConfigurations.Filter.Key.FilterRules.ValueRef") } - namespace := ko.ObjectMeta.GetNamespace() - if arr.Namespace != nil && *arr.Namespace != "" { - namespace = *arr.Namespace + namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + rm.cfg.EnableCrossNamespace, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", ko.ObjectMeta.GetNamespace(), + "targetNamespace", *arr.Namespace, + "referenceName", *arr.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ + "resource in namespace %q references %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) + setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index 583ee2d8..fe767a91 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -1099,26 +1099,30 @@ func setSDKForContainer( // setSDKForSecret returns a string of Go code that sets a target variable to // the value of a Secret when the type of the source variable is a -// SecretKeyReference. +// SecretKeyReference. It first validates cross-namespace access using +// ValidateCrossNamespaceReferenceString, then fetches the secret value. // // The Go code output from this function looks like this: // -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) +// secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( +// rm.cfg.EnableCrossNamespace, +// r.ko.ObjectMeta.GetNamespace(), +// ko.Spec.MasterUserPassword.Namespace, +// ko.Spec.MasterUserPassword.Name, +// ) // if err != nil { -// return nil, ackrequeue.Needed(err) +// return nil, err // } -// if tmpSecret != "" { -// res.SetMasterUserPassword(tmpSecret) +// if isCrossNs { +// // log warning and set condition // } -// -// or: -// -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f3iter) +// ko.Spec.MasterUserPassword.Namespace = secretNamespace +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) // if err != nil { // return nil, ackrequeue.Needed(err) // } // if tmpSecret != "" { -// f3elem = tmpSecret +// res.SetMasterUserPassword(tmpSecret) // } // // The second case is used when the SecretKeyReference field @@ -1140,6 +1144,43 @@ func setSDKForSecret( indent := strings.Repeat("\t", indentLevel) secVar := "tmpSecret" + // Validate cross-namespace access before fetching the secret + // secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + // rm.cfg.EnableCrossNamespace, + // r.ko.ObjectMeta.GetNamespace(), + // sourceVarName.Namespace, + // sourceVarName.Name, + // ) + out += fmt.Sprintf( + "%s\tsecretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString(\n", + indent, + ) + out += fmt.Sprintf("%s\t\trm.cfg.EnableCrossNamespace,\n", indent) + out += fmt.Sprintf("%s\t\tr.ko.ObjectMeta.GetNamespace(),\n", indent) + out += fmt.Sprintf("%s\t\t%s.Namespace,\n", indent, sourceVarName) + out += fmt.Sprintf("%s\t\t%s.Name,\n", indent, sourceVarName) + out += fmt.Sprintf("%s\t)\n", indent) + out += fmt.Sprintf("%s\tif err != nil {\n", indent) + out += fmt.Sprintf("%s\t\treturn nil, err\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + out += fmt.Sprintf("%s\tif isCrossNs {\n", indent) + out += fmt.Sprintf("%s\t\tackrtlog.FromContext(ctx).Info(\"cross-namespace secret reference detected; \"+\n", indent) + out += fmt.Sprintf("%s\t\t\t\"this behavior will be disabled by default in a future release. \"+\n", indent) + out += fmt.Sprintf("%s\t\t\t\"Set --enable-cross-namespace to preserve this behavior.\",\n", indent) + out += fmt.Sprintf("%s\t\t\t\"ownerNamespace\", r.ko.ObjectMeta.GetNamespace(),\n", indent) + out += fmt.Sprintf("%s\t\t\t\"secretNamespace\", %s.Namespace,\n", indent, sourceVarName) + out += fmt.Sprintf("%s\t\t\t\"secretName\", %s.Name,\n", indent, sourceVarName) + out += fmt.Sprintf("%s\t\t)\n", indent) + out += fmt.Sprintf("%s\t\tcrossNsMsg := fmt.Sprintf(\"Cross-namespace secret reference detected: \"+\n", indent) + out += fmt.Sprintf("%s\t\t\t\"resource in namespace %%q references secret %%q in namespace %%q. \"+\n", indent) + out += fmt.Sprintf("%s\t\t\t\"Cross-namespace behavior will be disabled by default in a future release. \"+\n", indent) + out += fmt.Sprintf("%s\t\t\t\"Set --enable-cross-namespace=true to preserve this behavior.\",\n", indent) + out += fmt.Sprintf("%s\t\t\tr.ko.ObjectMeta.GetNamespace(), %s.Name, %s.Namespace)\n", indent, sourceVarName, sourceVarName) + out += fmt.Sprintf("%s\t\tsetCrossNamespaceCondition(r.ko, crossNsMsg)\n", indent) + out += fmt.Sprintf("%s\t}\n", indent) + // Override the secret reference namespace with the validated namespace + out += fmt.Sprintf("%s\t%s.Namespace = secretNamespace\n", indent, sourceVarName) + // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) out += fmt.Sprintf( "%s\t%s, err := rm.rr.SecretValueFromReference(ctx, %s)\n", diff --git a/pkg/generate/code/set_sdk_test.go b/pkg/generate/code/set_sdk_test.go index 595e6300..1a2521af 100644 --- a/pkg/generate/code/set_sdk_test.go +++ b/pkg/generate/code/set_sdk_test.go @@ -105,6 +105,31 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { for _, f1f0iter := range r.ko.Spec.AuthenticationMode.Passwords { var f1f0elem string if f1f0iter != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + f1f0iter.Namespace, + f1f0iter.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", f1f0iter.Namespace, + "secretName", f1f0iter.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), f1f0iter.Name, f1f0iter.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + f1f0iter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f1f0iter) if err != nil { return nil, ackrequeue.Needed(err) @@ -208,6 +233,31 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { f3f4.MasterUserName = r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserName } if r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, + r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, + "secretName", r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) @@ -1489,6 +1539,31 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { res.ManageMasterUserPassword = r.ko.Spec.ManageMasterUserPassword } if r.ko.Spec.MasterUserPassword != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + r.ko.Spec.MasterUserPassword.Namespace, + r.ko.Spec.MasterUserPassword.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", r.ko.Spec.MasterUserPassword.Namespace, + "secretName", r.ko.Spec.MasterUserPassword.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Name, r.ko.Spec.MasterUserPassword.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) @@ -1779,6 +1854,31 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { } if delta.DifferentAt("Spec.MasterUserPassword") { if r.ko.Spec.MasterUserPassword != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + r.ko.Spec.MasterUserPassword.Namespace, + r.ko.Spec.MasterUserPassword.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", r.ko.Spec.MasterUserPassword.Namespace, + "secretName", r.ko.Spec.MasterUserPassword.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Name, r.ko.Spec.MasterUserPassword.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) @@ -2318,6 +2418,31 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { f18elem.Groups = aws.ToStringSlice(f18iter.Groups) } if f18iter.Password != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + f18iter.Password.Namespace, + f18iter.Password.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", f18iter.Password.Namespace, + "secretName", f18iter.Password.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), f18iter.Password.Name, f18iter.Password.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + f18iter.Password.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f18iter.Password) if err != nil { return nil, ackrequeue.Needed(err) @@ -4669,6 +4794,31 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi for f4f0key, f4f0valiter := range r.ko.Spec.Environment.Variables { var f4f0val string if f4f0valiter != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + f4f0valiter.Namespace, + f4f0valiter.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", f4f0valiter.Namespace, + "secretName", f4f0valiter.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), f4f0valiter.Name, f4f0valiter.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + f4f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f4f0valiter) if err != nil { return nil, ackrequeue.Needed(err) @@ -4806,6 +4956,31 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi for f2f0key, f2f0valiter := range r.ko.Spec.Environment.Variables { var f2f0val string if f2f0valiter != nil { + secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + rm.cfg.EnableCrossNamespace, + r.ko.ObjectMeta.GetNamespace(), + f2f0valiter.Namespace, + f2f0valiter.Name, + ) + if err != nil { + return nil, err + } + if isCrossNs { + ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ + "this behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace to preserve this behavior.", + "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), + "secretNamespace", f2f0valiter.Namespace, + "secretName", f2f0valiter.Name, + ) + crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ + "resource in namespace %q references secret %q in namespace %q. "+ + "Cross-namespace behavior will be disabled by default in a future release. "+ + "Set --enable-cross-namespace=true to preserve this behavior.", + r.ko.ObjectMeta.GetNamespace(), f2f0valiter.Name, f2f0valiter.Namespace) + setCrossNamespaceCondition(r.ko, crossNsMsg) + } + f2f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f2f0valiter) if err != nil { return nil, ackrequeue.Needed(err) diff --git a/templates/helm/templates/deployment.yaml.tpl b/templates/helm/templates/deployment.yaml.tpl index a8219bac..1e55f061 100644 --- a/templates/helm/templates/deployment.yaml.tpl +++ b/templates/helm/templates/deployment.yaml.tpl @@ -104,6 +104,8 @@ spec: - "$(FEATURE_GATES)" {{ "{{- end }}" }} - {{ "--enable-carm={{ .Values.enableCARM }}" }} + - {{ "--enable-cross-namespace={{ .Values.enableCrossNamespace }}" }} + - {{ "--enable-cross-namespace-references={{ .Values.enableCrossNamespaceReferences }}" }} image: {{ "{{ .Values.image.repository }}:{{ .Values.image.tag }}" }} imagePullPolicy: {{ "{{ .Values.image.pullPolicy }}" }} name: controller diff --git a/templates/helm/values.schema.json.tpl b/templates/helm/values.schema.json.tpl index dcd0eeb3..cca512c3 100644 --- a/templates/helm/values.schema.json.tpl +++ b/templates/helm/values.schema.json.tpl @@ -280,6 +280,11 @@ "description": "Parameter to enable or disable cross account resource management.", "type": "boolean", "default": true + }, + "enableCrossNamespace": { + "description": "Enable cross-namespace behavior (resource references, secret references, field exports). When false, the controller rejects any operation that crosses namespace boundaries.", + "type": "boolean", + "default": true }, "serviceAccount": { "description": "ServiceAccount settings", diff --git a/templates/helm/values.yaml.tpl b/templates/helm/values.yaml.tpl index 15cf4d09..b69fb7dd 100644 --- a/templates/helm/values.yaml.tpl +++ b/templates/helm/values.yaml.tpl @@ -180,6 +180,17 @@ leaderElection: # Enable Cross Account Resource Management (default = true). Set this to false to disable cross account resource management. enableCARM: true +# Enable cross-namespace behavior including resource references, secret references, +# and field exports (default = true). When false, the controller rejects any operation +# that crosses namespace boundaries. +enableCrossNamespace: true + +# Enables cross-namespace resolution of AWSResourceReference entries. +# When false (default), the controller rejects any reference whose +# Namespace field differs from the namespace of the resource containing +# the reference. +enableCrossNamespaceReferences: false + # Configuration for feature gates. These are optional controller features that # can be individually enabled ("true") or disabled ("false") by adding key/value # pairs below. diff --git a/templates/pkg/resource/references.go.tpl b/templates/pkg/resource/references.go.tpl index 91da645b..0b9c82e1 100644 --- a/templates/pkg/resource/references.go.tpl +++ b/templates/pkg/resource/references.go.tpl @@ -15,6 +15,8 @@ import ( {{ if .CRD.HasReferenceFields -}} ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" {{ end -}} acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" {{ $servicePackageName := .ServicePackageName -}} @@ -31,6 +33,11 @@ import ( ) {{ if .CRD.HasReferenceFields -}} +// Hack to avoid import errors during build... +var ( + _ = &ackrtlog.ResourceLogger{} +) + {{ range $fieldName := .CRD.SortedFieldNames -}} {{ $field := (index $.CRD.Fields $fieldName) -}} {{ if and $field.HasReference (not (eq $field.ReferencedServiceName $servicePackageName)) -}} @@ -134,3 +141,6 @@ func (rm *resourceManager) resolveReferenceFor{{ $field.FieldPathWithUnderscore {{ end -}} {{ end -}} +{{ if .CRD.HasReferenceFields -}} +{{ end -}} + diff --git a/templates/pkg/resource/sdk.go.tpl b/templates/pkg/resource/sdk.go.tpl index 30929e8f..4751395d 100644 --- a/templates/pkg/resource/sdk.go.tpl +++ b/templates/pkg/resource/sdk.go.tpl @@ -15,6 +15,7 @@ import ( ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" "github.com/aws/aws-sdk-go-v2/aws" svcsdk "github.com/aws/aws-sdk-go-v2/service/{{ .ServicePackageName }}" @@ -39,6 +40,7 @@ var ( _ = fmt.Sprintf("") _ = &ackrequeue.NoRequeue{} _ = &aws.Config{} + _ = ackrt.ValidateCrossNamespaceReferenceString ) // sdkFind returns SDK-specific information about a supplied resource @@ -339,3 +341,29 @@ func (rm *resourceManager) terminalAWSError(err error) bool { {{- if $hookCode := Hook .CRD "sdk_file_end" }} {{ $hookCode }} {{- end }} + +// setCrossNamespaceCondition sets the ACK.CrossNamespaceOptInRequired condition +// on the resource to notify users that their cross-namespace usage will require +// explicit opt-in in a future release. +func setCrossNamespaceCondition(ko *svcapitypes.{{ .CRD.Names.Camel }}, message string) { + if ko.Status.Conditions == nil { + ko.Status.Conditions = []*ackv1alpha1.Condition{} + } + var crossNsCond *ackv1alpha1.Condition + for _, c := range ko.Status.Conditions { + if c.Type == ackv1alpha1.ConditionTypeCrossNamespaceOptInRequired { + crossNsCond = c + break + } + } + if crossNsCond == nil { + crossNsCond = &ackv1alpha1.Condition{ + Type: ackv1alpha1.ConditionTypeCrossNamespaceOptInRequired, + } + ko.Status.Conditions = append(ko.Status.Conditions, crossNsCond) + } + now := metav1.Now() + crossNsCond.LastTransitionTime = &now + crossNsCond.Status = corev1.ConditionTrue + crossNsCond.Message = &message +} From 30bdd708ddc94889d99d4d34ac5217ada38f9fc7 Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Wed, 27 May 2026 15:28:07 -0700 Subject: [PATCH 2/9] fix: Address PR review comments from knottnt - Update ResolveReferencesForField function comment to reflect new generated output with ValidateCrossNamespaceReference call, three return values, and cross-namespace warning/condition logic - Remove stale --enable-cross-namespace-references flag from deployment.yaml.tpl (superseded by --enable-cross-namespace) - Remove stale enableCrossNamespaceReferences value from values.yaml.tpl (superseded by enableCrossNamespace) --- pkg/generate/code/resource_reference.go | 53 +++++++++++--------- templates/helm/templates/deployment.yaml.tpl | 1 - templates/helm/values.yaml.tpl | 6 --- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 573445af..1d696707 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -122,6 +122,11 @@ func ReferenceFieldsValidation( // are related to the given concrete field, determining whether its in a valid // condition and updating the concrete field with the referenced value. // +// The generated code calls ackrt.ValidateCrossNamespaceReference to check +// whether the reference targets a different namespace. If the cross-namespace +// flag is disabled, the helper returns an error. If enabled and cross-namespace, +// a warning is logged and a condition is set on the resource. +// // Sample output (resolving a singular reference): // // if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil { @@ -130,6 +135,19 @@ func ReferenceFieldsValidation( // if arr.Name == nil || *arr.Name == "" { // return hasReferences, fmt.Errorf("provided resource reference is nil or empty: APIRef") // } +// namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( +// rm.cfg.EnableCrossNamespace, +// ko.ObjectMeta.GetNamespace(), +// arr.Namespace, +// *arr.Name, +// ) +// if err != nil { +// return hasReferences, err +// } +// if isCrossNs { +// ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; ...") +// setCrossNamespaceCondition(ko, crossNsMsg) +// } // obj := &svcapitypes.API{} // if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { // return hasReferences, err @@ -146,6 +164,16 @@ func ReferenceFieldsValidation( // if arr.Name == nil || *arr.Name == "" { // return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SecurityGroupRefs") // } +// namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( +// rm.cfg.EnableCrossNamespace, +// ko.ObjectMeta.GetNamespace(), +// arr.Namespace, +// *arr.Name, +// ) +// if err != nil { +// return hasReferences, err +// } +// if isCrossNs { ... } // obj := &ec2apitypes.SecurityGroup{} // if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { // return hasReferences, err @@ -156,31 +184,6 @@ func ReferenceFieldsValidation( // ko.Spec.SecurityGroupIDs = append(ko.Spec.SecurityGroupIDs, (*string)(obj.Status.ID)) // } // } -// -// Sample output (resolving nested lists of structs containing references): -// -// if ko.Spec.Notification != nil { -// for f0idx, f0iter := range ko.Spec.Notification.LambdaFunctionConfigurations { -// if f0iter.Filter != nil { -// if f0iter.Filter.Key != nil { -// for f1idx, f1iter := range f0iter.Filter.Key.FilterRules { -// if f1iter.ValueRef != nil && f1iter.ValueRef.From != nil { -// hasReferences = true -// arr := f1iter.ValueRef.From -// if arr.Name == nil || *arr.Name == "" { -// return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Notification.LambdaFunctionConfigurations.Filter.Key.FilterRules.ValueRef") -// } -// obj := &svcapitypes.Bucket{} -// if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { -// return hasReferences, err -// } -// ko.Spec.Notification.LambdaFunctionConfigurations[f0idx].Filter.Key.FilterRules[f1idx].Value = (*string)(obj.Spec.Name) -// } -// } -// } -// } -// } -// } func ResolveReferencesForField(field *model.Field, sourceVarName string, indentLevel int) (string, error) { isListOfRefs := field.ShapeRef.Shape.Type == "list" diff --git a/templates/helm/templates/deployment.yaml.tpl b/templates/helm/templates/deployment.yaml.tpl index 1e55f061..7d478d7f 100644 --- a/templates/helm/templates/deployment.yaml.tpl +++ b/templates/helm/templates/deployment.yaml.tpl @@ -105,7 +105,6 @@ spec: {{ "{{- end }}" }} - {{ "--enable-carm={{ .Values.enableCARM }}" }} - {{ "--enable-cross-namespace={{ .Values.enableCrossNamespace }}" }} - - {{ "--enable-cross-namespace-references={{ .Values.enableCrossNamespaceReferences }}" }} image: {{ "{{ .Values.image.repository }}:{{ .Values.image.tag }}" }} imagePullPolicy: {{ "{{ .Values.image.pullPolicy }}" }} name: controller diff --git a/templates/helm/values.yaml.tpl b/templates/helm/values.yaml.tpl index b69fb7dd..aafedbcf 100644 --- a/templates/helm/values.yaml.tpl +++ b/templates/helm/values.yaml.tpl @@ -185,12 +185,6 @@ enableCARM: true # that crosses namespace boundaries. enableCrossNamespace: true -# Enables cross-namespace resolution of AWSResourceReference entries. -# When false (default), the controller rejects any reference whose -# Namespace field differs from the namespace of the resource containing -# the reference. -enableCrossNamespaceReferences: false - # Configuration for feature gates. These are optional controller features that # can be individually enabled ("true") or disabled ("false") by adding key/value # pairs below. From e944078b0bc4c43fff3e17096da3d69d0420ce66 Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Thu, 28 May 2026 16:49:07 -0700 Subject: [PATCH 3/9] fix: Address review comments from michaelhtm - Replace inlined cross-namespace warning + condition logic with calls to the new ackrt.HandleCrossNamespaceReference runtime helper. This collapses ~14 lines of generated code per cross-namespace check into a single helper call. Applies to both resource references (resource_reference.go) and secret references (set_sdk.go). - Remove the per-package setCrossNamespaceCondition function from the sdk.go.tpl template; the runtime helper now owns the lookup-or-create pattern, avoiding 26 lines of generated code per resource. - Drop the now-unused ackrtlog import and var-block from the references.go.tpl template since the runtime helper handles logging. - Remove the empty {{ if .CRD.HasReferenceFields -}}{{ end -}} block at the bottom of references.go.tpl that was left over from the previous refactor. - Restore the third sample output (resolving nested lists of structs containing references) in the ResolveReferencesForField doc comment, updated to reflect the new generated output. - Update set_sdk_test.go and resource_reference_test.go expected outputs to match the simplified generated code. Requires runtime helpers added in aws-controllers-k8s/runtime PR ( HandleCrossNamespaceReference, SetCrossNamespaceOptInRequired, CrossNamespaceRefKindResource, CrossNamespaceRefKindSecret). --- pkg/generate/code/resource_reference.go | 54 ++++++--- pkg/generate/code/resource_reference_test.go | 112 +++++-------------- pkg/generate/code/set_sdk.go | 24 ++-- pkg/generate/code/set_sdk_test.go | 112 +++++-------------- templates/pkg/resource/references.go.tpl | 10 -- templates/pkg/resource/sdk.go.tpl | 26 ----- 6 files changed, 106 insertions(+), 232 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index 1d696707..e0937900 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -125,7 +125,8 @@ func ReferenceFieldsValidation( // The generated code calls ackrt.ValidateCrossNamespaceReference to check // whether the reference targets a different namespace. If the cross-namespace // flag is disabled, the helper returns an error. If enabled and cross-namespace, -// a warning is logged and a condition is set on the resource. +// ackrt.HandleCrossNamespaceReference logs a deprecation warning and sets the +// ACK.CrossNamespaceOptInRequired condition on the resource. // // Sample output (resolving a singular reference): // @@ -145,8 +146,11 @@ func ReferenceFieldsValidation( // return hasReferences, err // } // if isCrossNs { -// ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; ...") -// setCrossNamespaceCondition(ko, crossNsMsg) +// ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( +// ctx, ko.Status.Conditions, +// ackrt.CrossNamespaceRefKindResource, +// ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, +// ) // } // obj := &svcapitypes.API{} // if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -184,6 +188,34 @@ func ReferenceFieldsValidation( // ko.Spec.SecurityGroupIDs = append(ko.Spec.SecurityGroupIDs, (*string)(obj.Status.ID)) // } // } +// +// Sample output (resolving nested lists of structs containing references): +// +// if ko.Spec.Notification != nil { +// for f0idx, f0iter := range ko.Spec.Notification.LambdaFunctionConfigurations { +// if f0iter.Filter != nil { +// if f0iter.Filter.Key != nil { +// for f1idx, f1iter := range f0iter.Filter.Key.FilterRules { +// if f1iter.ValueRef != nil && f1iter.ValueRef.From != nil { +// hasReferences = true +// arr := f1iter.ValueRef.From +// if arr.Name == nil || *arr.Name == "" { +// return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Notification.LambdaFunctionConfigurations.Filter.Key.FilterRules.ValueRef") +// } +// namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( ... ) +// if err != nil { return hasReferences, err } +// if isCrossNs { ... } +// obj := &svcapitypes.Bucket{} +// if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { +// return hasReferences, err +// } +// ko.Spec.Notification.LambdaFunctionConfigurations[f0idx].Filter.Key.FilterRules[f1idx].Value = (*string)(obj.Spec.Name) +// } +// } +// } +// } +// } +// } func ResolveReferencesForField(field *model.Field, sourceVarName string, indentLevel int) (string, error) { isListOfRefs := field.ShapeRef.Shape.Type == "list" @@ -241,19 +273,11 @@ func ResolveReferencesForField(field *model.Field, sourceVarName string, indentL outPrefix += fmt.Sprintf("%s\treturn hasReferences, err\n", innerIndent) outPrefix += fmt.Sprintf("%s}\n", innerIndent) outPrefix += fmt.Sprintf("%sif isCrossNs {\n", innerIndent) - outPrefix += fmt.Sprintf("%s\tackrtlog.FromContext(ctx).Info(\"cross-namespace resource reference detected; \"+\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"this behavior will be disabled by default in a future release. \"+\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"Set --enable-cross-namespace to preserve this behavior.\",\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"ownerNamespace\", ko.ObjectMeta.GetNamespace(),\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"targetNamespace\", *arr.Namespace,\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"referenceName\", *arr.Name,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tko.Status.Conditions = ackrt.HandleCrossNamespaceReference(\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\tctx, ko.Status.Conditions,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\tackrt.CrossNamespaceRefKindResource,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t\tko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name,\n", innerIndent) outPrefix += fmt.Sprintf("%s\t)\n", innerIndent) - outPrefix += fmt.Sprintf("%s\tcrossNsMsg := fmt.Sprintf(\"Cross-namespace resource reference detected: \"+\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"resource in namespace %%q references %%q in namespace %%q. \"+\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"Cross-namespace behavior will be disabled by default in a future release. \"+\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\t\"Set --enable-cross-namespace=true to preserve this behavior.\",\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\tko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace)\n", innerIndent) - outPrefix += fmt.Sprintf("%s\tsetCrossNamespaceCondition(ko, crossNsMsg)\n", innerIndent) outPrefix += fmt.Sprintf("%s}\n", innerIndent) outPrefix += getReferencedStateForField(field, innerIndentLevel) diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index 25a14b21..7076c601 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -154,19 +154,11 @@ func Test_ResolveReferencesForField_SingleReference(t *testing.T) { return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -210,19 +202,11 @@ func Test_ResolveReferencesForField_ReferencingARN(t *testing.T) { return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -267,19 +251,11 @@ func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) { return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &ec2apitypes.SecurityGroup{} if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -328,19 +304,11 @@ func Test_ResolveReferencesForField_NestedSingleReference(t *testing.T) { return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -389,19 +357,11 @@ func Test_ResolveReferencesForField_SingleReference_DeeplyNested(t *testing.T) { return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -450,19 +410,11 @@ func Test_ResolveReferencesForField_SingleReference_WithinSlice(t *testing.T) { return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.InternetGateway{} if err := getReferencedResourceState_InternetGateway(ctx, apiReader, obj, *arr.Name, namespace); err != nil { @@ -514,19 +466,11 @@ func Test_ResolveReferencesForField_SingleReference_WithinMultipleSlices(t *test return hasReferences, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace resource reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", ko.ObjectMeta.GetNamespace(), - "targetNamespace", *arr.Namespace, - "referenceName", *arr.Name, + ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace resource reference detected: "+ - "resource in namespace %q references %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - ko.ObjectMeta.GetNamespace(), *arr.Name, *arr.Namespace) - setCrossNamespaceCondition(ko, crossNsMsg) } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index fe767a91..50c73d69 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -1114,7 +1114,13 @@ func setSDKForContainer( // return nil, err // } // if isCrossNs { -// // log warning and set condition +// r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( +// ctx, r.ko.Status.Conditions, +// ackrt.CrossNamespaceRefKindSecret, +// r.ko.ObjectMeta.GetNamespace(), +// ko.Spec.MasterUserPassword.Namespace, +// ko.Spec.MasterUserPassword.Name, +// ) // } // ko.Spec.MasterUserPassword.Namespace = secretNamespace // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) @@ -1164,19 +1170,11 @@ func setSDKForSecret( out += fmt.Sprintf("%s\t\treturn nil, err\n", indent) out += fmt.Sprintf("%s\t}\n", indent) out += fmt.Sprintf("%s\tif isCrossNs {\n", indent) - out += fmt.Sprintf("%s\t\tackrtlog.FromContext(ctx).Info(\"cross-namespace secret reference detected; \"+\n", indent) - out += fmt.Sprintf("%s\t\t\t\"this behavior will be disabled by default in a future release. \"+\n", indent) - out += fmt.Sprintf("%s\t\t\t\"Set --enable-cross-namespace to preserve this behavior.\",\n", indent) - out += fmt.Sprintf("%s\t\t\t\"ownerNamespace\", r.ko.ObjectMeta.GetNamespace(),\n", indent) - out += fmt.Sprintf("%s\t\t\t\"secretNamespace\", %s.Namespace,\n", indent, sourceVarName) - out += fmt.Sprintf("%s\t\t\t\"secretName\", %s.Name,\n", indent, sourceVarName) + out += fmt.Sprintf("%s\t\tr.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference(\n", indent) + out += fmt.Sprintf("%s\t\t\tctx, r.ko.Status.Conditions,\n", indent) + out += fmt.Sprintf("%s\t\t\tackrt.CrossNamespaceRefKindSecret,\n", indent) + out += fmt.Sprintf("%s\t\t\tr.ko.ObjectMeta.GetNamespace(), %s.Namespace, %s.Name,\n", indent, sourceVarName, sourceVarName) out += fmt.Sprintf("%s\t\t)\n", indent) - out += fmt.Sprintf("%s\t\tcrossNsMsg := fmt.Sprintf(\"Cross-namespace secret reference detected: \"+\n", indent) - out += fmt.Sprintf("%s\t\t\t\"resource in namespace %%q references secret %%q in namespace %%q. \"+\n", indent) - out += fmt.Sprintf("%s\t\t\t\"Cross-namespace behavior will be disabled by default in a future release. \"+\n", indent) - out += fmt.Sprintf("%s\t\t\t\"Set --enable-cross-namespace=true to preserve this behavior.\",\n", indent) - out += fmt.Sprintf("%s\t\t\tr.ko.ObjectMeta.GetNamespace(), %s.Name, %s.Namespace)\n", indent, sourceVarName, sourceVarName) - out += fmt.Sprintf("%s\t\tsetCrossNamespaceCondition(r.ko, crossNsMsg)\n", indent) out += fmt.Sprintf("%s\t}\n", indent) // Override the secret reference namespace with the validated namespace out += fmt.Sprintf("%s\t%s.Namespace = secretNamespace\n", indent, sourceVarName) diff --git a/pkg/generate/code/set_sdk_test.go b/pkg/generate/code/set_sdk_test.go index 1a2521af..3957fd9d 100644 --- a/pkg/generate/code/set_sdk_test.go +++ b/pkg/generate/code/set_sdk_test.go @@ -115,19 +115,11 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", f1f0iter.Namespace, - "secretName", f1f0iter.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), f1f0iter.Namespace, f1f0iter.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), f1f0iter.Name, f1f0iter.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } f1f0iter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f1f0iter) @@ -243,19 +235,11 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, - "secretName", r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) @@ -1549,19 +1533,11 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", r.ko.Spec.MasterUserPassword.Namespace, - "secretName", r.ko.Spec.MasterUserPassword.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Namespace, r.ko.Spec.MasterUserPassword.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Name, r.ko.Spec.MasterUserPassword.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) @@ -1864,19 +1840,11 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", r.ko.Spec.MasterUserPassword.Namespace, - "secretName", r.ko.Spec.MasterUserPassword.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Namespace, r.ko.Spec.MasterUserPassword.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Name, r.ko.Spec.MasterUserPassword.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) @@ -2428,19 +2396,11 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", f18iter.Password.Namespace, - "secretName", f18iter.Password.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), f18iter.Password.Namespace, f18iter.Password.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), f18iter.Password.Name, f18iter.Password.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } f18iter.Password.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f18iter.Password) @@ -4804,19 +4764,11 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", f4f0valiter.Namespace, - "secretName", f4f0valiter.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), f4f0valiter.Namespace, f4f0valiter.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), f4f0valiter.Name, f4f0valiter.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } f4f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f4f0valiter) @@ -4966,19 +4918,11 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi return nil, err } if isCrossNs { - ackrtlog.FromContext(ctx).Info("cross-namespace secret reference detected; "+ - "this behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace to preserve this behavior.", - "ownerNamespace", r.ko.ObjectMeta.GetNamespace(), - "secretNamespace", f2f0valiter.Namespace, - "secretName", f2f0valiter.Name, + r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( + ctx, r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, + r.ko.ObjectMeta.GetNamespace(), f2f0valiter.Namespace, f2f0valiter.Name, ) - crossNsMsg := fmt.Sprintf("Cross-namespace secret reference detected: "+ - "resource in namespace %q references secret %q in namespace %q. "+ - "Cross-namespace behavior will be disabled by default in a future release. "+ - "Set --enable-cross-namespace=true to preserve this behavior.", - r.ko.ObjectMeta.GetNamespace(), f2f0valiter.Name, f2f0valiter.Namespace) - setCrossNamespaceCondition(r.ko, crossNsMsg) } f2f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f2f0valiter) diff --git a/templates/pkg/resource/references.go.tpl b/templates/pkg/resource/references.go.tpl index 0b9c82e1..6f9506b7 100644 --- a/templates/pkg/resource/references.go.tpl +++ b/templates/pkg/resource/references.go.tpl @@ -16,7 +16,6 @@ import ( ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" - ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" {{ end -}} acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" {{ $servicePackageName := .ServicePackageName -}} @@ -33,11 +32,6 @@ import ( ) {{ if .CRD.HasReferenceFields -}} -// Hack to avoid import errors during build... -var ( - _ = &ackrtlog.ResourceLogger{} -) - {{ range $fieldName := .CRD.SortedFieldNames -}} {{ $field := (index $.CRD.Fields $fieldName) -}} {{ if and $field.HasReference (not (eq $field.ReferencedServiceName $servicePackageName)) -}} @@ -140,7 +134,3 @@ func (rm *resourceManager) resolveReferenceFor{{ $field.FieldPathWithUnderscore {{ end -}} {{ end -}} {{ end -}} - -{{ if .CRD.HasReferenceFields -}} -{{ end -}} - diff --git a/templates/pkg/resource/sdk.go.tpl b/templates/pkg/resource/sdk.go.tpl index 4751395d..faf37f67 100644 --- a/templates/pkg/resource/sdk.go.tpl +++ b/templates/pkg/resource/sdk.go.tpl @@ -341,29 +341,3 @@ func (rm *resourceManager) terminalAWSError(err error) bool { {{- if $hookCode := Hook .CRD "sdk_file_end" }} {{ $hookCode }} {{- end }} - -// setCrossNamespaceCondition sets the ACK.CrossNamespaceOptInRequired condition -// on the resource to notify users that their cross-namespace usage will require -// explicit opt-in in a future release. -func setCrossNamespaceCondition(ko *svcapitypes.{{ .CRD.Names.Camel }}, message string) { - if ko.Status.Conditions == nil { - ko.Status.Conditions = []*ackv1alpha1.Condition{} - } - var crossNsCond *ackv1alpha1.Condition - for _, c := range ko.Status.Conditions { - if c.Type == ackv1alpha1.ConditionTypeCrossNamespaceOptInRequired { - crossNsCond = c - break - } - } - if crossNsCond == nil { - crossNsCond = &ackv1alpha1.Condition{ - Type: ackv1alpha1.ConditionTypeCrossNamespaceOptInRequired, - } - ko.Status.Conditions = append(ko.Status.Conditions, crossNsCond) - } - now := metav1.Now() - crossNsCond.LastTransitionTime = &now - crossNsCond.Status = corev1.ConditionTrue - crossNsCond.Message = &message -} From 77c3c719b8cfa8320f0d4e34597ff6da17ce27e6 Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Fri, 29 May 2026 12:55:14 -0700 Subject: [PATCH 4/9] fix: Use combined ResolveCrossNamespaceReference helper Address review feedback from michaelhtm (#699) to fold the entire validate + handle flow into a single helper call. The generated code no longer needs to inspect or branch on isCrossNs. - resource_reference.go: replace ValidateCrossNamespaceReference + Handle block with single ResolveCrossNamespaceReference call (3 returns -> 2) - set_sdk.go: same simplification for secret references using ResolveCrossNamespaceReferenceString - Update doc comments in both generators to reflect the new output - Update 7 test expectations in resource_reference_test.go and 7 in set_sdk_test.go Requires runtime helpers added in aws-controllers-k8s/runtime#249 (ResolveCrossNamespaceReference, ResolveCrossNamespaceReferenceString). --- pkg/generate/code/resource_reference.go | 49 +++++------- pkg/generate/code/resource_reference_test.go | 84 +++++++------------- pkg/generate/code/set_sdk.go | 40 ++++------ pkg/generate/code/set_sdk_test.go | 84 +++++++------------- 4 files changed, 91 insertions(+), 166 deletions(-) diff --git a/pkg/generate/code/resource_reference.go b/pkg/generate/code/resource_reference.go index e0937900..93024238 100644 --- a/pkg/generate/code/resource_reference.go +++ b/pkg/generate/code/resource_reference.go @@ -122,11 +122,11 @@ func ReferenceFieldsValidation( // are related to the given concrete field, determining whether its in a valid // condition and updating the concrete field with the referenced value. // -// The generated code calls ackrt.ValidateCrossNamespaceReference to check -// whether the reference targets a different namespace. If the cross-namespace -// flag is disabled, the helper returns an error. If enabled and cross-namespace, -// ackrt.HandleCrossNamespaceReference logs a deprecation warning and sets the -// ACK.CrossNamespaceOptInRequired condition on the resource. +// The generated code calls ackrt.ResolveCrossNamespaceReference to validate +// the reference and, when the reference targets a different namespace and +// the cross-namespace flag is enabled, emit a deprecation warning and set +// the ACK.CrossNamespaceOptInRequired condition on the resource. When the +// flag is disabled, the helper returns a terminal error. // // Sample output (resolving a singular reference): // @@ -136,8 +136,11 @@ func ReferenceFieldsValidation( // if arr.Name == nil || *arr.Name == "" { // return hasReferences, fmt.Errorf("provided resource reference is nil or empty: APIRef") // } -// namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( +// namespace, err := ackrt.ResolveCrossNamespaceReference( +// ctx, // rm.cfg.EnableCrossNamespace, +// &ko.Status.Conditions, +// ackrt.CrossNamespaceRefKindResource, // ko.ObjectMeta.GetNamespace(), // arr.Namespace, // *arr.Name, @@ -145,13 +148,6 @@ func ReferenceFieldsValidation( // if err != nil { // return hasReferences, err // } -// if isCrossNs { -// ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( -// ctx, ko.Status.Conditions, -// ackrt.CrossNamespaceRefKindResource, -// ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, -// ) -// } // obj := &svcapitypes.API{} // if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { // return hasReferences, err @@ -168,16 +164,10 @@ func ReferenceFieldsValidation( // if arr.Name == nil || *arr.Name == "" { // return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SecurityGroupRefs") // } -// namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( -// rm.cfg.EnableCrossNamespace, -// ko.ObjectMeta.GetNamespace(), -// arr.Namespace, -// *arr.Name, -// ) +// namespace, err := ackrt.ResolveCrossNamespaceReference( ... ) // if err != nil { // return hasReferences, err // } -// if isCrossNs { ... } // obj := &ec2apitypes.SecurityGroup{} // if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { // return hasReferences, err @@ -202,9 +192,10 @@ func ReferenceFieldsValidation( // if arr.Name == nil || *arr.Name == "" { // return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Notification.LambdaFunctionConfigurations.Filter.Key.FilterRules.ValueRef") // } -// namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( ... ) -// if err != nil { return hasReferences, err } -// if isCrossNs { ... } +// namespace, err := ackrt.ResolveCrossNamespaceReference( ... ) +// if err != nil { +// return hasReferences, err +// } // obj := &svcapitypes.Bucket{} // if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { // return hasReferences, err @@ -263,8 +254,11 @@ func ResolveReferencesForField(field *model.Field, sourceVarName string, indentL outPrefix += fmt.Sprintf("%s\treturn hasReferences, fmt.Errorf(\"provided resource reference is nil or empty: %s\")\n", innerIndent, refFieldPath) outPrefix += fmt.Sprintf("%s}\n", innerIndent) - outPrefix += fmt.Sprintf("%snamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference(\n", innerIndent) + outPrefix += fmt.Sprintf("%snamespace, err := ackrt.ResolveCrossNamespaceReference(\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tctx,\n", innerIndent) outPrefix += fmt.Sprintf("%s\trm.cfg.EnableCrossNamespace,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\t&ko.Status.Conditions,\n", innerIndent) + outPrefix += fmt.Sprintf("%s\tackrt.CrossNamespaceRefKindResource,\n", innerIndent) outPrefix += fmt.Sprintf("%s\tko.ObjectMeta.GetNamespace(),\n", innerIndent) outPrefix += fmt.Sprintf("%s\tarr.Namespace,\n", innerIndent) outPrefix += fmt.Sprintf("%s\t*arr.Name,\n", innerIndent) @@ -272,13 +266,6 @@ func ResolveReferencesForField(field *model.Field, sourceVarName string, indentL outPrefix += fmt.Sprintf("%sif err != nil {\n", innerIndent) outPrefix += fmt.Sprintf("%s\treturn hasReferences, err\n", innerIndent) outPrefix += fmt.Sprintf("%s}\n", innerIndent) - outPrefix += fmt.Sprintf("%sif isCrossNs {\n", innerIndent) - outPrefix += fmt.Sprintf("%s\tko.Status.Conditions = ackrt.HandleCrossNamespaceReference(\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\tctx, ko.Status.Conditions,\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\tackrt.CrossNamespaceRefKindResource,\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t\tko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name,\n", innerIndent) - outPrefix += fmt.Sprintf("%s\t)\n", innerIndent) - outPrefix += fmt.Sprintf("%s}\n", innerIndent) outPrefix += getReferencedStateForField(field, innerIndentLevel) diff --git a/pkg/generate/code/resource_reference_test.go b/pkg/generate/code/resource_reference_test.go index 7076c601..34e6d0d7 100644 --- a/pkg/generate/code/resource_reference_test.go +++ b/pkg/generate/code/resource_reference_test.go @@ -144,8 +144,11 @@ func Test_ResolveReferencesForField_SingleReference(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: APIRef") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -153,13 +156,6 @@ func Test_ResolveReferencesForField_SingleReference(t *testing.T) { if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err @@ -192,8 +188,11 @@ func Test_ResolveReferencesForField_ReferencingARN(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PermissionsBoundaryRef") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -201,13 +200,6 @@ func Test_ResolveReferencesForField_ReferencingARN(t *testing.T) { if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err @@ -241,8 +233,11 @@ func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SecurityGroupRefs") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -250,13 +245,6 @@ func Test_ResolveReferencesForField_SliceOfReferences(t *testing.T) { if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &ec2apitypes.SecurityGroup{} if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err @@ -294,8 +282,11 @@ func Test_ResolveReferencesForField_NestedSingleReference(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: JWTConfiguration.IssuerRef") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -303,13 +294,6 @@ func Test_ResolveReferencesForField_NestedSingleReference(t *testing.T) { if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &svcapitypes.API{} if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err @@ -347,8 +331,11 @@ func Test_ResolveReferencesForField_SingleReference_DeeplyNested(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Logging.LoggingEnabled.TargetBucketRef") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -356,13 +343,6 @@ func Test_ResolveReferencesForField_SingleReference_DeeplyNested(t *testing.T) { if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err @@ -400,8 +380,11 @@ func Test_ResolveReferencesForField_SingleReference_WithinSlice(t *testing.T) { if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Routes.GatewayRef") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -409,13 +392,6 @@ func Test_ResolveReferencesForField_SingleReference_WithinSlice(t *testing.T) { if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &svcapitypes.InternetGateway{} if err := getReferencedResourceState_InternetGateway(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err @@ -456,8 +432,11 @@ func Test_ResolveReferencesForField_SingleReference_WithinMultipleSlices(t *test if arr.Name == nil || *arr.Name == "" { return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Notification.LambdaFunctionConfigurations.Filter.Key.FilterRules.ValueRef") } - namespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReference( + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, ko.ObjectMeta.GetNamespace(), arr.Namespace, *arr.Name, @@ -465,13 +444,6 @@ func Test_ResolveReferencesForField_SingleReference_WithinMultipleSlices(t *test if err != nil { return hasReferences, err } - if isCrossNs { - ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, ko.Status.Conditions, - ackrt.CrossNamespaceRefKindResource, - ko.ObjectMeta.GetNamespace(), *arr.Namespace, *arr.Name, - ) - } obj := &svcapitypes.Bucket{} if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return hasReferences, err diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index 50c73d69..c18ef0fc 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -1099,13 +1099,17 @@ func setSDKForContainer( // setSDKForSecret returns a string of Go code that sets a target variable to // the value of a Secret when the type of the source variable is a -// SecretKeyReference. It first validates cross-namespace access using -// ValidateCrossNamespaceReferenceString, then fetches the secret value. +// SecretKeyReference. It first calls ResolveCrossNamespaceReferenceString to +// validate cross-namespace access (and emit a deprecation warning + condition +// when the reference is cross-namespace), then fetches the secret value. // // The Go code output from this function looks like this: // -// secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( +// secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( +// ctx, // rm.cfg.EnableCrossNamespace, +// &r.ko.Status.Conditions, +// ackrt.CrossNamespaceRefKindSecret, // r.ko.ObjectMeta.GetNamespace(), // ko.Spec.MasterUserPassword.Namespace, // ko.Spec.MasterUserPassword.Name, @@ -1113,15 +1117,6 @@ func setSDKForContainer( // if err != nil { // return nil, err // } -// if isCrossNs { -// r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( -// ctx, r.ko.Status.Conditions, -// ackrt.CrossNamespaceRefKindSecret, -// r.ko.ObjectMeta.GetNamespace(), -// ko.Spec.MasterUserPassword.Namespace, -// ko.Spec.MasterUserPassword.Name, -// ) -// } // ko.Spec.MasterUserPassword.Namespace = secretNamespace // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) // if err != nil { @@ -1150,18 +1145,24 @@ func setSDKForSecret( indent := strings.Repeat("\t", indentLevel) secVar := "tmpSecret" - // Validate cross-namespace access before fetching the secret - // secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + // Resolve cross-namespace access before fetching the secret + // secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + // ctx, // rm.cfg.EnableCrossNamespace, + // &r.ko.Status.Conditions, + // ackrt.CrossNamespaceRefKindSecret, // r.ko.ObjectMeta.GetNamespace(), // sourceVarName.Namespace, // sourceVarName.Name, // ) out += fmt.Sprintf( - "%s\tsecretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString(\n", + "%s\tsecretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString(\n", indent, ) + out += fmt.Sprintf("%s\t\tctx,\n", indent) out += fmt.Sprintf("%s\t\trm.cfg.EnableCrossNamespace,\n", indent) + out += fmt.Sprintf("%s\t\t&r.ko.Status.Conditions,\n", indent) + out += fmt.Sprintf("%s\t\tackrt.CrossNamespaceRefKindSecret,\n", indent) out += fmt.Sprintf("%s\t\tr.ko.ObjectMeta.GetNamespace(),\n", indent) out += fmt.Sprintf("%s\t\t%s.Namespace,\n", indent, sourceVarName) out += fmt.Sprintf("%s\t\t%s.Name,\n", indent, sourceVarName) @@ -1169,14 +1170,7 @@ func setSDKForSecret( out += fmt.Sprintf("%s\tif err != nil {\n", indent) out += fmt.Sprintf("%s\t\treturn nil, err\n", indent) out += fmt.Sprintf("%s\t}\n", indent) - out += fmt.Sprintf("%s\tif isCrossNs {\n", indent) - out += fmt.Sprintf("%s\t\tr.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference(\n", indent) - out += fmt.Sprintf("%s\t\t\tctx, r.ko.Status.Conditions,\n", indent) - out += fmt.Sprintf("%s\t\t\tackrt.CrossNamespaceRefKindSecret,\n", indent) - out += fmt.Sprintf("%s\t\t\tr.ko.ObjectMeta.GetNamespace(), %s.Namespace, %s.Name,\n", indent, sourceVarName, sourceVarName) - out += fmt.Sprintf("%s\t\t)\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - // Override the secret reference namespace with the validated namespace + // Override the secret reference namespace with the resolved namespace out += fmt.Sprintf("%s\t%s.Namespace = secretNamespace\n", indent, sourceVarName) // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) diff --git a/pkg/generate/code/set_sdk_test.go b/pkg/generate/code/set_sdk_test.go index 3957fd9d..46af3454 100644 --- a/pkg/generate/code/set_sdk_test.go +++ b/pkg/generate/code/set_sdk_test.go @@ -105,8 +105,11 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { for _, f1f0iter := range r.ko.Spec.AuthenticationMode.Passwords { var f1f0elem string if f1f0iter != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), f1f0iter.Namespace, f1f0iter.Name, @@ -114,13 +117,6 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), f1f0iter.Namespace, f1f0iter.Name, - ) - } f1f0iter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f1f0iter) if err != nil { @@ -225,8 +221,11 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { f3f4.MasterUserName = r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserName } if r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, @@ -234,13 +233,6 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, - ) - } r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) if err != nil { @@ -1523,8 +1515,11 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { res.ManageMasterUserPassword = r.ko.Spec.ManageMasterUserPassword } if r.ko.Spec.MasterUserPassword != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Namespace, r.ko.Spec.MasterUserPassword.Name, @@ -1532,13 +1527,6 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Namespace, r.ko.Spec.MasterUserPassword.Name, - ) - } r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { @@ -1830,8 +1818,11 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { } if delta.DifferentAt("Spec.MasterUserPassword") { if r.ko.Spec.MasterUserPassword != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Namespace, r.ko.Spec.MasterUserPassword.Name, @@ -1839,13 +1830,6 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), r.ko.Spec.MasterUserPassword.Namespace, r.ko.Spec.MasterUserPassword.Name, - ) - } r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { @@ -2386,8 +2370,11 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { f18elem.Groups = aws.ToStringSlice(f18iter.Groups) } if f18iter.Password != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), f18iter.Password.Namespace, f18iter.Password.Name, @@ -2395,13 +2382,6 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), f18iter.Password.Namespace, f18iter.Password.Name, - ) - } f18iter.Password.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f18iter.Password) if err != nil { @@ -4754,8 +4734,11 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi for f4f0key, f4f0valiter := range r.ko.Spec.Environment.Variables { var f4f0val string if f4f0valiter != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), f4f0valiter.Namespace, f4f0valiter.Name, @@ -4763,13 +4746,6 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), f4f0valiter.Namespace, f4f0valiter.Name, - ) - } f4f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f4f0valiter) if err != nil { @@ -4908,8 +4884,11 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi for f2f0key, f2f0valiter := range r.ko.Spec.Environment.Variables { var f2f0val string if f2f0valiter != nil { - secretNamespace, isCrossNs, err := ackrt.ValidateCrossNamespaceReferenceString( + secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( + ctx, rm.cfg.EnableCrossNamespace, + &r.ko.Status.Conditions, + ackrt.CrossNamespaceRefKindSecret, r.ko.ObjectMeta.GetNamespace(), f2f0valiter.Namespace, f2f0valiter.Name, @@ -4917,13 +4896,6 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi if err != nil { return nil, err } - if isCrossNs { - r.ko.Status.Conditions = ackrt.HandleCrossNamespaceReference( - ctx, r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), f2f0valiter.Namespace, f2f0valiter.Name, - ) - } f2f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f2f0valiter) if err != nil { From eab25a9e820dbf3d6afbf6f7f513cd7e27e9f3b9 Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Wed, 3 Jun 2026 16:57:00 -0700 Subject: [PATCH 5/9] chore: Bump runtime to cross-namespace helper commit Point at runtime pseudo-version v0.59.2-0.20260603224255-14be98880374 (commit 14be988, PR #249) which adds the ResolveCrossNamespaceReference and ResolveCrossNamespaceReferenceString helpers that generated code now calls. This is a temporary pin for testing; revert to the tagged release (v0.60.0) once it is published upstream. --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index ddf68a39..c684db7d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.0 require ( github.com/aws-controllers-k8s/pkg v0.0.23 - github.com/aws-controllers-k8s/runtime v0.59.1 + github.com/aws-controllers-k8s/runtime v0.59.2-0.20260603224255-14be98880374 github.com/aws/aws-sdk-go v1.49.0 github.com/aws/aws-sdk-go-v2 v1.32.7 github.com/dlclark/regexp2 v1.10.0 // indirect diff --git a/go.sum b/go.sum index 904359f4..fe2a7c2f 100644 --- a/go.sum +++ b/go.sum @@ -64,10 +64,8 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws-controllers-k8s/pkg v0.0.23 h1:iqu8jKQUnyP/c6TiVcXySQYpkATui0iXFC5ax9x01oM= github.com/aws-controllers-k8s/pkg v0.0.23/go.mod h1:VvdjLWmR6IJ3KU8KByKiq/lJE8M+ur2piXysXKTGUS0= -github.com/aws-controllers-k8s/runtime v0.59.0 h1:VECkUXw3e8WLQo52o3Mw0u1WOrfAzugv8A5fFoaEhxU= -github.com/aws-controllers-k8s/runtime v0.59.0/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= -github.com/aws-controllers-k8s/runtime v0.59.1 h1:7UDKl9/dif8oNjxx/5Z5ciUfuyn86MS4BvoG9LqF6h4= -github.com/aws-controllers-k8s/runtime v0.59.1/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= +github.com/aws-controllers-k8s/runtime v0.59.2-0.20260603224255-14be98880374 h1:qeu0+TU976/aCVWA9rGG2UTbOkvRkA6HfG+4ZnNd2Lc= +github.com/aws-controllers-k8s/runtime v0.59.2-0.20260603224255-14be98880374/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= github.com/aws/aws-sdk-go v1.49.0 h1:g9BkW1fo9GqKfwg2+zCD+TW/D36Ux+vtfJ8guF4AYmY= github.com/aws/aws-sdk-go v1.49.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= From f0dc0d8eb3a28973285f7e2a26c6801e403651ee Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Thu, 4 Jun 2026 16:59:35 -0700 Subject: [PATCH 6/9] fix: Move secret cross-namespace validation into runtime Drop the per-call secret validation block from generated sdk.go. The generated code only wrapped SecretValueFromReference call sites in sdk.go, missing custom update functions (e.g. RDS db_cluster) and hooks that call SecretValueFromReference directly. Validation now lives entirely in the runtime's SecretValueFromReference (aws-controllers-k8s/runtime), so every caller is covered without generated boilerplate. - set_sdk.go: generated secret code reverts to a plain SecretValueFromReference call; no ResolveCrossNamespaceReferenceString - sdk.go.tpl: drop the now-unused ackrt import and its suppression entry - set_sdk_test.go: remove the validation block from 7 expected outputs Addresses review feedback from knottnt on #699. --- pkg/generate/code/set_sdk.go | 53 ++++-------------- pkg/generate/code/set_sdk_test.go | 91 ------------------------------- templates/pkg/resource/sdk.go.tpl | 2 - 3 files changed, 10 insertions(+), 136 deletions(-) diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index c18ef0fc..1152819a 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -1099,25 +1099,15 @@ func setSDKForContainer( // setSDKForSecret returns a string of Go code that sets a target variable to // the value of a Secret when the type of the source variable is a -// SecretKeyReference. It first calls ResolveCrossNamespaceReferenceString to -// validate cross-namespace access (and emit a deprecation warning + condition -// when the reference is cross-namespace), then fetches the secret value. +// SecretKeyReference. +// +// Cross-namespace validation (and the Phase 1 deprecation warning) is +// performed inside the runtime's SecretValueFromReference, so it is not +// emitted here. This ensures every caller is covered, including custom +// update functions and hooks that call SecretValueFromReference directly. // // The Go code output from this function looks like this: // -// secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( -// ctx, -// rm.cfg.EnableCrossNamespace, -// &r.ko.Status.Conditions, -// ackrt.CrossNamespaceRefKindSecret, -// r.ko.ObjectMeta.GetNamespace(), -// ko.Spec.MasterUserPassword.Namespace, -// ko.Spec.MasterUserPassword.Name, -// ) -// if err != nil { -// return nil, err -// } -// ko.Spec.MasterUserPassword.Namespace = secretNamespace // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) // if err != nil { // return nil, ackrequeue.Needed(err) @@ -1145,33 +1135,10 @@ func setSDKForSecret( indent := strings.Repeat("\t", indentLevel) secVar := "tmpSecret" - // Resolve cross-namespace access before fetching the secret - // secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - // ctx, - // rm.cfg.EnableCrossNamespace, - // &r.ko.Status.Conditions, - // ackrt.CrossNamespaceRefKindSecret, - // r.ko.ObjectMeta.GetNamespace(), - // sourceVarName.Namespace, - // sourceVarName.Name, - // ) - out += fmt.Sprintf( - "%s\tsecretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString(\n", - indent, - ) - out += fmt.Sprintf("%s\t\tctx,\n", indent) - out += fmt.Sprintf("%s\t\trm.cfg.EnableCrossNamespace,\n", indent) - out += fmt.Sprintf("%s\t\t&r.ko.Status.Conditions,\n", indent) - out += fmt.Sprintf("%s\t\tackrt.CrossNamespaceRefKindSecret,\n", indent) - out += fmt.Sprintf("%s\t\tr.ko.ObjectMeta.GetNamespace(),\n", indent) - out += fmt.Sprintf("%s\t\t%s.Namespace,\n", indent, sourceVarName) - out += fmt.Sprintf("%s\t\t%s.Name,\n", indent, sourceVarName) - out += fmt.Sprintf("%s\t)\n", indent) - out += fmt.Sprintf("%s\tif err != nil {\n", indent) - out += fmt.Sprintf("%s\t\treturn nil, err\n", indent) - out += fmt.Sprintf("%s\t}\n", indent) - // Override the secret reference namespace with the resolved namespace - out += fmt.Sprintf("%s\t%s.Namespace = secretNamespace\n", indent, sourceVarName) + // Cross-namespace validation for the secret reference is performed inside + // the runtime's SecretValueFromReference, so that every call site is + // covered (including custom update functions and hooks). No per-call + // validation is generated here. // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) out += fmt.Sprintf( diff --git a/pkg/generate/code/set_sdk_test.go b/pkg/generate/code/set_sdk_test.go index 46af3454..595e6300 100644 --- a/pkg/generate/code/set_sdk_test.go +++ b/pkg/generate/code/set_sdk_test.go @@ -105,19 +105,6 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { for _, f1f0iter := range r.ko.Spec.AuthenticationMode.Passwords { var f1f0elem string if f1f0iter != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - f1f0iter.Namespace, - f1f0iter.Name, - ) - if err != nil { - return nil, err - } - f1f0iter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f1f0iter) if err != nil { return nil, ackrequeue.Needed(err) @@ -221,19 +208,6 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { f3f4.MasterUserName = r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserName } if r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace, - r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Name, - ) - if err != nil { - return nil, err - } - r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) @@ -1515,19 +1489,6 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { res.ManageMasterUserPassword = r.ko.Spec.ManageMasterUserPassword } if r.ko.Spec.MasterUserPassword != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - r.ko.Spec.MasterUserPassword.Namespace, - r.ko.Spec.MasterUserPassword.Name, - ) - if err != nil { - return nil, err - } - r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) @@ -1818,19 +1779,6 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { } if delta.DifferentAt("Spec.MasterUserPassword") { if r.ko.Spec.MasterUserPassword != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - r.ko.Spec.MasterUserPassword.Namespace, - r.ko.Spec.MasterUserPassword.Name, - ) - if err != nil { - return nil, err - } - r.ko.Spec.MasterUserPassword.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) @@ -2370,19 +2318,6 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { f18elem.Groups = aws.ToStringSlice(f18iter.Groups) } if f18iter.Password != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - f18iter.Password.Namespace, - f18iter.Password.Name, - ) - if err != nil { - return nil, err - } - f18iter.Password.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f18iter.Password) if err != nil { return nil, ackrequeue.Needed(err) @@ -4734,19 +4669,6 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi for f4f0key, f4f0valiter := range r.ko.Spec.Environment.Variables { var f4f0val string if f4f0valiter != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - f4f0valiter.Namespace, - f4f0valiter.Name, - ) - if err != nil { - return nil, err - } - f4f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f4f0valiter) if err != nil { return nil, ackrequeue.Needed(err) @@ -4884,19 +4806,6 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi for f2f0key, f2f0valiter := range r.ko.Spec.Environment.Variables { var f2f0val string if f2f0valiter != nil { - secretNamespace, err := ackrt.ResolveCrossNamespaceReferenceString( - ctx, - rm.cfg.EnableCrossNamespace, - &r.ko.Status.Conditions, - ackrt.CrossNamespaceRefKindSecret, - r.ko.ObjectMeta.GetNamespace(), - f2f0valiter.Namespace, - f2f0valiter.Name, - ) - if err != nil { - return nil, err - } - f2f0valiter.Namespace = secretNamespace tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f2f0valiter) if err != nil { return nil, ackrequeue.Needed(err) diff --git a/templates/pkg/resource/sdk.go.tpl b/templates/pkg/resource/sdk.go.tpl index faf37f67..30929e8f 100644 --- a/templates/pkg/resource/sdk.go.tpl +++ b/templates/pkg/resource/sdk.go.tpl @@ -15,7 +15,6 @@ import ( ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" - ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" "github.com/aws/aws-sdk-go-v2/aws" svcsdk "github.com/aws/aws-sdk-go-v2/service/{{ .ServicePackageName }}" @@ -40,7 +39,6 @@ var ( _ = fmt.Sprintf("") _ = &ackrequeue.NoRequeue{} _ = &aws.Config{} - _ = ackrt.ValidateCrossNamespaceReferenceString ) // sdkFind returns SDK-specific information about a supplied resource From 6c7b652f0d0bba090a5486c4c4726ffc407cfd9e Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Sun, 7 Jun 2026 02:38:05 -0700 Subject: [PATCH 7/9] feat: Pass owning resource to SecretValueFromReference for cross-namespace notice The runtime's SecretValueFromReference now takes the owning resource as an explicit acktypes.ConditionManager argument so it can set the cross-namespace deprecation ACK.Advisory condition on it (runtime mechanism 1b, replacing the context-stash). Update the generated SetSDK code to pass the resource handle: rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) The resource variable is threaded through setSDKForContainer / SetSDKForStruct / setSDKForSlice / setSDKForMap / setSDKForUnion so secrets nested in collections still pass the top-level resource (not a loop iterator); at entry points it is derived from the leading token of the source variable path. The generated *resource type implements acktypes.ConditionManager. Companion to the runtime change; builds on #699. --- pkg/generate/ack/controller.go | 4 +- pkg/generate/code/set_sdk.go | 82 +++++++++++++++++++++++++++---- pkg/generate/code/set_sdk_test.go | 20 ++++---- 3 files changed, 86 insertions(+), 20 deletions(-) diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index 5beab739..eca18562 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -124,7 +124,9 @@ var ( return code.SetSDK(r.Config(), r, ackmodel.OpTypeDelete, sourceVarName, targetVarName, indentLevel) }, "GoCodeSetSDKForStruct": func(r *ackmodel.CRD, targetFieldName string, targetVarName string, targetShapeRef *awssdkmodel.ShapeRef, sourceFieldPath string, sourceVarName string, indentLevel int) (string, error) { - return code.SetSDKForStruct(r.Config(), r, targetFieldName, targetVarName, targetShapeRef, sourceFieldPath, sourceVarName, model.OpTypeList, indentLevel) + // Empty resourceVarName: SetSDKForStruct derives it from the + // leading token of sourceVarName when invoked as an entry point. + return code.SetSDKForStruct(r.Config(), r, "", targetFieldName, targetVarName, targetShapeRef, sourceFieldPath, sourceVarName, model.OpTypeList, indentLevel) }, "GoCodeSetResourceForStruct": func(r *ackmodel.CRD, targetFieldName string, targetVarName string, targetShapeRef *awssdkmodel.ShapeRef, sourceVarName string, sourceShapeRef *awssdkmodel.ShapeRef, indentLevel int) (string, error) { var setCfg *ackgenconfig.SetFieldConfig = nil diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index 1152819a..40581c91 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -119,6 +119,16 @@ func SetSDK( out := "\n" indent := strings.Repeat("\t", indentLevel) + // The owning resource variable, used so secret fields can pass it to the + // runtime's SecretValueFromReference (which sets the cross-namespace + // deprecation ACK.Advisory condition on it). It is the leading token of + // the source variable path (e.g. "r" from "r.ko"); the generated + // *resource type implements acktypes.ConditionManager. + resourceVarName := sourceVarName + if dot := strings.IndexByte(resourceVarName, '.'); dot != -1 { + resourceVarName = resourceVarName[:dot] + } + // Check if there's an input wrapper field path configured. If so, we need // to create a wrapper struct and populate its fields from the CRD spec, // then assign the wrapper to the input shape's wrapper field. @@ -402,6 +412,7 @@ func SetSDK( ) containerOut, err := setSDKForContainer( cfg, r, + resourceVarName, memberName, memberVarName, sourceFieldPath, @@ -430,6 +441,7 @@ func SetSDK( if r.IsSecretField(memberName) { out += setSDKForSecret( cfg, r, + resourceVarName, memberName, targetVarName, sourceAdaptedVarName, @@ -1005,6 +1017,10 @@ func setSDKReadMany( func setSDKForContainer( cfg *ackgenconfig.Config, r *model.CRD, + // The name of the variable holding the owning resource (a *resource, + // which implements acktypes.ConditionManager). Threaded down so secret + // fields nested in collections can pass it to SecretValueFromReference. + resourceVarName string, // The name of the SDK Input shape member we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1023,6 +1039,7 @@ func setSDKForContainer( case "structure": return SetSDKForStruct( cfg, r, + resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1034,6 +1051,7 @@ func setSDKForContainer( case "list": return setSDKForSlice( cfg, r, + resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1045,6 +1063,7 @@ func setSDKForContainer( case "map": return setSDKForMap( cfg, r, + resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1056,6 +1075,7 @@ func setSDKForContainer( case "union": return setSDKForUnion( cfg, r, + resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1074,6 +1094,7 @@ func setSDKForContainer( ) out += setSDKForSecret( cfg, r, + resourceVarName, "", targetVarName, sourceVarName, @@ -1101,14 +1122,17 @@ func setSDKForContainer( // the value of a Secret when the type of the source variable is a // SecretKeyReference. // -// Cross-namespace validation (and the Phase 1 deprecation warning) is -// performed inside the runtime's SecretValueFromReference, so it is not -// emitted here. This ensures every caller is covered, including custom -// update functions and hooks that call SecretValueFromReference directly. +// Cross-namespace validation (and the Phase 1 deprecation notice) is performed +// inside the runtime's SecretValueFromReference, so it is not emitted here. +// This ensures every caller is covered, including custom update functions and +// hooks that call SecretValueFromReference directly. The owning resource is +// passed as the second argument so the runtime can set the cross-namespace +// deprecation ACK.Advisory condition on it; it is derived from the source +// variable (e.g. "r" from "r.ko.Spec.MasterUserPassword"). // // The Go code output from this function looks like this: // -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1122,6 +1146,11 @@ func setSDKForContainer( func setSDKForSecret( cfg *ackgenconfig.Config, r *model.CRD, + // The name of the variable holding the owning resource (a + // *resource, which implements acktypes.ConditionManager). It is passed to + // SecretValueFromReference so the runtime can set the cross-namespace + // deprecation ACK.Advisory condition on it. Typically "r" or "desired". + resourceVarName string, // The name of the SDK Shape field we're setting targetFieldName string, // The variable name that we want to set a value on @@ -1138,12 +1167,14 @@ func setSDKForSecret( // Cross-namespace validation for the secret reference is performed inside // the runtime's SecretValueFromReference, so that every call site is // covered (including custom update functions and hooks). No per-call - // validation is generated here. + // validation is generated here. The owning resource is passed so the + // runtime can set the cross-namespace deprecation ACK.Advisory condition + // on it. - // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) + // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) out += fmt.Sprintf( - "%s\t%s, err := rm.rr.SecretValueFromReference(ctx, %s)\n", - indent, secVar, sourceVarName, + "%s\t%s, err := rm.rr.SecretValueFromReference(ctx, %s, %s)\n", + indent, secVar, resourceVarName, sourceVarName, ) // if err != nil { // return nil, ackrequeue.Needed(err) @@ -1175,6 +1206,12 @@ func setSDKForSecret( func SetSDKForStruct( cfg *ackgenconfig.Config, r *model.CRD, + // The name of the variable holding the owning resource (a *resource, + // which implements acktypes.ConditionManager). Threaded down so secret + // fields nested in this struct can pass it to SecretValueFromReference. + // When this is the entry point (e.g. the GoCodeSetSDKForStruct template + // helper), it is derived from the leading token of sourceVarName. + resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1191,6 +1228,16 @@ func SetSDKForStruct( out := "" indent := strings.Repeat("\t", indentLevel) targetShape := targetShapeRef.Shape + // When SetSDKForStruct is invoked as a top-level entry point (rather than + // recursively via setSDKForContainer), resourceVarName is not supplied by + // the caller. Fall back to the leading token of the source variable path + // (e.g. "r" from "r.ko.Spec.Foo"), which names the owning *resource. + if resourceVarName == "" { + resourceVarName = sourceVarName + if dot := strings.IndexByte(resourceVarName, '.'); dot != -1 { + resourceVarName = resourceVarName[:dot] + } + } for memberIndex, memberName := range targetShape.MemberNames() { memberShapeRef := targetShape.MemberRefs[memberName] @@ -1248,6 +1295,7 @@ func SetSDKForStruct( ) containerOut, err := setSDKForContainer( cfg, r, + resourceVarName, memberName, memberVarName, memberFieldPath, @@ -1276,6 +1324,7 @@ func SetSDKForStruct( if r.IsSecretField(memberFieldPath) { out += setSDKForSecret( cfg, r, + resourceVarName, memberName, targetVarName, sourceAdaptedVarName, @@ -1310,6 +1359,10 @@ func SetSDKForStruct( func setSDKForSlice( cfg *ackgenconfig.Config, r *model.CRD, + // The name of the variable holding the owning resource (a *resource, + // which implements acktypes.ConditionManager). Threaded down so secret + // fields nested in this slice can pass it to SecretValueFromReference. + resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1382,6 +1435,7 @@ func setSDKForSlice( } else { containerOut, err := setSDKForContainer( cfg, r, + resourceVarName, containerFieldName, elemVarName, sourceFieldPath, @@ -1414,6 +1468,10 @@ func setSDKForSlice( func setSDKForMap( cfg *ackgenconfig.Config, r *model.CRD, + // The name of the variable holding the owning resource (a *resource, + // which implements acktypes.ConditionManager). Threaded down so secret + // fields nested in this map can pass it to SecretValueFromReference. + resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1481,6 +1539,7 @@ func setSDKForMap( } else { containerOut, err := setSDKForContainer( cfg, r, + resourceVarName, containerFieldName, valVarName, sourceFieldPath, @@ -1844,6 +1903,10 @@ func resolveAWSMapValueType(valueType string) string { func setSDKForUnion( cfg *ackgenconfig.Config, r *model.CRD, + // The name of the variable holding the owning resource (a *resource, + // which implements acktypes.ConditionManager). Threaded down so secret + // fields nested in this union can pass it to SecretValueFromReference. + resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1929,6 +1992,7 @@ func setSDKForUnion( ) containerOut, err := setSDKForContainer( cfg, r, + resourceVarName, memberName, indexedVarName, memberFieldPath, diff --git a/pkg/generate/code/set_sdk_test.go b/pkg/generate/code/set_sdk_test.go index 595e6300..3307bf8e 100644 --- a/pkg/generate/code/set_sdk_test.go +++ b/pkg/generate/code/set_sdk_test.go @@ -105,7 +105,7 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { for _, f1f0iter := range r.ko.Spec.AuthenticationMode.Passwords { var f1f0elem string if f1f0iter != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f1f0iter) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f1f0iter) if err != nil { return nil, ackrequeue.Needed(err) } @@ -208,7 +208,7 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { f3f4.MasterUserName = r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserName } if r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) } @@ -841,7 +841,7 @@ func TestSetSDK_ECR_Repository_Create(t *testing.T) { // res.SetAtRestEncryptionEnabled(*r.ko.Spec.AtRestEncryptionEnabled) // } // if r.ko.Spec.AuthToken != nil { -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AuthToken) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.AuthToken) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1096,7 +1096,7 @@ func TestSetSDK_ECR_Repository_Create(t *testing.T) { // expected := ` // res.SetApplyImmediately(true) // if r.ko.Spec.AuthToken != nil { -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AuthToken) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.AuthToken) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1218,7 +1218,7 @@ func TestSetSDK_ECR_Repository_Create(t *testing.T) { // for _, f3iter := range r.ko.Spec.Passwords { // var f3elem string // if f3iter != nil { -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f3iter) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f3iter) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1489,7 +1489,7 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { res.ManageMasterUserPassword = r.ko.Spec.ManageMasterUserPassword } if r.ko.Spec.MasterUserPassword != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) } @@ -1779,7 +1779,7 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { } if delta.DifferentAt("Spec.MasterUserPassword") { if r.ko.Spec.MasterUserPassword != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) } @@ -2318,7 +2318,7 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { f18elem.Groups = aws.ToStringSlice(f18iter.Groups) } if f18iter.Password != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f18iter.Password) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f18iter.Password) if err != nil { return nil, ackrequeue.Needed(err) } @@ -4669,7 +4669,7 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi for f4f0key, f4f0valiter := range r.ko.Spec.Environment.Variables { var f4f0val string if f4f0valiter != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f4f0valiter) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f4f0valiter) if err != nil { return nil, ackrequeue.Needed(err) } @@ -4806,7 +4806,7 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi for f2f0key, f2f0valiter := range r.ko.Spec.Environment.Variables { var f2f0val string if f2f0valiter != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f2f0valiter) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f2f0valiter) if err != nil { return nil, ackrequeue.Needed(err) } From b93930b0bb080a97946d84df0ab2d228bf4b6e46 Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Mon, 8 Jun 2026 12:10:24 -0700 Subject: [PATCH 8/9] chore: Pin runtime to PR #254 commit for cross-namespace notice Add a temporary replace directive pointing at sapphirew/ack-runtime@4ee0bc5 (runtime PR #254) which introduces the SecretValueFromReference signature change and ACK.Advisory cross-namespace condition. This resolves the dependency skew that was failing code-generator tests. Drop this replace and bump the require line once #254 merges and a tagged runtime release is published. --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c684db7d..e52df368 100644 --- a/go.mod +++ b/go.mod @@ -103,3 +103,5 @@ require ( sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) + +replace github.com/aws-controllers-k8s/runtime => github.com/sapphirew/ack-runtime v0.0.0-20260607093755-4ee0bc56d816 diff --git a/go.sum b/go.sum index fe2a7c2f..2e300f44 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,6 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws-controllers-k8s/pkg v0.0.23 h1:iqu8jKQUnyP/c6TiVcXySQYpkATui0iXFC5ax9x01oM= github.com/aws-controllers-k8s/pkg v0.0.23/go.mod h1:VvdjLWmR6IJ3KU8KByKiq/lJE8M+ur2piXysXKTGUS0= -github.com/aws-controllers-k8s/runtime v0.59.2-0.20260603224255-14be98880374 h1:qeu0+TU976/aCVWA9rGG2UTbOkvRkA6HfG+4ZnNd2Lc= -github.com/aws-controllers-k8s/runtime v0.59.2-0.20260603224255-14be98880374/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= github.com/aws/aws-sdk-go v1.49.0 h1:g9BkW1fo9GqKfwg2+zCD+TW/D36Ux+vtfJ8guF4AYmY= github.com/aws/aws-sdk-go v1.49.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= @@ -514,6 +512,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/sapphirew/ack-runtime v0.0.0-20260607093755-4ee0bc56d816 h1:ZBmQHpcjrv+wmqublik4Mjf8jxG9W+gndnV5JpUU5v0= +github.com/sapphirew/ack-runtime v0.0.0-20260607093755-4ee0bc56d816/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= From e0bcddb9535258e1ed77b0c0d1d314e6987a3812 Mon Sep 17 00:00:00 2001 From: Hao Wang Date: Mon, 8 Jun 2026 14:59:19 -0700 Subject: [PATCH 9/9] Revert: emit unchanged SecretValueFromReference call; re-pin runtime The runtime change reverted the SecretValueFromReference signature back to (ctx, ref) and restored the context-stash mechanism, so a breaking signature change is no longer forced on hand-written controller callers (e.g. rds-controller db_cluster/custom_update.go). Accordingly, revert the generated-code change: setSDKForSecret again emits rm.rr.SecretValueFromReference(ctx, ) with no resource-var threading, and the setSDKForContainer/Struct/Slice/Map/Union helpers drop the resourceVarName parameter. Re-pin the runtime replace directive to the amended fork commit (9bb3e73). --- go.mod | 2 +- go.sum | 4 +- pkg/generate/ack/controller.go | 4 +- pkg/generate/code/set_sdk.go | 82 ++++--------------------------- pkg/generate/code/set_sdk_test.go | 20 ++++---- 5 files changed, 23 insertions(+), 89 deletions(-) diff --git a/go.mod b/go.mod index e52df368..b0cf84d6 100644 --- a/go.mod +++ b/go.mod @@ -104,4 +104,4 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) -replace github.com/aws-controllers-k8s/runtime => github.com/sapphirew/ack-runtime v0.0.0-20260607093755-4ee0bc56d816 +replace github.com/aws-controllers-k8s/runtime => github.com/sapphirew/ack-runtime v0.0.0-20260608212517-9bb3e738fe97 diff --git a/go.sum b/go.sum index 2e300f44..c8ed8e00 100644 --- a/go.sum +++ b/go.sum @@ -512,8 +512,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= -github.com/sapphirew/ack-runtime v0.0.0-20260607093755-4ee0bc56d816 h1:ZBmQHpcjrv+wmqublik4Mjf8jxG9W+gndnV5JpUU5v0= -github.com/sapphirew/ack-runtime v0.0.0-20260607093755-4ee0bc56d816/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= +github.com/sapphirew/ack-runtime v0.0.0-20260608212517-9bb3e738fe97 h1:l58zGokUaqMvnateGrtzKcGIrOSd7fnap+g+Cxeijd4= +github.com/sapphirew/ack-runtime v0.0.0-20260608212517-9bb3e738fe97/go.mod h1:ljWD1IdtVx/qC7C4lVobF4vLNhno/xX5A78BOke1Ksk= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= diff --git a/pkg/generate/ack/controller.go b/pkg/generate/ack/controller.go index eca18562..5beab739 100644 --- a/pkg/generate/ack/controller.go +++ b/pkg/generate/ack/controller.go @@ -124,9 +124,7 @@ var ( return code.SetSDK(r.Config(), r, ackmodel.OpTypeDelete, sourceVarName, targetVarName, indentLevel) }, "GoCodeSetSDKForStruct": func(r *ackmodel.CRD, targetFieldName string, targetVarName string, targetShapeRef *awssdkmodel.ShapeRef, sourceFieldPath string, sourceVarName string, indentLevel int) (string, error) { - // Empty resourceVarName: SetSDKForStruct derives it from the - // leading token of sourceVarName when invoked as an entry point. - return code.SetSDKForStruct(r.Config(), r, "", targetFieldName, targetVarName, targetShapeRef, sourceFieldPath, sourceVarName, model.OpTypeList, indentLevel) + return code.SetSDKForStruct(r.Config(), r, targetFieldName, targetVarName, targetShapeRef, sourceFieldPath, sourceVarName, model.OpTypeList, indentLevel) }, "GoCodeSetResourceForStruct": func(r *ackmodel.CRD, targetFieldName string, targetVarName string, targetShapeRef *awssdkmodel.ShapeRef, sourceVarName string, sourceShapeRef *awssdkmodel.ShapeRef, indentLevel int) (string, error) { var setCfg *ackgenconfig.SetFieldConfig = nil diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index 40581c91..1152819a 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -119,16 +119,6 @@ func SetSDK( out := "\n" indent := strings.Repeat("\t", indentLevel) - // The owning resource variable, used so secret fields can pass it to the - // runtime's SecretValueFromReference (which sets the cross-namespace - // deprecation ACK.Advisory condition on it). It is the leading token of - // the source variable path (e.g. "r" from "r.ko"); the generated - // *resource type implements acktypes.ConditionManager. - resourceVarName := sourceVarName - if dot := strings.IndexByte(resourceVarName, '.'); dot != -1 { - resourceVarName = resourceVarName[:dot] - } - // Check if there's an input wrapper field path configured. If so, we need // to create a wrapper struct and populate its fields from the CRD spec, // then assign the wrapper to the input shape's wrapper field. @@ -412,7 +402,6 @@ func SetSDK( ) containerOut, err := setSDKForContainer( cfg, r, - resourceVarName, memberName, memberVarName, sourceFieldPath, @@ -441,7 +430,6 @@ func SetSDK( if r.IsSecretField(memberName) { out += setSDKForSecret( cfg, r, - resourceVarName, memberName, targetVarName, sourceAdaptedVarName, @@ -1017,10 +1005,6 @@ func setSDKReadMany( func setSDKForContainer( cfg *ackgenconfig.Config, r *model.CRD, - // The name of the variable holding the owning resource (a *resource, - // which implements acktypes.ConditionManager). Threaded down so secret - // fields nested in collections can pass it to SecretValueFromReference. - resourceVarName string, // The name of the SDK Input shape member we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1039,7 +1023,6 @@ func setSDKForContainer( case "structure": return SetSDKForStruct( cfg, r, - resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1051,7 +1034,6 @@ func setSDKForContainer( case "list": return setSDKForSlice( cfg, r, - resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1063,7 +1045,6 @@ func setSDKForContainer( case "map": return setSDKForMap( cfg, r, - resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1075,7 +1056,6 @@ func setSDKForContainer( case "union": return setSDKForUnion( cfg, r, - resourceVarName, targetFieldName, targetVarName, targetShapeRef, @@ -1094,7 +1074,6 @@ func setSDKForContainer( ) out += setSDKForSecret( cfg, r, - resourceVarName, "", targetVarName, sourceVarName, @@ -1122,17 +1101,14 @@ func setSDKForContainer( // the value of a Secret when the type of the source variable is a // SecretKeyReference. // -// Cross-namespace validation (and the Phase 1 deprecation notice) is performed -// inside the runtime's SecretValueFromReference, so it is not emitted here. -// This ensures every caller is covered, including custom update functions and -// hooks that call SecretValueFromReference directly. The owning resource is -// passed as the second argument so the runtime can set the cross-namespace -// deprecation ACK.Advisory condition on it; it is derived from the source -// variable (e.g. "r" from "r.ko.Spec.MasterUserPassword"). +// Cross-namespace validation (and the Phase 1 deprecation warning) is +// performed inside the runtime's SecretValueFromReference, so it is not +// emitted here. This ensures every caller is covered, including custom +// update functions and hooks that call SecretValueFromReference directly. // // The Go code output from this function looks like this: // -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1146,11 +1122,6 @@ func setSDKForContainer( func setSDKForSecret( cfg *ackgenconfig.Config, r *model.CRD, - // The name of the variable holding the owning resource (a - // *resource, which implements acktypes.ConditionManager). It is passed to - // SecretValueFromReference so the runtime can set the cross-namespace - // deprecation ACK.Advisory condition on it. Typically "r" or "desired". - resourceVarName string, // The name of the SDK Shape field we're setting targetFieldName string, // The variable name that we want to set a value on @@ -1167,14 +1138,12 @@ func setSDKForSecret( // Cross-namespace validation for the secret reference is performed inside // the runtime's SecretValueFromReference, so that every call site is // covered (including custom update functions and hooks). No per-call - // validation is generated here. The owning resource is passed so the - // runtime can set the cross-namespace deprecation ACK.Advisory condition - // on it. + // validation is generated here. - // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) + // tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword) out += fmt.Sprintf( - "%s\t%s, err := rm.rr.SecretValueFromReference(ctx, %s, %s)\n", - indent, secVar, resourceVarName, sourceVarName, + "%s\t%s, err := rm.rr.SecretValueFromReference(ctx, %s)\n", + indent, secVar, sourceVarName, ) // if err != nil { // return nil, ackrequeue.Needed(err) @@ -1206,12 +1175,6 @@ func setSDKForSecret( func SetSDKForStruct( cfg *ackgenconfig.Config, r *model.CRD, - // The name of the variable holding the owning resource (a *resource, - // which implements acktypes.ConditionManager). Threaded down so secret - // fields nested in this struct can pass it to SecretValueFromReference. - // When this is the entry point (e.g. the GoCodeSetSDKForStruct template - // helper), it is derived from the leading token of sourceVarName. - resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1228,16 +1191,6 @@ func SetSDKForStruct( out := "" indent := strings.Repeat("\t", indentLevel) targetShape := targetShapeRef.Shape - // When SetSDKForStruct is invoked as a top-level entry point (rather than - // recursively via setSDKForContainer), resourceVarName is not supplied by - // the caller. Fall back to the leading token of the source variable path - // (e.g. "r" from "r.ko.Spec.Foo"), which names the owning *resource. - if resourceVarName == "" { - resourceVarName = sourceVarName - if dot := strings.IndexByte(resourceVarName, '.'); dot != -1 { - resourceVarName = resourceVarName[:dot] - } - } for memberIndex, memberName := range targetShape.MemberNames() { memberShapeRef := targetShape.MemberRefs[memberName] @@ -1295,7 +1248,6 @@ func SetSDKForStruct( ) containerOut, err := setSDKForContainer( cfg, r, - resourceVarName, memberName, memberVarName, memberFieldPath, @@ -1324,7 +1276,6 @@ func SetSDKForStruct( if r.IsSecretField(memberFieldPath) { out += setSDKForSecret( cfg, r, - resourceVarName, memberName, targetVarName, sourceAdaptedVarName, @@ -1359,10 +1310,6 @@ func SetSDKForStruct( func setSDKForSlice( cfg *ackgenconfig.Config, r *model.CRD, - // The name of the variable holding the owning resource (a *resource, - // which implements acktypes.ConditionManager). Threaded down so secret - // fields nested in this slice can pass it to SecretValueFromReference. - resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1435,7 +1382,6 @@ func setSDKForSlice( } else { containerOut, err := setSDKForContainer( cfg, r, - resourceVarName, containerFieldName, elemVarName, sourceFieldPath, @@ -1468,10 +1414,6 @@ func setSDKForSlice( func setSDKForMap( cfg *ackgenconfig.Config, r *model.CRD, - // The name of the variable holding the owning resource (a *resource, - // which implements acktypes.ConditionManager). Threaded down so secret - // fields nested in this map can pass it to SecretValueFromReference. - resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1539,7 +1481,6 @@ func setSDKForMap( } else { containerOut, err := setSDKForContainer( cfg, r, - resourceVarName, containerFieldName, valVarName, sourceFieldPath, @@ -1903,10 +1844,6 @@ func resolveAWSMapValueType(valueType string) string { func setSDKForUnion( cfg *ackgenconfig.Config, r *model.CRD, - // The name of the variable holding the owning resource (a *resource, - // which implements acktypes.ConditionManager). Threaded down so secret - // fields nested in this union can pass it to SecretValueFromReference. - resourceVarName string, // The name of the CR field we're outputting for targetFieldName string, // The variable name that we want to set a value to @@ -1992,7 +1929,6 @@ func setSDKForUnion( ) containerOut, err := setSDKForContainer( cfg, r, - resourceVarName, memberName, indexedVarName, memberFieldPath, diff --git a/pkg/generate/code/set_sdk_test.go b/pkg/generate/code/set_sdk_test.go index 3307bf8e..595e6300 100644 --- a/pkg/generate/code/set_sdk_test.go +++ b/pkg/generate/code/set_sdk_test.go @@ -105,7 +105,7 @@ func TestSetSDK_MemoryDB_User_Create(t *testing.T) { for _, f1f0iter := range r.ko.Spec.AuthenticationMode.Passwords { var f1f0elem string if f1f0iter != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f1f0iter) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f1f0iter) if err != nil { return nil, ackrequeue.Needed(err) } @@ -208,7 +208,7 @@ func TestSetSDK_OpenSearch_Domain_Create(t *testing.T) { f3f4.MasterUserName = r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserName } if r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AdvancedSecurityOptions.MasterUserOptions.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) } @@ -841,7 +841,7 @@ func TestSetSDK_ECR_Repository_Create(t *testing.T) { // res.SetAtRestEncryptionEnabled(*r.ko.Spec.AtRestEncryptionEnabled) // } // if r.ko.Spec.AuthToken != nil { -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.AuthToken) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AuthToken) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1096,7 +1096,7 @@ func TestSetSDK_ECR_Repository_Create(t *testing.T) { // expected := ` // res.SetApplyImmediately(true) // if r.ko.Spec.AuthToken != nil { -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.AuthToken) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.AuthToken) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1218,7 +1218,7 @@ func TestSetSDK_ECR_Repository_Create(t *testing.T) { // for _, f3iter := range r.ko.Spec.Passwords { // var f3elem string // if f3iter != nil { -// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f3iter) +// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f3iter) // if err != nil { // return nil, ackrequeue.Needed(err) // } @@ -1489,7 +1489,7 @@ func TestSetSDK_RDS_DBInstance_Create(t *testing.T) { res.ManageMasterUserPassword = r.ko.Spec.ManageMasterUserPassword } if r.ko.Spec.MasterUserPassword != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) } @@ -1779,7 +1779,7 @@ func TestSetSDK_RDS_DBInstance_Update(t *testing.T) { } if delta.DifferentAt("Spec.MasterUserPassword") { if r.ko.Spec.MasterUserPassword != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, r.ko.Spec.MasterUserPassword) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r.ko.Spec.MasterUserPassword) if err != nil { return nil, ackrequeue.Needed(err) } @@ -2318,7 +2318,7 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) { f18elem.Groups = aws.ToStringSlice(f18iter.Groups) } if f18iter.Password != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f18iter.Password) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f18iter.Password) if err != nil { return nil, ackrequeue.Needed(err) } @@ -4669,7 +4669,7 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Create(t *testi for f4f0key, f4f0valiter := range r.ko.Spec.Environment.Variables { var f4f0val string if f4f0valiter != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f4f0valiter) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f4f0valiter) if err != nil { return nil, ackrequeue.Needed(err) } @@ -4806,7 +4806,7 @@ func TestSetSDK_Lambda_Function_EnvironmentVariable_MapOfSecrets_Update(t *testi for f2f0key, f2f0valiter := range r.ko.Spec.Environment.Variables { var f2f0val string if f2f0valiter != nil { - tmpSecret, err := rm.rr.SecretValueFromReference(ctx, r, f2f0valiter) + tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f2f0valiter) if err != nil { return nil, ackrequeue.Needed(err) }