Describe the bug
When using services.k8s.aws/adoption-policy: adopt annotation without the services.k8s.aws/adoption-fields annotation, the controller returns an unclear unexpected end of JSON input error. This error becomes a TerminalError, permanently blocking reconciliation.
The error message does not indicate that the adoption-fields annotation is missing, making it very difficult to diagnose.
Reproduction Steps
- Create a Role CR with
adoption-policy: adopt but without adoption-fields:
apiVersion: iam.services.k8s.aws/v1alpha1
kind: Role
metadata:
name: my-existing-role
namespace: ack-system
annotations:
services.k8s.aws/region: ap-northeast-1
services.k8s.aws/adoption-policy: adopt
# NOTE: services.k8s.aws/adoption-fields is intentionally omitted
spec:
name: my-existing-role
assumeRolePolicyDocument: |
{ ... }
- Apply the resource:
kubectl apply -f role.yaml
- Controller logs show:
{"level":"error","msg":"Reconciler error","controller":"role","error":"unexpected end of JSON input"}
The resource enters a terminal error state with no way to recover except re-creating with the correct annotation.
Expected Behavior
The error message should clearly state that the services.k8s.aws/adoption-fields annotation is required when adoption-policy is set to adopt, for example:
services.k8s.aws/adoption-fields annotation is required when adoption-policy is set to 'adopt';
please provide the resource identifier fields as a JSON object (e.g. '{"name": "my-resource"}')
Root Cause
In pkg/runtime/util.go (ExtractAdoptionFields):
func ExtractAdoptionFields(res acktypes.AWSResource) (map[string]string, error) {
fields := getAdoptionFields(res) // returns "" when annotation is missing
extractedFields := &map[string]string{}
err := json.Unmarshal([]byte(fields), extractedFields) // json.Unmarshal("") → "unexpected end of JSON input"
if err != nil {
return nil, err
}
return *extractedFields, nil
}
When adoption-fields annotation is not set, getAdoptionFields() returns an empty string "". Then json.Unmarshal([]byte(""), ...) produces the generic Go error unexpected end of JSON input.
In reconciler.go, this error is then wrapped as a TerminalError for adopt policy (but silently ignored for adopt-or-create).
Proposed Fix
Add an explicit empty string check before calling json.Unmarshal, and wrap the JSON parse error with context:
func ExtractAdoptionFields(res acktypes.AWSResource) (map[string]string, error) {
fields := getAdoptionFields(res)
if fields == "" {
return nil, fmt.Errorf(
"services.k8s.aws/adoption-fields annotation is required when " +
"adoption-policy is set to 'adopt'; please provide the resource " +
"identifier fields as a JSON object (e.g. '{\"name\": \"my-resource\"}')",
)
}
extractedFields := &map[string]string{}
err := json.Unmarshal([]byte(fields), extractedFields)
if err != nil {
return nil, fmt.Errorf("failed to parse services.k8s.aws/adoption-fields annotation as JSON: %w", err)
}
return *extractedFields, nil
}
I have a branch with the fix and tests ready to submit as a PR once this issue is triaged.
Environment
- ACK IAM Controller: v1.6.2
- ACK Runtime: v0.58.0
- Kubernetes: EKS 1.31
- Region: ap-northeast-1
Related Issues
/kind bug
/area runtime
/area adoption-annotation
Describe the bug
When using
services.k8s.aws/adoption-policy: adoptannotation without theservices.k8s.aws/adoption-fieldsannotation, the controller returns an unclearunexpected end of JSON inputerror. This error becomes aTerminalError, permanently blocking reconciliation.The error message does not indicate that the
adoption-fieldsannotation is missing, making it very difficult to diagnose.Reproduction Steps
adoption-policy: adoptbut withoutadoption-fields:{"level":"error","msg":"Reconciler error","controller":"role","error":"unexpected end of JSON input"}The resource enters a terminal error state with no way to recover except re-creating with the correct annotation.
Expected Behavior
The error message should clearly state that the
services.k8s.aws/adoption-fieldsannotation is required whenadoption-policyis set toadopt, for example:Root Cause
In
pkg/runtime/util.go(ExtractAdoptionFields):When
adoption-fieldsannotation is not set,getAdoptionFields()returns an empty string"". Thenjson.Unmarshal([]byte(""), ...)produces the generic Go errorunexpected end of JSON input.In
reconciler.go, this error is then wrapped as aTerminalErrorforadoptpolicy (but silently ignored foradopt-or-create).Proposed Fix
Add an explicit empty string check before calling
json.Unmarshal, and wrap the JSON parse error with context:I have a branch with the fix and tests ready to submit as a PR once this issue is triaged.
Environment
Related Issues
status.ackResourceMetadata#2779 - Populate adoption fields in status.ackResourceMetadata/kind bug
/area runtime
/area adoption-annotation