Skip to content
Merged
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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.20260617181621-38bd4c967a01
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
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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.20260617181621-38bd4c967a01 h1:1kdTtKwkVyw5KnOt9/5b0+e3OZ55EcwTTDw9AvfnKWU=
github.com/aws-controllers-k8s/runtime v0.59.2-0.20260617181621-38bd4c967a01/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=
Expand Down
40 changes: 37 additions & 3 deletions pkg/generate/code/resource_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ 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.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):
//
// if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil {
Expand All @@ -130,6 +136,18 @@ func ReferenceFieldsValidation(
// if arr.Name == nil || *arr.Name == "" {
// return hasReferences, fmt.Errorf("provided resource reference is nil or empty: APIRef")
// }
// namespace, err := ackrt.ResolveCrossNamespaceReference(
// ctx,
// rm.cfg.EnableCrossNamespace,
// &ko.Status.Conditions,
// ackrt.CrossNamespaceRefKindResource,
// ko.ObjectMeta.GetNamespace(),
// arr.Namespace,
// *arr.Name,
// )
// if err != nil {
// return hasReferences, err
// }
// obj := &svcapitypes.API{}
// if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
// return hasReferences, err
Expand All @@ -146,6 +164,10 @@ func ReferenceFieldsValidation(
// if arr.Name == nil || *arr.Name == "" {
// return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SecurityGroupRefs")
// }
// namespace, err := ackrt.ResolveCrossNamespaceReference( ... )
// if err != nil {
// return hasReferences, err
// }
// obj := &ec2apitypes.SecurityGroup{}
// if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
// return hasReferences, err
Expand All @@ -170,6 +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, 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
Expand Down Expand Up @@ -228,9 +254,17 @@ 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, 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)
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 += getReferencedStateForField(field, innerIndentLevel)
Expand Down
98 changes: 77 additions & 21 deletions pkg/generate/code/resource_reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &svcapitypes.API{}
if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down Expand Up @@ -180,9 +188,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &svcapitypes.Policy{}
if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down Expand Up @@ -217,9 +233,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &ec2apitypes.SecurityGroup{}
if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down Expand Up @@ -258,9 +282,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &svcapitypes.API{}
if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down Expand Up @@ -299,9 +331,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &svcapitypes.Bucket{}
if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down Expand Up @@ -340,9 +380,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &svcapitypes.InternetGateway{}
if err := getReferencedResourceState_InternetGateway(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down Expand Up @@ -384,9 +432,17 @@ 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, err := ackrt.ResolveCrossNamespaceReference(
ctx,
rm.cfg.EnableCrossNamespace,
&ko.Status.Conditions,
ackrt.CrossNamespaceRefKindResource,
ko.ObjectMeta.GetNamespace(),
arr.Namespace,
*arr.Name,
)
if err != nil {
return hasReferences, err
}
obj := &svcapitypes.Bucket{}
if err := getReferencedResourceState_Bucket(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
Expand Down
20 changes: 10 additions & 10 deletions pkg/generate/code/set_sdk.go
Comment thread
knottnt marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,11 @@ 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.
//
// The Go code output from this function looks like this:
//
// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, ko.Spec.MasterUserPassword)
Expand All @@ -1111,16 +1116,6 @@ func setSDKForContainer(
// res.SetMasterUserPassword(tmpSecret)
// }
//
// or:
//
// tmpSecret, err := rm.rr.SecretValueFromReference(ctx, f3iter)
// if err != nil {
// return nil, ackrequeue.Needed(err)
// }
// if tmpSecret != "" {
// f3elem = tmpSecret
// }
//
// The second case is used when the SecretKeyReference field
// is a slice of `[]*string` in the original AWS API Input shape.

Expand All @@ -1140,6 +1135,11 @@ func setSDKForSecret(
indent := strings.Repeat("\t", indentLevel)
secVar := "tmpSecret"

// 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(
"%s\t%s, err := rm.rr.SecretValueFromReference(ctx, %s)\n",
Expand Down
1 change: 1 addition & 0 deletions templates/helm/templates/deployment.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ spec:
- "$(FEATURE_GATES)"
{{ "{{- end }}" }}
- {{ "--enable-carm={{ .Values.enableCARM }}" }}
- {{ "--enable-cross-namespace={{ .Values.enableCrossNamespace }}" }}
image: {{ "{{ .Values.image.repository }}:{{ .Values.image.tag }}" }}
imagePullPolicy: {{ "{{ .Values.image.pullPolicy }}" }}
name: controller
Expand Down
5 changes: 5 additions & 0 deletions templates/helm/values.schema.json.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions templates/helm/values.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ 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

# Configuration for feature gates. These are optional controller features that
# can be individually enabled ("true") or disabled ("false") by adding key/value
# pairs below.
Expand Down
2 changes: 1 addition & 1 deletion templates/pkg/resource/references.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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"
{{ end -}}
acktypes "github.com/aws-controllers-k8s/runtime/pkg/types"
{{ $servicePackageName := .ServicePackageName -}}
Expand Down Expand Up @@ -133,4 +134,3 @@ func (rm *resourceManager) resolveReferenceFor{{ $field.FieldPathWithUnderscore
{{ end -}}
{{ end -}}
{{ end -}}