diff --git a/examples/examples_nodejs_test.go b/examples/examples_nodejs_test.go index a4b8aeb1c6d..9b94ebfdedd 100644 --- a/examples/examples_nodejs_test.go +++ b/examples/examples_nodejs_test.go @@ -17,6 +17,8 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/s3" + "github.com/pulumi/providertest/pulumitest" + "github.com/pulumi/providertest/pulumitest/opttest" "github.com/pulumi/pulumi/pkg/v3/testing/integration" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -577,3 +579,35 @@ func getAwsSession(t *testing.T) *session.Session { require.NoError(t, err) return sess } + +func TestUpdateImportedLambda(t *testing.T) { + test := pulumitest.NewPulumiTest(t, "lambda-import-ts", + opttest.LocalProviderPath("aws", filepath.Join(getCwd(t), "..", "bin")), + ) + + test.SetConfig("runtime", "nodejs18.x") + res := test.Up() + lambdaName := res.Outputs["lambda_name"] + lambdaRole := res.Outputs["lambda_role"] + + secondStack := test.InstallStack("new_stack") + + // Check that we can reimport the lambda. + secondStack.SetConfig("lambda_name", lambdaName.Value.(string)) + secondStack.SetConfig("runtime", "nodejs18.x") + secondStack.SetConfig("lambda_role", lambdaRole.Value.(string)) + secondStack.Up() + + // Check that we can change a property on the lambda + secondStack.SetConfig("runtime", "nodejs16.x") + secondStack.Up() +} + +func TestNoCodeLambda(t *testing.T) { + test := pulumitest.NewPulumiTest(t, "no-code-lambda", + opttest.LocalProviderPath("aws", filepath.Join(getCwd(t), "..", "bin")), + ) + _, err := test.CurrentStack().Up(test.Context()) + require.Error(t, err) + require.Contains(t, err.Error(), "ValidationException") +} diff --git a/examples/examples_test.go b/examples/examples_test.go index f6632119226..807da5bdc7a 100644 --- a/examples/examples_test.go +++ b/examples/examples_test.go @@ -586,6 +586,136 @@ func TestWrongStateMaxItemOneDiffProduced(t *testing.T) { replay(t, repro) } +func TestSourceCodeHashImportedLambdaChecksCleanly(t *testing.T) { + replay(t, ` + [{ + "method": "/pulumirpc.ResourceProvider/Check", + "request": { + "urn": "urn:pulumi:imported::repro_lambda::aws:lambda/function:Function::mylambda", + "olds": { + "__defaults": [], + "architectures": [ + "x86_64" + ], + "environment": { + "__defaults": [], + "variables": { + "__defaults": [], + "foo": "bar" + } + }, + "ephemeralStorage": { + "__defaults": [], + "size": 512 + }, + "handler": "index.test", + "loggingConfig": { + "__defaults": [], + "logFormat": "Text", + "logGroup": "/aws/lambda/testLambda-83906f2" + }, + "name": "testLambda-83906f2", + "packageType": "Zip", + "role": "arn:aws:iam::616138583583:role/iamForLambda-d5757fe", + "runtime": "nodejs18.x", + "sourceCodeHash": "WUsPYQdwiMj+sDZzl3tNaSzS42vqVfng2CZtgcy+TRs=", + "tracingConfig": { + "__defaults": [], + "mode": "PassThrough" + } + }, + "news": { + "__defaults": [], + "architectures": [ + "x86_64" + ], + "environment": { + "__defaults": [], + "variables": { + "__defaults": [], + "foo": "bar" + } + }, + "ephemeralStorage": { + "__defaults": [], + "size": 512 + }, + "handler": "index.test", + "loggingConfig": { + "__defaults": [], + "logFormat": "Text", + "logGroup": "/aws/lambda/testLambda-83906f2" + }, + "name": "testLambda-83906f2", + "packageType": "Zip", + "role": "arn:aws:iam::616138583583:role/iamForLambda-d5757fe", + "runtime": "nodejs18.x", + "sourceCodeHash": "WUsPYQdwiMj+sDZzl3tNaSzS42vqVfng2CZtgcy+TRs=", + "tracingConfig": { + "__defaults": [], + "mode": "PassThrough" + } + }, + "randomSeed": "MpcMRlpQV7R8mD8Nmx9KpLPPIyFTHsB8HA4kxXdWTfo=" + }, + "response": { + "inputs": { + "__defaults": [ + "memorySize", + "publish", + "reservedConcurrentExecutions", + "skipDestroy", + "timeout" + ], + "architectures": [ + "x86_64" + ], + "environment": { + "__defaults": [], + "variables": { + "__defaults": [], + "foo": "bar" + } + }, + "ephemeralStorage": { + "__defaults": [], + "size": 512 + }, + "handler": "index.test", + "loggingConfig": { + "__defaults": [ + "applicationLogLevel", + "systemLogLevel" + ], + "applicationLogLevel": "", + "logFormat": "Text", + "logGroup": "/aws/lambda/testLambda-83906f2", + "systemLogLevel": "" + }, + "memorySize": 128, + "name": "testLambda-83906f2", + "packageType": "Zip", + "publish": false, + "reservedConcurrentExecutions": -1, + "role": "arn:aws:iam::616138583583:role/iamForLambda-d5757fe", + "runtime": "nodejs18.x", + "skipDestroy": false, + "sourceCodeHash": "WUsPYQdwiMj+sDZzl3tNaSzS42vqVfng2CZtgcy+TRs=", + "timeout": 3, + "tracingConfig": { + "__defaults": [], + "mode": "PassThrough" + } + } + }, + "metadata": { + "kind": "resource", + "mode": "client", + "name": "aws" + } +}]`) +} + // A lot of tests do not currently refresh cleanly. The work to root cause each tests has not been // done yet but the common causes are listed here: // diff --git a/examples/go.mod b/examples/go.mod index b254f2b73aa..5b8dd3a1644 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -4,6 +4,7 @@ go 1.21.0 require ( github.com/aws/aws-sdk-go v1.50.13 + github.com/pulumi/providertest v0.0.10 github.com/pulumi/pulumi-aws/provider/v6 v6.0.0-00010101000000-000000000000 github.com/pulumi/pulumi-terraform-bridge/pf v0.27.0 github.com/pulumi/pulumi-terraform-bridge/testing v0.0.2-0.20230927165309-e3fd9503f2d3 @@ -207,6 +208,7 @@ require ( github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/color v1.16.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/gertd/go-pluralize v0.2.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect @@ -299,6 +301,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/natefinch/atomic v1.0.1 // indirect + github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/run v1.1.0 // indirect github.com/opentracing/basictracer-go v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect @@ -368,6 +371,7 @@ require ( google.golang.org/grpc v1.61.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/examples/go.sum b/examples/go.sum index 03820dd37a2..56395eea054 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1478,6 +1478,12 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8= +github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.4.9 h1:x6+GEQeYWC+cnLNsHK5uXXgEQADmlH/1EqMrjfXjzk8= +github.com/gkampitakis/go-snaps v0.4.9/go.mod h1:8HW4KX3JKV8M0GSw69CvT+Jqhd1AlBPMPpBfjBI3bdY= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= @@ -2630,7 +2636,15 @@ github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0= github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/examples/lambda-import-ts/.gitignore b/examples/lambda-import-ts/.gitignore new file mode 100644 index 00000000000..c6958891dd2 --- /dev/null +++ b/examples/lambda-import-ts/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/node_modules/ diff --git a/examples/lambda-import-ts/Pulumi.yaml b/examples/lambda-import-ts/Pulumi.yaml new file mode 100644 index 00000000000..590f6e44dde --- /dev/null +++ b/examples/lambda-import-ts/Pulumi.yaml @@ -0,0 +1,7 @@ +name: lambda-import-ts +runtime: nodejs +description: A minimal AWS TypeScript Pulumi program +config: + pulumi:tags: + value: + pulumi:template: aws-typescript diff --git a/examples/lambda-import-ts/index.ts b/examples/lambda-import-ts/index.ts new file mode 100644 index 00000000000..42459342ac3 --- /dev/null +++ b/examples/lambda-import-ts/index.ts @@ -0,0 +1,44 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as archive from "@pulumi/archive"; +import * as aws from "@pulumi/aws"; + +const config = new pulumi.Config(); +const imported_lambda_name = config.get("lambda_name"); +const imported_lambda_role = config.get("lambda_role"); + +const runtime = config.get("runtime"); + +const assumeRole = aws.iam.getPolicyDocument({ + statements: [{ + effect: "Allow", + principals: [{ + type: "Service", + identifiers: ["lambda.amazonaws.com"], + }], + actions: ["sts:AssumeRole"], + }], +}); +const iamForLambda = new aws.iam.Role("iamForLambda", {assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json)}); +const lambda = archive.getFile({ + type: "zip", + sourceFile: "lambda.js", + outputPath: "lambda_function_payload.zip", +}); + +const testLambda = new aws.lambda.Function("testLambda", { + name: imported_lambda_name, + code: imported_lambda_name ? undefined : new pulumi.asset.FileArchive("lambda_function_payload.zip"), + role: imported_lambda_role || iamForLambda.arn, + handler: "index.test", + runtime: runtime || "nodejs18.x", + environment: { + variables: { + foo: "bar", + }, + }, +}, imported_lambda_name ? { + import: imported_lambda_name, ignoreChanges: ["code", "filename", "s3_bucket", "source_code_hash", "image_uri"] +} : undefined); + +export const lambda_name = testLambda.name; +export const lambda_role = testLambda.role; \ No newline at end of file diff --git a/examples/lambda-import-ts/lambda.js b/examples/lambda-import-ts/lambda.js new file mode 100644 index 00000000000..07816631635 --- /dev/null +++ b/examples/lambda-import-ts/lambda.js @@ -0,0 +1 @@ +console.log("hi") \ No newline at end of file diff --git a/examples/lambda-import-ts/package.json b/examples/lambda-import-ts/package.json new file mode 100644 index 00000000000..43363d71fb3 --- /dev/null +++ b/examples/lambda-import-ts/package.json @@ -0,0 +1,13 @@ +{ + "name": "lambda-import-ts", + "main": "index.ts", + "devDependencies": { + "@types/node": "^18" + }, + "dependencies": { + "@pulumi/pulumi": "^3.0.0", + "@pulumi/aws": "5.28.0", + "@pulumi/awsx": "^2.0.2", + "@pulumi/archive": "^0.0.4" + } +} diff --git a/examples/lambda-import-ts/tsconfig.json b/examples/lambda-import-ts/tsconfig.json new file mode 100644 index 00000000000..ab65afa6135 --- /dev/null +++ b/examples/lambda-import-ts/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/examples/no-code-lambda/Pulumi.yaml b/examples/no-code-lambda/Pulumi.yaml new file mode 100644 index 00000000000..4d807fe8f35 --- /dev/null +++ b/examples/no-code-lambda/Pulumi.yaml @@ -0,0 +1,34 @@ +name: no-code-lambda +runtime: yaml +description: A minimal AWS Pulumi YAML program +config: + pulumi:tags: + value: + pulumi:template: aws-yaml +resources: + iamForLambda: + type: aws:iam:Role + properties: + assumeRolePolicy: ${assumeRole.json} + testLambda: + type: aws:lambda:Function + properties: + role: ${iamForLambda.arn} + handler: index.test + runtime: nodejs18.x + environment: + variables: + foo: bar +variables: + assumeRole: + fn::invoke: + Function: aws:iam:getPolicyDocument + Arguments: + statements: + - effect: Allow + principals: + - type: Service + identifiers: + - lambda.amazonaws.com + actions: + - sts:AssumeRole diff --git a/patches/0044-Allow-creating-lambdas-without-code-related-properti.patch b/patches/0044-Allow-creating-lambdas-without-code-related-properti.patch new file mode 100644 index 00000000000..983f832acfa --- /dev/null +++ b/patches/0044-Allow-creating-lambdas-without-code-related-properti.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Venelin +Date: Tue, 13 Feb 2024 11:36:02 +0000 +Subject: [PATCH 44/44] Allow creating lambdas without code related properties + + +diff --git a/internal/service/lambda/function.go b/internal/service/lambda/function.go +index ba93ca7987..9ebfcbe4f1 100644 +--- a/internal/service/lambda/function.go ++++ b/internal/service/lambda/function.go +@@ -169,7 +169,7 @@ func ResourceFunction() *schema.Resource { + "filename": { + Type: schema.TypeString, + Optional: true, +- ExactlyOneOf: []string{"filename", "image_uri", "s3_bucket"}, ++ ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version", "image_uri"}, + }, + "function_name": { + Type: schema.TypeString, +@@ -208,7 +208,7 @@ func ResourceFunction() *schema.Resource { + "image_uri": { + Type: schema.TypeString, + Optional: true, +- ExactlyOneOf: []string{"filename", "image_uri", "s3_bucket"}, ++ ConflictsWith: []string{"filename", "s3_bucket", "s3_key", "s3_object_version"}, + }, + "invoke_arn": { + Type: schema.TypeString, +@@ -326,7 +326,7 @@ func ResourceFunction() *schema.Resource { + "s3_bucket": { + Type: schema.TypeString, + Optional: true, +- ExactlyOneOf: []string{"filename", "image_uri", "s3_bucket"}, ++ ConflictsWith: []string{"filename", "image_uri"}, + RequiredWith: []string{"s3_key"}, + }, + "s3_key": {