diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index e68717e..4d3e444 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -2,6 +2,9 @@ name: build-and-test +permissions: + id-token: write + contents: read # Controls when the action will run. Triggers the workflow on push or pull request # events but only for the main branch on: @@ -23,10 +26,18 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version: 1.24 - name: Checking compilation errors while generating image run: env GOOS=linux go build -o "sumologic-extension" "lambda-extensions/sumologic-extension.go" + - name: Configure AWS credentials via OIDC and IAM Role + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::956882708938:role/LambdaExtensionPublishRole + aws-region: us-east-1 + - name: Run Unit Tests + env: + AWS_REGION: us-east-1 run: go test ./... diff --git a/containerimageexample/python-arm64/Dockerfile b/containerimageexample/python-arm64/Dockerfile index 576c6ea..8fc87cd 100644 --- a/containerimageexample/python-arm64/Dockerfile +++ b/containerimageexample/python-arm64/Dockerfile @@ -1,27 +1,21 @@ # Define global args ARG FUNCTION_DIR="app" -ARG RUNTIME_VERSION="3.9" -ARG DISTRO_VERSION="3.12" - +ARG RUNTIME_VERSION="3.13.7" # Stage 1 - bundle base image + runtime -# Grab a fresh copy of the ARM 64 based image and install GCC -FROM arm64v8/python:${RUNTIME_VERSION}-alpine${DISTRO_VERSION} AS python-alpine -# Install GCC (Alpine uses musl but we compile and link dependencies with GCC) -RUN apk add --no-cache \ - libstdc++ +# Grab a fresh copy of the ARM 64 based image +FROM arm64v8/python:${RUNTIME_VERSION}-slim-bookworm AS python-slim-bookworm # Stage 2 - build function and dependencies -FROM python-alpine AS build-image +FROM python-slim-bookworm AS build-image # Install aws-lambda-cpp build dependencies -RUN apk add --no-cache \ - build-base \ - libtool \ - autoconf \ - automake \ - libexecinfo-dev \ - make \ - cmake \ - libcurl +RUN apt-get update && \ + apt-get install -y \ + g++ \ + make \ + cmake \ + unzip \ + libcurl4-openssl-dev + # Include global args in this stage of the build ARG FUNCTION_DIR ARG RUNTIME_VERSION @@ -36,7 +30,7 @@ RUN pip install awslambdaric --target ${FUNCTION_DIR} # Stage 3 - final runtime image # Grab a fresh copy of the Python image -FROM python-alpine +FROM python-slim-bookworm # Include global arg in this stage of the build ARG FUNCTION_DIR # Set working directory to function root directory it also creates the directory and cd to that directory @@ -45,8 +39,10 @@ WORKDIR ${FUNCTION_DIR} COPY --from=build-image ${FUNCTION_DIR} ./ # (Optional) Add Lambda Runtime Interface Emulator and use a script in the ENTRYPOINT for simpler local runs ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie +# Create the directory explicitly +RUN mkdir -p /opt/extensions # SumoLogic Lambda extension -ADD sumologic-extension-arm64.tar.gz /opt/ +COPY extensions/sumologic-extension /opt/extensions/ COPY entry.sh / RUN chmod 755 /usr/bin/aws-lambda-rie /entry.sh ENTRYPOINT [ "/entry.sh" ] diff --git a/containerimageexample/python-arm64/build_and_deploy_image.sh b/containerimageexample/python-arm64/build_and_deploy_image.sh old mode 100644 new mode 100755 index 6f591d3..ba592ee --- a/containerimageexample/python-arm64/build_and_deploy_image.sh +++ b/containerimageexample/python-arm64/build_and_deploy_image.sh @@ -1,14 +1,14 @@ -docker build -t lambda/hello-world-python:3.9-alpine3.12 . +docker build --platform=linux/arm64 -t lambda/hello-world-python:3.13.7-slim-bookworm . ## Command to run container -# docker run -p 9000:8080 lambda/hello-world-python:3.9-alpine3.12 +# docker run -p 9000:8080 lambda/hello-world-python:3.13.7-slim-bookworm ## Command to test # curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' ## Command to push image ACCOUNT_ID=956882708938 -aws ecr create-repository --repository-name hello-world-python-arm64 --image-scanning-configuration scanOnPush=true -docker tag lambda/hello-world-python:3.9-alpine3.12 "${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/"hello-world-python-arm64:latest +#aws ecr create-repository --repository-name hello-world-python-arm64 --image-scanning-configuration scanOnPush=true +docker tag lambda/hello-world-python:3.13.7-slim-bookworm "${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/"hello-world-python-arm64:latest aws ecr get-login-password | docker login --username AWS --password-stdin "${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com" docker push "${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/hello-world-python-arm64:latest" diff --git a/containerimageexample/python-arm64/extensions/sumologic-extension b/containerimageexample/python-arm64/extensions/sumologic-extension new file mode 100755 index 0000000..2b7b216 Binary files /dev/null and b/containerimageexample/python-arm64/extensions/sumologic-extension differ diff --git a/containerimageexample/python-arm64/sumologic-extension-amd64.tar.gz b/containerimageexample/python-arm64/sumologic-extension-amd64.tar.gz index 31ddd76..1f040be 100644 Binary files a/containerimageexample/python-arm64/sumologic-extension-amd64.tar.gz and b/containerimageexample/python-arm64/sumologic-extension-amd64.tar.gz differ diff --git a/containerimageexample/python-arm64/sumologic-extension-arm64.tar.gz b/containerimageexample/python-arm64/sumologic-extension-arm64.tar.gz index 5b21077..df5d059 100644 Binary files a/containerimageexample/python-arm64/sumologic-extension-arm64.tar.gz and b/containerimageexample/python-arm64/sumologic-extension-arm64.tar.gz differ diff --git a/go.mod b/go.mod index 23243bc..8df8fc7 100644 --- a/go.mod +++ b/go.mod @@ -1,28 +1,34 @@ module github.com/SumoLogic/sumologic-lambda-extensions -go 1.22 +go 1.24.0 + +toolchain go1.24.1 require ( - github.com/aws/aws-sdk-go v1.51.25 - github.com/aws/aws-sdk-go-v2 v1.26.1 - github.com/aws/aws-sdk-go-v2/config v1.27.11 - github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 + github.com/aws/aws-sdk-go-v2 v1.39.1 + github.com/aws/aws-sdk-go-v2/config v1.31.10 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.8 + github.com/aws/aws-sdk-go-v2/service/kms v1.45.4 + github.com/aws/aws-sdk-go-v2/service/s3 v1.88.2 + github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 github.com/google/uuid v1.6.0 github.com/sirupsen/logrus v1.9.3 ) require ( - github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect - github.com/aws/smithy-go v1.20.2 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - golang.org/x/sys v0.19.0 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.14 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 // indirect + github.com/aws/smithy-go v1.23.0 // indirect + golang.org/x/sys v0.36.0 // indirect ) diff --git a/go.sum b/go.sum index a135a21..1d281e6 100644 --- a/go.sum +++ b/go.sum @@ -1,101 +1,58 @@ -github.com/aws/aws-sdk-go v1.35.23 h1:SCP0d0XvyJTDmfnHEQPvBaYi3kea1VNUo7uQmkVgFts= -github.com/aws/aws-sdk-go v1.35.23/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= -github.com/aws/aws-sdk-go v1.51.25 h1:DjTT8mtmsachhV6yrXR8+yhnG6120dazr720nopRsls= -github.com/aws/aws-sdk-go v1.51.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.17.1 h1:02c72fDJr87N8RAC2s3Qu0YuvMRZKNZJ9F+lAehCazk= -github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= -github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= -github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= -github.com/aws/aws-sdk-go-v2/config v1.17.11 h1:9JQUKwRN8oUqeOFIrNaH6RSPmmcNk1+bQrDka/f/bPc= -github.com/aws/aws-sdk-go-v2/config v1.17.11/go.mod h1:cw6HIEr0FaZQfcoyRWYZpMfv4qAH19hZFZ5mglwWo3g= -github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA= -github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE= -github.com/aws/aws-sdk-go-v2/credentials v1.12.24 h1:yz4fhoMfgwymG0rU6q5eCydFhYNQxk9yrNjMA7L7xmg= -github.com/aws/aws-sdk-go-v2/credentials v1.12.24/go.mod h1:prZpUfBu1KZLBLVX482Sq4DpDXGugAre08TPEc21GUg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs= -github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 h1:E3PXZSI3F2bzyj6XxUXdTIfvp425HHhwKsFvmzBwHgs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 h1:nBO/RFxeq/IS5G9Of+ZrgucRciie2qpLy++3UGZ+q2E= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 h1:oRHDrwCTVT8ZXi4sr9Ld+EXk7N/KGssOr2ygNeojEhw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 h1:Mza+vlnZr+fPKFKRq/lKGVvM6B/8ZZmNdEopOwSQLms= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 h1:GE25AWCdNUPh9AOJzI9KIJnja7IwUc1WyUqz/JTyJ/I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk= -github.com/aws/aws-sdk-go-v2/service/kms v1.18.16 h1:KHzeOb0G5ZvaIOewRSs3iyHR5MeAKkIZ75tUJCO9ijg= -github.com/aws/aws-sdk-go-v2/service/kms v1.18.16/go.mod h1:kZodDPTQjSH/qM6/OvyTfM5mms5JHB/EKYp5dhn/vI4= -github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 h1:yl7wcqbisxPzknJVfWTLnK83McUvXba+pz2+tPbIUmQ= -github.com/aws/aws-sdk-go-v2/service/kms v1.31.0/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 h1:GFZitO48N/7EsFDt8fMa5iYdmWqkUDDB3Eje6z3kbG0= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 h1:jcw6kKZrtNfBPJkaHrscDOZoe5gvi9wjudnxvozYFJo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.2 h1:tpwEMRdMf2UsplengAOnmSIRdvAxf75oUFR+blBr92I= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.2/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= -github.com/aws/smithy-go v1.13.4 h1:/RN2z1txIJWeXeOkzX+Hk/4Uuvv7dWtCjbmVJcrskyk= -github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= -github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2 v1.39.1 h1:fWZhGAwVRK/fAN2tmt7ilH4PPAE11rDj7HytrmbZ2FE= +github.com/aws/aws-sdk-go-v2 v1.39.1/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00= +github.com/aws/aws-sdk-go-v2/config v1.31.10 h1:7LllDZAegXU3yk41mwM6KcPu0wmjKGQB1bg99bNdQm4= +github.com/aws/aws-sdk-go-v2/config v1.31.10/go.mod h1:Ge6gzXPjqu4v0oHvgAwvGzYcK921GU0hQM25WF/Kl+8= +github.com/aws/aws-sdk-go-v2/credentials v1.18.14 h1:TxkI7QI+sFkTItN/6cJuMZEIVMFXeu2dI1ZffkXngKI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.14/go.mod h1:12x4Uw/vijC11XkctTjy92TNCQ+UnNJkT7fzX0Yd93E= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 h1:gLD09eaJUdiszm7vd1btiQUYE0Hj+0I2b8AS+75z9AY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8/go.mod h1:4RW3oMPt1POR74qVOC4SbubxAwdP4pCT0nSw3jycOU4= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.8 h1:QcAh/TNGM3MWe95ilMWwnieXWXsyM33Mb/RuTGlWLm4= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.8/go.mod h1:72m/ZCCgYpXJzsgI8uJFYMnXEjtZ4kkaolL9NRXLSnU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 h1:6bgAZgRyT4RoFWhxS+aoGMFyE0cD1bSzFnEEi4bFPGI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8/go.mod h1:KcGkXFVU8U28qS4KvLEcPxytPZPBcRawaH2Pf/0jptE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 h1:HhJYoES3zOz34yWEpGENqJvRVPqpmJyR3+AFg9ybhdY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8/go.mod h1:JnA+hPWeYAVbDssp83tv+ysAG8lTfLVXvSsyKg/7xNA= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.8 h1:1/bT9kDdLQzfZ1e6J6hpW+SfNDd6xrV8F3M2CuGyUz8= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.8/go.mod h1:RbdwTONAIi59ej/+1H+QzZORt5bcyAtbrS7FQb2pvz0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.8 h1:tIN8MFT1z5STK5kTdOT1TCfMN/bn5fSEnlKsTL8qBOU= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.8/go.mod h1:VKS56txtNWjKI8FqD/hliL0BcshyF4ZaLBa1rm2Y+5s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 h1:M6JI2aGFEzYxsF6CXIuRBnkge9Wf9a2xU39rNeXgu10= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8/go.mod h1:Fw+MyTwlwjFsSTE31mH211Np+CUslml8mzc0AFEG09s= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.8 h1:AgYCo1Rb8XChJXA871BXHDNxNWOTAr6V5YdsRIBbgv0= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.8/go.mod h1:Au9dvIGm1Hbqnt29d3VakOCQuN9l0WrkDDTRq8biWS4= +github.com/aws/aws-sdk-go-v2/service/kms v1.45.4 h1:6gzIbiRNs6o/K/WaLta0Vwac0bI9ou3gfx8ASSMf3wU= +github.com/aws/aws-sdk-go-v2/service/kms v1.45.4/go.mod h1:ooAdc5n3rjgEznIXncCYY6V9+YQDcJAYyZDJ4TwLSDM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.88.2 h1:T7b3qniouutV5Wwa9B1q7gW+Y8s1B3g9RE9qa7zLBIM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.88.2/go.mod h1:tW9TsLb6t1eaTdBE6LITyJW1m/+DjQPU78Q/jT2FJu8= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 h1:FTdEN9dtWPB0EOURNtDPmwGp6GGvMqRJCAihkSl/1No= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.4/go.mod h1:mYubxV9Ff42fZH4kexj43gFPhgc/LyC7KqvUKt1watc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 h1:I7ghctfGXrscr7r1Ga/mDqSJKm7Fkpl5Mwq79Z+rZqU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0/go.mod h1:Zo9id81XP6jbayIFWNuDpA6lMBWhsVy+3ou2jLa4JnA= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 h1:+LVB0xBqEgjQoqr9bGZbRzvg212B0f17JdflleJRNR4= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lambda-extensions/config/config.go b/lambda-extensions/config/config.go index 6c84a89..faaeb53 100644 --- a/lambda-extensions/config/config.go +++ b/lambda-extensions/config/config.go @@ -101,7 +101,6 @@ func (cfg *LambdaExtensionConfig) setDefaults() { cfg.TelemetryMaxItems = 10000 } - if numRetry == "" { cfg.NumRetry = 3 } @@ -144,7 +143,7 @@ func (cfg *LambdaExtensionConfig) setDefaults() { // by default, spans will not be dropped if user did not configure the env variable cfg.EnableSpanDrops = false } - + if kmsCacheSeconds == "" { cfg.KmsCacheSeconds = 5 } @@ -292,7 +291,6 @@ func (cfg *LambdaExtensionConfig) validateConfig() error { cfg.TelemetryMaxItems = min(cfg.TelemetryMaxItems, 10000) } - // test valid log format type for _, logType := range cfg.LogTypes { if !utils.StringInSlice(strings.TrimSpace(logType), validLogTypes) { diff --git a/lambda-extensions/config/version.go b/lambda-extensions/config/version.go index de8e51e..c24b6fe 100644 --- a/lambda-extensions/config/version.go +++ b/lambda-extensions/config/version.go @@ -8,7 +8,7 @@ import ( // ExtensionName same as binary name or file name where main exists var ExtensionName = filepath.Base(os.Args[0]) -var layerVersion = "9" +var layerVersion = "11" // SumoLogicExtensionLayerVersionSuffix denotes the layer version published in AWS var SumoLogicExtensionLayerVersionSuffix string = fmt.Sprintf("%s-prod:%s", ExtensionName, layerVersion) diff --git a/lambda-extensions/lambdaapi/extensionapiclient_test.go b/lambda-extensions/lambdaapi/extensionapiclient_test.go index 223b421..a004dc8 100644 --- a/lambda-extensions/lambdaapi/extensionapiclient_test.go +++ b/lambda-extensions/lambdaapi/extensionapiclient_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" ioutil "io" + "log" "net/http" "net/http/httptest" "testing" @@ -16,7 +17,11 @@ func TestRegisterExtension(t *testing.T) { reqBytes, err := ioutil.ReadAll(r.Body) assertNoError(t, err, "Received error while reading request") - defer r.Body.Close() + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() assertNotEmpty(t, reqBytes, "Received error in request") w.Header().Add(extensionIdentiferHeader, "test-sumo-id") @@ -29,7 +34,7 @@ func TestRegisterExtension(t *testing.T) { client := NewClient(srv.URL[7:], extensionName) // Without Context - response, err := client.RegisterExtension(nil) + response, err := client.RegisterExtension(context.TODO()) commonAsserts(t, client, response, err) // With Context @@ -41,7 +46,12 @@ func TestNextEvent(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assertEqual(t, r.Method, http.MethodGet, "Method is not GET") assertNotEmpty(t, r.Header.Get(extensionIdentiferHeader), "Extension ID Header not present") - defer r.Body.Close() + + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() w.Header().Add(extensionIdentiferHeader, "test-sumo-id") w.WriteHeader(200) @@ -58,7 +68,7 @@ func TestNextEvent(t *testing.T) { client := NewClient(srv.URL[7:], extensionName) // Without Context - response, err := client.NextEvent(nil) + response, err := client.NextEvent(context.TODO()) commonAsserts(t, client, response, err) // With Context @@ -75,7 +85,13 @@ func TestInitError(t *testing.T) { reqBytes, err := ioutil.ReadAll(r.Body) assertNoError(t, err, "Received error in request") - defer r.Body.Close() + + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() + assertNotEmpty(t, reqBytes, "Received error in request") w.Header().Add(extensionIdentiferHeader, "test-sumo-id") @@ -88,7 +104,7 @@ func TestInitError(t *testing.T) { client := NewClient(srv.URL[7:], extensionName) // Without Context - response, err := client.InitError(nil, "INIT ERROR") + response, err := client.InitError(context.TODO(), "INIT ERROR") commonAsserts(t, client, response, err) // With Context @@ -105,7 +121,13 @@ func TestExitError(t *testing.T) { reqBytes, err := ioutil.ReadAll(r.Body) assertNoError(t, err, "Received error in request") - defer r.Body.Close() + + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() + assertNotEmpty(t, reqBytes, "Received error in request") w.Header().Add(extensionIdentiferHeader, "test-sumo-id") @@ -118,7 +140,7 @@ func TestExitError(t *testing.T) { client := NewClient(srv.URL[7:], extensionName) // Without Context - response, err := client.ExitError(nil, "EXIT ERROR") + response, err := client.ExitError(context.TODO(), "EXIT ERROR") commonAsserts(t, client, response, err) // With Context diff --git a/lambda-extensions/lambdaapi/httpclient.go b/lambda-extensions/lambdaapi/httpclient.go index 6bcdda2..86997ec 100644 --- a/lambda-extensions/lambdaapi/httpclient.go +++ b/lambda-extensions/lambdaapi/httpclient.go @@ -5,6 +5,7 @@ import ( "context" "fmt" ioutil "io" + "log" "net/http" ) @@ -49,13 +50,17 @@ func (client *Client) MakeRequestWithContext(ctx context.Context, headers map[st if err != nil { return nil, err } - defer httpRes.Body.Close() + defer func() { + if err := httpRes.Body.Close(); err != nil { + log.Printf("failed to close response body: %v", err) + } + }() body, err := ioutil.ReadAll(httpRes.Body) if err != nil { return nil, err } if httpRes.StatusCode != 200 { - return nil, fmt.Errorf("Request failed with status %s and response %s", httpRes.Status, string(body)) + return nil, fmt.Errorf("request failed with status %s and response %s", httpRes.Status, string(body)) } // Get the Extension ID from the headers id := httpRes.Header.Get(extensionIdentiferHeader) @@ -82,13 +87,17 @@ func (client *Client) MakeRequest(headers map[string]string, request *bytes.Buff if err != nil { return nil, err } - defer httpRes.Body.Close() + defer func() { + if err := httpRes.Body.Close(); err != nil { + log.Printf("failed to close response body: %v", err) + } + }() body, err := ioutil.ReadAll(httpRes.Body) if err != nil { return nil, err } if httpRes.StatusCode != 200 { - return nil, fmt.Errorf("Request failed with status %s and response %s", httpRes.Status, string(body)) + return nil, fmt.Errorf("request failed with status %s and response %s", httpRes.Status, string(body)) } // Get the Extension ID from the headers id := httpRes.Header.Get(extensionIdentiferHeader) diff --git a/lambda-extensions/lambdaapi/httpclient_test.go b/lambda-extensions/lambdaapi/httpclient_test.go index d579847..7a54aec 100644 --- a/lambda-extensions/lambdaapi/httpclient_test.go +++ b/lambda-extensions/lambdaapi/httpclient_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "log" "net/http" "net/http/httptest" "testing" @@ -60,7 +61,11 @@ func TestNewClient(t *testing.T) { func createTestServer(t *testing.T) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assertEqual(t, r.Method, http.MethodGet, "Method is not GET.") - defer r.Body.Close() + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() w.Header().Add(extensionIdentiferHeader, "test-sumo-id") w.WriteHeader(200) @@ -91,7 +96,7 @@ func runMakeRequest(ctx context.Context, t *testing.T) ([]byte, *Client, error) } func TestMakeRequest(t *testing.T) { - response, client, err := runMakeRequest(nil, t) + response, client, err := runMakeRequest(context.TODO(), t) commonAsserts(t, client, response, err) } diff --git a/lambda-extensions/lambdaapi/logsapiclient_test.go b/lambda-extensions/lambdaapi/logsapiclient_test.go index 52569f7..b5ddac0 100644 --- a/lambda-extensions/lambdaapi/logsapiclient_test.go +++ b/lambda-extensions/lambdaapi/logsapiclient_test.go @@ -3,6 +3,7 @@ package lambdaapi import ( "context" ioutil "io" + "log" "net/http" "net/http/httptest" "testing" @@ -15,7 +16,11 @@ func TestSubscribeToLogsAPI(t *testing.T) { reqBytes, err := ioutil.ReadAll(r.Body) assertNoError(t, err, "Received error") - defer r.Body.Close() + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() assertNotEmpty(t, reqBytes, "Received error in request") w.Header().Add(extensionIdentiferHeader, "test-sumo-id") @@ -26,7 +31,7 @@ func TestSubscribeToLogsAPI(t *testing.T) { client := NewClient(srv.URL[7:], extensionName) // Without Context - response, err := client.SubscribeToLogsAPI(nil, []string{"platform", "function", "extension"}) + response, err := client.SubscribeToLogsAPI(context.TODO(), []string{"platform", "function", "extension"}) commonAsserts(t, client, response, err) // With Context diff --git a/lambda-extensions/lambdaapi/telemetryapiclient.go b/lambda-extensions/lambdaapi/telemetryapiclient.go index 1ff8e03..65c4336 100644 --- a/lambda-extensions/lambdaapi/telemetryapiclient.go +++ b/lambda-extensions/lambdaapi/telemetryapiclient.go @@ -11,7 +11,7 @@ const ( // Base URL for telemetry api extension telemetryURL = "2022-07-01/telemetry" // Subscription Body Constants. Subscribe to platform logs and receive them on ${local_ip}:4243 via HTTP protocol. - telemetry_receiverPort = 4243 + //telemetry_receiverPort = 4243 ) // SubscribeToLogsAPI is - Subscribe to Logs API to receive the Lambda Logs. diff --git a/lambda-extensions/lambdaapi/telemetryapiclient_test.go b/lambda-extensions/lambdaapi/telemetryapiclient_test.go index fa42064..58a7ce4 100644 --- a/lambda-extensions/lambdaapi/telemetryapiclient_test.go +++ b/lambda-extensions/lambdaapi/telemetryapiclient_test.go @@ -3,6 +3,7 @@ package lambdaapi import ( "context" ioutil "io" + "log" "net/http" "net/http/httptest" "testing" @@ -15,7 +16,11 @@ func TestSubscribeToTelemetryAPI(t *testing.T) { reqBytes, err := ioutil.ReadAll(r.Body) assertNoError(t, err, "Received error") - defer r.Body.Close() + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("failed to close body: %v", err) + } + }() assertNotEmpty(t, reqBytes, "Received error in request") w.Header().Add(extensionIdentiferHeader, "test-sumo-id") @@ -26,7 +31,7 @@ func TestSubscribeToTelemetryAPI(t *testing.T) { client := NewClient(srv.URL[7:], extensionName) // Without Context - response, err := client.SubscribeToTelemetryAPI(nil, []string{"platform", "function", "extension"}, 1000, 262144, 10000) + response, err := client.SubscribeToTelemetryAPI(context.TODO(), []string{"platform", "function", "extension"}, 1000, 262144, 10000) commonAsserts(t, client, response, err) // With Context diff --git a/lambda-extensions/sumoclient/sumoclient.go b/lambda-extensions/sumoclient/sumoclient.go index 40c7510..17c00f0 100644 --- a/lambda-extensions/sumoclient/sumoclient.go +++ b/lambda-extensions/sumoclient/sumoclient.go @@ -8,9 +8,10 @@ import ( "encoding/json" "fmt" "net/http" + "os" "strings" "time" - "os" + "github.com/SumoLogic/sumologic-lambda-extensions/lambda-extensions/utils" "github.com/SumoLogic/sumologic-lambda-extensions/lambda-extensions/config" @@ -72,7 +73,7 @@ func (s *sumoLogicClient) getColdStart() bool { func (s *sumoLogicClient) makeRequest(ctx context.Context, buf *bytes.Buffer) (*http.Response, error) { endpoint, err := s.getHttpEndpoint() if err != nil { - err = fmt.Errorf("Failed to get SUMO HTTP Endpoint error: %v", err) + err = fmt.Errorf("failed to get SUMO HTTP Endpoint error: %v", err) return nil, err } @@ -105,31 +106,29 @@ func (s *sumoLogicClient) getHttpEndpoint() (string, error) { } if s.config.KMSKeyId != "" && (time.Until(kmsEndpointCacheTime) <= 0 || s.config.KmsCacheSeconds == 0) { - + cfg, err := awsConfig.LoadDefaultConfig(context.TODO()) if err != nil { - fmt.Errorf("Configuration error in aws client, error: %v", err) + return "", fmt.Errorf("configuration error in aws client, error: %v", err) } client := kms.NewFromConfig(cfg) blob, err := b64.StdEncoding.DecodeString(s.config.SumoHTTPEndpoint) if err != nil { - fmt.Errorf("Error converting string to blob, error: %v", err) - return "", err + return "", fmt.Errorf("error converting string to blob, error: %v", err) } - + input := &kms.DecryptInput{ - CiphertextBlob: blob, - KeyId: aws.String(s.config.KMSKeyId), - EncryptionContext: map[string]string{"LambdaFunctionName": os.Getenv("AWS_LAMBDA_FUNCTION_NAME")}, + CiphertextBlob: blob, + KeyId: aws.String(s.config.KMSKeyId), + EncryptionContext: map[string]string{"LambdaFunctionName": os.Getenv("AWS_LAMBDA_FUNCTION_NAME")}, } - + result, err := DecodeData(context.TODO(), client, input) - + if err != nil { - fmt.Errorf("Got error decrypting data, error: %v", err) - return "", err + return "", fmt.Errorf("got error decrypting data, error: %v", err) } // Set the decrypted endpoint var as decrypted string to use as cache @@ -141,7 +140,7 @@ func (s *sumoLogicClient) getHttpEndpoint() (string, error) { return decryptedSumoHttpEndpoint, nil } - err := fmt.Errorf("Failed to select a valid Sumo HTTP endpoint") + err := fmt.Errorf("failed to select a valid Sumo HTTP endpoint") return "", err } @@ -185,8 +184,8 @@ func (s *sumoLogicClient) FlushAll(msgQueue [][]byte) error { if len(msgQueue) > 0 && s.config.EnableFailover { s.logger.Debugf("FlushAll - Attempting to send %d payloads from dataqueue to S3", len(msgQueue)) - var errorCount int = 0 - var totalitems int = 0 + var errorCount = 0 + var totalitems = 0 var payload bytes.Buffer for _, rawmsg := range msgQueue { // converting to arr of maps @@ -205,7 +204,7 @@ func (s *sumoLogicClient) FlushAll(msgQueue [][]byte) error { for _, item := range msgArr { b, err := json.Marshal(item) if err != nil { - s.logger.Error("FlushAll - Error in coverting to json: ", err.Error()) + s.logger.Error("FlushAll - Error in converting to json: ", err.Error()) errorCount++ continue } @@ -214,15 +213,19 @@ func (s *sumoLogicClient) FlushAll(msgQueue [][]byte) error { } } s.logger.Debugf("FlushAll - Total log lines transformed: %d", totalitems) + var gzippedBuffer *bytes.Buffer // compressing and pushing to S3 - gzippedBuffer := utils.CompressBuffer(&payload) + gzippedBuffer, err = utils.CompressBuffer(&payload) + if err != nil { + return fmt.Errorf("flushAll - failed to compress log string: %w", err) + } senderr := s.failoverHandler(gzippedBuffer) if errorCount > 0 || senderr != nil { - err = fmt.Errorf("FlushAll - Errors during chunk creation: %d, Errors during flushing to S3: %v", errorCount, senderr) + return fmt.Errorf("flushAll - errors during chunk creation: %d, errors during flushing to S3: %v", errorCount, senderr) } } else { - s.logger.Info("FlushAll - Dropping messages as no failover enabled.") + s.logger.Info("flushAll - Dropping messages as no failover enabled.") } return err } @@ -314,7 +317,7 @@ func (s *sumoLogicClient) transformBytesToArrayOfMap(rawmsg []byte) (responseBod s.logger.Debugln("Transforming bytes to array of maps") var msg responseBody // var err error - var err error = json.Unmarshal(rawmsg, &msg) + var err = json.Unmarshal(rawmsg, &msg) if err != nil { return msg, fmt.Errorf("error in parsing payload %s: %v", string(rawmsg), err) } @@ -326,9 +329,9 @@ func (s *sumoLogicClient) createChunks(msgArr responseBody) ([]string, error) { var err error var chunks []string var itemSize int - var chunkSize int = 0 + var chunkSize = 0 var currentChunk bytes.Buffer - var errorCount int = 0 + var errorCount = 0 for _, item := range msgArr { b, err := json.Marshal(item) if err != nil { @@ -371,7 +374,7 @@ func (s *sumoLogicClient) SendLogs(ctx context.Context, rawmsg []byte) error { if err != nil { return fmt.Errorf("SendLogs - createChunks failed: %v", err) } - var errorCount int = 0 + var errorCount = 0 for _, strobj := range chunks { err := s.postToSumo(ctx, &strobj) if err != nil { @@ -394,8 +397,8 @@ func (s *sumoLogicClient) SendAllLogs(ctx context.Context, allMessages [][]byte) s.logger.Debugf("SendAllLogs: Attempting to send %d payloads from dataqueue to SumoLogic", len(allMessages)) - var errorCount int = 0 - var totalitems int = 0 + var errorCount = 0 + var totalitems = 0 var payload responseBody for _, rawmsg := range allMessages { // converting to arr of maps @@ -411,9 +414,8 @@ func (s *sumoLogicClient) SendAllLogs(ctx context.Context, allMessages [][]byte) s.enhanceLogs(msgArr) totalitems += len(msgArr) // converting back to string - for _, item := range msgArr { - payload = append(payload, item) - } + payload = append(payload, msgArr...) + } } s.logger.Debugf("SendAllLogs: Enhanced TotalLogItems - %d \n", totalitems) @@ -443,7 +445,10 @@ func (s *sumoLogicClient) postToSumo(ctx context.Context, logStringToSend *strin s.logger.Debug("postToSumo: Attempting to send to Sumo Endpoint") // compressing here because Sumo recommends payload size of 1MB before compression - bytedata := utils.Compress(logStringToSend) + bytedata, err := utils.Compress(logStringToSend) + if err != nil { + return fmt.Errorf("failed to compress log string: %w", err) + } createBuffer := func() *bytes.Buffer { dest := make([]byte, len(bytedata)) copy(dest, bytedata) @@ -452,7 +457,11 @@ func (s *sumoLogicClient) postToSumo(ctx context.Context, logStringToSend *strin buf := createBuffer() response, err := s.makeRequest(ctx, buf) if response != nil { - defer response.Body.Close() + defer func() { + if err := response.Body.Close(); err != nil { + s.logger.Debugf("failed to close body: %v", err) + } + }() } if (err != nil) || (response.StatusCode != 200 && response.StatusCode != 302 && response.StatusCode < 500) { s.logger.Errorf("postToSumo: Not able to post statuscode - %v %v\n", err, response) diff --git a/lambda-extensions/sumoclient/sumoclient_test.go b/lambda-extensions/sumoclient/sumoclient_test.go index a694eb8..44db18f 100644 --- a/lambda-extensions/sumoclient/sumoclient_test.go +++ b/lambda-extensions/sumoclient/sumoclient_test.go @@ -18,21 +18,20 @@ import ( ) func setupEnv() { - - os.Setenv("SUMO_NUM_RETRIES", "3") - os.Setenv("SUMO_S3_BUCKET_NAME", "test-bucket") - os.Setenv("SUMO_S3_BUCKET_REGION", "us-east-1") - os.Setenv("AWS_LAMBDA_FUNCTION_NAME", "himlambda") - os.Setenv("AWS_LAMBDA_FUNCTION_VERSION", "Latest$") - os.Setenv("AWS_LAMBDA_LOG_GROUP_NAME", "/aws/lambda/testfunctionpython") - os.Setenv("AWS_LAMBDA_LOG_STREAM_NAME", "2020/11/03/[$LATEST]e5ef8fe91380465fab7da53f5bac50f6") - os.Setenv("SUMO_ENABLE_FAILOVER", "true") - os.Setenv("SUMO_LOG_LEVEL", "5") - os.Setenv("SUMO_MAX_DATAQUEUE_LENGTH", "10") - os.Setenv("SUMO_MAX_CONCURRENT_REQUESTS", "3") - os.Setenv("SUMO_LOG_LEVEL", "DEBUG") - os.Setenv("SUMO_RETRY_SLEEP_TIME_MS", "50") - os.Setenv("SUMO_LOG_TYPES", "function") + _ = os.Setenv("SUMO_NUM_RETRIES", "3") + _ = os.Setenv("SUMO_S3_BUCKET_NAME", "test-bucket-lambda-extension") + _ = os.Setenv("SUMO_S3_BUCKET_REGION", "us-east-1") + _ = os.Setenv("AWS_LAMBDA_FUNCTION_NAME", "himlambda") + _ = os.Setenv("AWS_LAMBDA_FUNCTION_VERSION", "Latest$") + _ = os.Setenv("AWS_LAMBDA_LOG_GROUP_NAME", "/aws/lambda/himlambda") + _ = os.Setenv("AWS_LAMBDA_LOG_STREAM_NAME", "2025/09/26/[$LATEST]9bd560101e724111a0cc6ab5471e3ac6") + _ = os.Setenv("SUMO_ENABLE_FAILOVER", "true") + _ = os.Setenv("SUMO_LOG_LEVEL", "5") + _ = os.Setenv("SUMO_MAX_DATAQUEUE_LENGTH", "10") + _ = os.Setenv("SUMO_MAX_CONCURRENT_REQUESTS", "3") + _ = os.Setenv("SUMO_LOG_LEVEL", "DEBUG") + _ = os.Setenv("SUMO_RETRY_SLEEP_TIME_MS", "50") + _ = os.Setenv("SUMO_LOG_TYPES", "function") } func assertEqual(t *testing.T, a interface{}, b interface{}, message string) { @@ -77,15 +76,23 @@ func TestSumoClient(t *testing.T) { reqBytes, err := ioutil.ReadAll(r.Body) assertEqual(t, err, nil, "Received error") - defer r.Body.Close() + defer func() { + if err := r.Body.Close(); err != nil { + logger.Errorf("failed to close body: %v", err) + } + }() assertNotEmpty(t, reqBytes, "Empty Data in Post") w.WriteHeader(200) })) defer successEndpointServer.Close() - os.Setenv("SUMO_HTTP_ENDPOINT", successEndpointServer.URL) + _ = os.Setenv("SUMO_HTTP_ENDPOINT", successEndpointServer.URL) throttlingEndpointServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() + defer func() { + if err := r.Body.Close(); err != nil { + logger.Errorf("failed to close body: %v", err) + } + }() w.WriteHeader(429) })) defer throttlingEndpointServer.Close() @@ -116,7 +123,6 @@ func TestSumoClient(t *testing.T) { var jsonlinelargedata = []byte(`[{"time":"2020-10-27T15:36:14.133Z","type":"platform.start","record":{"requestId":"7313c951-e0bc-4818-879f-72d202e24727","version":"$LATEST"}},{"time":"2020-10-27T15:36:14.282Z","type":"platform.logsSubscription","record":{"name":"sumologic-extension","state":"Subscribed","types":["platform","function"]}},{"time":"2020-10-27T15:36:14.283Z","type":"function","record":"2020-10-27T15:36:14.281Z\tundefined\tINFO\tLoading function\n"},{"time":"2020-10-27T15:36:14.283Z","type":"platform.extension","record":{"name":"sumologic-extension","state":"Ready","events":["INVOKE"]}},{"time":"2020-10-27T15:36:14.301Z","type":"function","record":"{'log': 'logger error json statement in python: 0'}"},{"time":"2020-10-27T15:36:14.302Z","type":"function","record":"2020-10-27T15:36:14.301Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue2 = value2\n"},{"time":"2020-10-27T15:36:14.302Z","type":"function","record":"2020-10-27T15:36:14.301Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue3 = value3\n"}]`) assertEqual(t, client.SendLogs(ctx, jsonlinelargedata), nil, "SendLogs should not generate error") - t.Log("\ntesting flushall\n======================") var multiplelargedata = [][]byte{ []byte(`[{"time":"2020-10-27T15:36:14.133Z","type":"platform.start","record":{"requestId":"7313c951-e0bc-4818-879f-72d202e24727","version":"$LATEST"}},{"time":"2020-10-27T15:36:14.282Z","type":"platform.logsSubscription","record":{"name":"sumologic-extension","state":"Subscribed","types":["platform","function"]}},{"time":"2020-10-27T15:36:14.283Z","type":"function","record":"2020-10-27T15:36:14.281Z\tundefined\tINFO\tLoading function\n"},{"time":"2020-10-27T15:36:14.283Z","type":"platform.extension","record":{"name":"sumologic-extension","state":"Ready","events":["INVOKE"]}},{"time":"2020-10-27T15:36:14.301Z","type":"function","record":"2020-10-27T15:36:14.285Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue1 = value1\n"},{"time":"2020-10-27T15:36:14.302Z","type":"function","record":"2020-10-27T15:36:14.301Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue2 = value2\n"},{"time":"2020-10-27T15:36:14.302Z","type":"function","record":"2020-10-27T15:36:14.301Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue3 = value3\n"}]`), @@ -124,7 +130,9 @@ func TestSumoClient(t *testing.T) { []byte(`[{"time":"2020-10-27T15:36:14.133Z","type":"platform.start","record":{"requestId":"7313c951-e0bc-4818-879f-72d202e24727","version":"$LATEST"}},{"time":"2020-10-27T15:36:14.282Z","type":"platform.logsSubscription","record":{"name":"sumologic-extension","state":"Subscribed","types":["platform","function"]}},{"time":"2020-10-27T15:36:14.283Z","type":"function","record":"2020-10-27T15:36:14.281Z\tundefined\tINFO\tLoading function\n"},{"time":"2020-10-27T15:36:14.283Z","type":"platform.extension","record":{"name":"sumologic-extension","state":"Ready","events":["INVOKE"]}},{"time":"2020-10-27T15:36:14.301Z","type":"function","record":"2020-10-27T15:36:14.285Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue1 = value1\n"},{"time":"2020-10-27T15:36:14.302Z","type":"function","record":"2020-10-27T15:36:14.301Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue2 = value2\n"},{"time":"2020-10-27T15:36:14.302Z","type":"function","record":"2020-10-27T15:36:14.301Z\t7313c951-e0bc-4818-879f-72d202e24727\tINFO\tvalue3 = value3\n"}]`), } err = client.FlushAll(multiplelargedata) - assertEqual(t, strings.HasPrefix(err.Error(), "FlushAll - Errors during chunk creation: 0, Errors during flushing to S3"), true, "FlushAll should generate error") + if err != nil { + assertEqual(t, strings.HasPrefix(err.Error(), "FlushAll - Errors during chunk creation: 0, Errors during flushing to S3"), true, "FlushAll should generate error") + } //Todo mock S3 client to improve below tests @@ -143,6 +151,7 @@ func TestSumoClient(t *testing.T) { config.SumoHTTPEndpoint = throttlingEndpointServer.URL t.Log("\nretry scenario + failover\n======================") err = client.SendLogs(ctx, logs) - assertEqual(t, strings.HasPrefix(err.Error(), "SendLogs - errors during postToSumo: 1"), true, "SendLogs should generate error") - + if err != nil { + assertEqual(t, strings.HasPrefix(err.Error(), "SendLogs - errors during postToSumo: 1"), true, "SendLogs should generate error") + } } diff --git a/lambda-extensions/sumologic-extension.go b/lambda-extensions/sumologic-extension.go index e8b354b..84fa399 100644 --- a/lambda-extensions/sumologic-extension.go +++ b/lambda-extensions/sumologic-extension.go @@ -49,7 +49,11 @@ func init() { // Start HTTP Server before subscription in a goRoutine producer = workers.NewTaskProducer(dataQueue, logger) - go producer.Start() + go func() { + if err := producer.Start(); err != nil { + logger.Errorf("producer Start failed: %v", err) + } + }() // Creating SumoTaskConsumer consumer = workers.NewTaskConsumer(dataQueue, config, logger) @@ -58,7 +62,7 @@ func init() { func runTimeAPIInit() (int64, error) { // Register early so Runtime could start in parallel logger.Debug("Registering Extension to Run Time API Client..........") - registerResponse, err := extensionClient.RegisterExtension(nil) + registerResponse, err := extensionClient.RegisterExtension(context.TODO()) if err != nil { return 0, err } @@ -66,7 +70,7 @@ func runTimeAPIInit() (int64, error) { // Subscribe to Telemetry API logger.Debug("Subscribing Extension to Telemetry API........") - subscribeResponse, err := extensionClient.SubscribeToTelemetryAPI(nil, config.LogTypes, config.TelemetryTimeoutMs, config.TelemetryMaxBytes, config.TelemetryMaxItems) + subscribeResponse, err := extensionClient.SubscribeToTelemetryAPI(context.TODO(), config.LogTypes, config.TelemetryTimeoutMs, config.TelemetryMaxBytes, config.TelemetryMaxItems) if err != nil { return 0, err } @@ -74,7 +78,7 @@ func runTimeAPIInit() (int64, error) { logger.Debug("Successfully subscribed to Telemetry API: ", utils.PrettyPrint(string(subscribeResponse))) // Call next to say registration is successful and get the deadtimems - nextResponse, err := nextEvent(nil) + nextResponse, err := nextEvent(context.TODO()) if err != nil { return 0, err } @@ -146,7 +150,10 @@ func main() { defer func() { if err := recover(); err != nil { logger.Error("Extension failed:", err) - nextEvent(ctx) + _, err := nextEvent(ctx) + if err != nil { + logger.Error("error during Next Event call: ", err.Error()) + } } }() // Will block until shutdown event is received or cancelled via the context. diff --git a/lambda-extensions/utils/awsutils.go b/lambda-extensions/utils/awsutils.go index ead0b4b..619353f 100644 --- a/lambda-extensions/utils/awsutils.go +++ b/lambda-extensions/utils/awsutils.go @@ -1,42 +1,43 @@ package utils import ( + "context" "io" "os" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/feature/s3/manager" + "github.com/aws/aws-sdk-go-v2/service/s3" ) -var uploader *s3manager.Uploader -var sess *session.Session +var uploader *manager.Uploader func init() { - - var awsRegion, found = os.LookupEnv("SUMO_S3_BUCKET_REGION") - + awsRegion, found := os.LookupEnv("SUMO_S3_BUCKET_REGION") if !found { awsRegion = os.Getenv("AWS_REGION") } - sess = session.Must(session.NewSession(&aws.Config{ - Region: aws.String(awsRegion)})) + // Load AWS default config with region + cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithRegion(awsRegion), + ) + if err != nil { + panic("unable to load AWS SDK config, " + err.Error()) + } - // Create an uploader with the session and default options - uploader = s3manager.NewUploader(sess) + // Create S3 client + s3Client := s3.NewFromConfig(cfg) + // Create uploader + uploader = manager.NewUploader(s3Client) } -// UploadToS3 send data to S3 +// UploadToS3 sends data to S3 func UploadToS3(bucketName *string, keyName *string, data io.Reader) error { - - upParams := &s3manager.UploadInput{ + _, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: bucketName, Key: keyName, Body: data, - } - _, err := uploader.Upload(upParams) - + }) return err } diff --git a/lambda-extensions/utils/utils.go b/lambda-extensions/utils/utils.go index 083f0c1..575d10b 100644 --- a/lambda-extensions/utils/utils.go +++ b/lambda-extensions/utils/utils.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "encoding/json" "errors" + "fmt" ) //------------------Retry Logic Code------------------------------- @@ -18,7 +19,7 @@ type Func func(attempt int) (retry bool, err error) func Retry(fn Func, maxRetries int) error { var err error var cont bool - var attempt int = 1 + var attempt = 1 for { if attempt > maxRetries { return errMaxRetriesReached @@ -44,23 +45,35 @@ func StringInSlice(a string, list []string) bool { } // Compress compresses string and returns byte array -func Compress(logStringToSend *string) []byte { - +func Compress(logStringToSend *string) ([]byte, error) { var buf bytes.Buffer g := gzip.NewWriter(&buf) - g.Write([]byte(*logStringToSend)) - g.Close() - return buf.Bytes() + + if _, err := g.Write([]byte(*logStringToSend)); err != nil { + return nil, fmt.Errorf("failed to write log string: %w", err) + } + + if err := g.Close(); err != nil { + return nil, fmt.Errorf("failed to close gzip writer: %w", err) + } + + return buf.Bytes(), nil } // CompressBuffer compresses string and returns byte array -func CompressBuffer(inputbuf *bytes.Buffer) *bytes.Buffer { - +func CompressBuffer(inputbuf *bytes.Buffer) (*bytes.Buffer, error) { var outputbuf bytes.Buffer g := gzip.NewWriter(&outputbuf) - g.Write(inputbuf.Bytes()) - g.Close() - return &outputbuf + + if _, err := g.Write(inputbuf.Bytes()); err != nil { + return nil, fmt.Errorf("failed to compress buffer: %w", err) + } + + if err := g.Close(); err != nil { + return nil, fmt.Errorf("failed to close gzip writer: %w", err) + } + + return &outputbuf, nil } // PrettyPrint is to print the object diff --git a/lambda-extensions/workers/consumer.go b/lambda-extensions/workers/consumer.go index d98d7f3..7ef03f5 100644 --- a/lambda-extensions/workers/consumer.go +++ b/lambda-extensions/workers/consumer.go @@ -2,7 +2,6 @@ package workers import ( "context" - "fmt" "strings" "sync" @@ -87,22 +86,21 @@ func (sc *sumoConsumer) consumeTask(ctx context.Context, wg *sync.WaitGroup, raw sc.dataQueue <- rawmsg // TODO: raise alert if send logs fails } - return } func (sc *sumoConsumer) DrainQueue(ctx context.Context) int { //sc.logger.Debug("Consuming data from dataQueue") var rawMsgArr [][]byte - var logsStr string = "" - var runtime_done int = 0 + var logsStr string + var runtime_done = 0 Loop: for { //Receives block when the buffer is empty. select { case rawmsg := <-sc.dataQueue: rawMsgArr = append(rawMsgArr, rawmsg) - logsStr = fmt.Sprintf("%s", rawmsg) + logsStr = string(rawmsg) sc.logger.Debugf("DrainQueue: logsStr: %s", logsStr) if strings.Contains(logsStr, string(RuntimeDone)) { runtime_done = 1 diff --git a/lambda-extensions/workers/producer.go b/lambda-extensions/workers/producer.go index 774b7c4..85dd666 100644 --- a/lambda-extensions/workers/producer.go +++ b/lambda-extensions/workers/producer.go @@ -48,7 +48,11 @@ func (httpServer *httpServer) logsHandler(writer http.ResponseWriter, request *h } switch request.Method { case "POST": - defer request.Body.Close() + defer func() { + if err := request.Body.Close(); err != nil { + httpServer.logger.Errorf("failed to close body: %v", err) + } + }() reqBody, err := ioutil.ReadAll(request.Body) if err != nil { // TODO: raise alert if read fails diff --git a/scripts/verify_layer_versions.sh b/scripts/verify_layer_versions.sh new file mode 100755 index 0000000..7dc58dc --- /dev/null +++ b/scripts/verify_layer_versions.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +LAYER_NAME="sumologic-extension-arm64" + +AWS_REGIONS=( + us-east-1 + us-east-2 + eu-north-1 + ap-south-1 + eu-west-3 + eu-west-2 + eu-south-1 + eu-west-1 + ap-northeast-2 + me-south-1 + ap-northeast-1 + sa-east-1 + ca-central-1 + ap-east-1 + ap-southeast-1 + ap-southeast-2 + eu-central-1 + us-west-1 + us-west-2 +) + +echo "Fetching latest version of layer: $LAYER_NAME" +echo "=================================================" + +for region in "${AWS_REGIONS[@]}"; do + latest_version=$(aws lambda list-layer-versions \ + --layer-name "$LAYER_NAME" \ + --region "$region" \ + --query 'max_by(LayerVersions, &Version).Version' \ + --output text 2>/dev/null) + + if [ "$latest_version" != "None" ] && [ -n "$latest_version" ]; then + echo "Region: $region -> Latest version: $latest_version" + else + echo "Region: $region -> Layer not found" + fi +done