Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.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
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.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=
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including this in the generated sdk.go files will get most of the SecretKeyReference usages, but I believe there are cases where Secrets are used in custom update functions. For example, RDS's MasterUserPassword is set in a custom_update.go file and won't be updated automatically. Is there any way we can move this validation to happen in SecretValueFromReference entirely in runtime?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, going to move secret validation into runtime's SecretValueFromReference from the current change in sdk.go.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 -}}