Skip to content

Lambda: spec.layers never reconciled when the function has no layers in AWS #3010

Description

@suprahh

Describe the bug

When a Lambda Function has no layers attached in AWS, adding layers to spec.layers
has no effect. The controller never computes a Spec.Layers delta, so
UpdateFunctionConfiguration is called without the Layers parameter and the layers are
never attached. No error is reported and the resource is marked ACK.ResourceSynced=True.

The root cause is in the generated sdkFind for Function. It starts from a deep copy of
the desired resource and only overwrites Spec.Layers when the AWS response contains
layers:

// pkg/resource/function/sdk.go
ko := r.ko.DeepCopy()                        // starts from DESIRED state
...
if resp.Configuration.Layers != nil {
    ...
    ko.Spec.Layers = layer                   // only assigned when AWS already has layers
    ko.Status.LayerStatuses = f16
} else {
    ko.Status.LayerStatuses = nil            // clears STATUS only; Spec.Layers is left as-is
}

When AWS returns no layers, the else branch clears only Status.LayerStatuses. The
latest resource therefore keeps the desired Spec.Layers value inherited from the
DeepCopy. newResourceDelta then compares desired against latest:

// pkg/resource/function/delta.go
if len(a.ko.Spec.Layers) != len(b.ko.Spec.Layers) {
    delta.Add("Spec.Layers", a.ko.Spec.Layers, b.ko.Spec.Layers)
} else if len(a.ko.Spec.Layers) > 0 {
    if !ackcompare.SliceStringPEqual(a.ko.Spec.Layers, b.ko.Spec.Layers) {
        delta.Add("Spec.Layers", a.ko.Spec.Layers, b.ko.Spec.Layers)
    }
}

Both sides hold the same value, so no delta is added. updateFunctionConfiguration guards
the field with delta.DifferentAt("Spec.Layers"), which is never true, so input.Layers
is never set.

The bug is self-sealing: it only manifests in the exact case where the layers are missing
from AWS and therefore need to be added. If the function already has a different layer,
resp.Configuration.Layers != nil holds, Spec.Layers is correctly overwritten from the
response, the delta fires and the update works as expected.

Status.LayerStatuses is correctly reported as empty throughout, which is a useful signal:
the CR shows spec.layers populated and status.layerStatuses empty at the same time.

Steps to reproduce

  1. Create a Lambda function without any layers (e.g. with Terraform, CloudFormation, or
    the console), so that GetFunction returns no Configuration.Layers.

  2. Adopt it with the controller:

apiVersion: lambda.services.k8s.aws/v1alpha1
kind: Function
metadata:
  name: my-function
  annotations:
    services.k8s.aws/adoption-policy: adopt
    services.k8s.aws/adoption-fields: |
      {"name": "my-function"}
spec:
  name: my-function
  # ...
  1. Once adopted, add a layer to the spec:
kubectl patch function my-function --type=merge \
  -p '{"spec":{"layers":["arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:88"]}}'
  1. Observe the result:
# The CR has the layer in spec, but status is empty
kubectl get function my-function \
  -o jsonpath='spec.layers={.spec.layers}{"\n"}status.layerStatuses={.status.layerStatuses}{"\n"}'
# spec.layers=["arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:88"]
# status.layerStatuses=

# The condition reports success
kubectl get function my-function -o jsonpath='{.status.conditions[?(@.type=="ACK.ResourceSynced")].status}'
# True

# AWS still has no layers
aws lambda get-function-configuration --function-name my-function --query 'Layers'
# null

The controller logs show no Spec.Layers entry in any "desired resource state has changed" diff, and no error. In our environment, across the full controller log buffer,
diffs were emitted for Spec.Handler, Spec.MemorySize, Spec.Timeout, Spec.Environment,
Spec.Tags and Spec.Code — and never once for Spec.Layers.

CloudTrail confirms the field is absent from the API call. Both UpdateFunctionConfiguration
events issued by the controller in the same reconcile window carried other fields but no
layers key in requestParameters:

UpdateFunctionConfiguration  by <controller-role>
   handler : src/handler.handler
   layers  : <absent from requestParameters>

UpdateFunctionConfiguration  by <controller-role>
   memorySize : 512
   timeout    : 30
   layers     : <absent from requestParameters>

Expected outcome

Adding ARNs to spec.layers should attach those layers to the function, regardless of
whether the function currently has layers in AWS. Concretely, sdkFind should set
ko.Spec.Layers = nil in the else branch — mirroring what it already does for
ko.Status.LayerStatuses — so that latest reflects the actual AWS state and the delta can
be computed correctly.

This appears to be the same class of defect as #1413 (Spec.Timeout omitted from
UpdateFunctionConfiguration), which was fixed in lambda-controller#41.

Environment

  • Kubernetes version: v1.34.9
  • Using EKS (yes/no), if so version? yes, EKS 1.34
  • AWS service targeted (S3, RDS, etc.): Lambda

Additional details:

  • lambda-controller version: v1.5.8 (public.ecr.aws/aws-controllers-k8s/lambda-controller:1.5.8)
  • Feature gates: ResourceAdoption: true, ReadOnlyResources: true
  • The affected code is byte-identical in v1.5.8, v1.16.0 and main, so the bug is
    present in the latest release. Diffing the if resp.Configuration.Layers != nil { ... } else { ... } block across those three revisions produces no differences.
  • Reproduced on three separate adopted functions in the same cluster; all three show
    spec.layers populated, status.layerStatuses empty and no layers in AWS.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.service/lambdaIndicates issues or PRs that are related to lambda-controller.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions