diff --git a/go/rekognition-lambda-s3-trigger/.gitignore b/go/rekognition-lambda-s3-trigger/.gitignore new file mode 100644 index 0000000000..92fe1ec34b --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/.gitignore @@ -0,0 +1,19 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# go.sum should be committed +!go.sum + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/go/rekognition-lambda-s3-trigger/README.md b/go/rekognition-lambda-s3-trigger/README.md new file mode 100644 index 0000000000..24399a4b45 --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/README.md @@ -0,0 +1,21 @@ +# Rekognition Lambda S3 Trigger + + +## How it works + +This project creates the following in your AWS account: + +- S3 bucket +- DynamoDB table +- Lambda function that performs image classification via AWS Rekognition when new images are uploaded to the S3 bucket +- Roles and policies allowing appropriate access to these resources +- Rekognition labels are written to the DynamoDb table. + +## Deploy +Run `cdk deploy`. This will deploy / redeploy your Stack to your AWS Account. + +## Manage GO dependencies +Run `go mod tidy` + +## Synthesize Cloudformation Template +To see the Cloudformation template generated by the CDK, run `cdk synth`. diff --git a/go/rekognition-lambda-s3-trigger/cdk.json b/go/rekognition-lambda-s3-trigger/cdk.json new file mode 100644 index 0000000000..c3acfea983 --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/cdk.json @@ -0,0 +1,74 @@ +{ + "app": "go mod download && go run rekognition-lambda-s3-trigger.go", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "go.mod", + "go.sum", + "**/*test.go" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true + } +} diff --git a/go/rekognition-lambda-s3-trigger/go.mod b/go/rekognition-lambda-s3-trigger/go.mod new file mode 100644 index 0000000000..3816dd2c5b --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/go.mod @@ -0,0 +1,48 @@ +module rekognition-lambda-s3-trigger + +go 1.22.0 + +toolchain go1.22.5 + +require ( + github.com/aws/aws-cdk-go/awscdk/v2 v2.167.1 + github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2 v2.167.1-alpha.0 + github.com/aws/aws-lambda-go v1.47.0 + github.com/aws/aws-sdk-go v1.55.5 + github.com/aws/aws-sdk-go-v2 v1.32.5 + github.com/aws/aws-sdk-go-v2/config v1.28.5 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.37.1 + github.com/aws/aws-sdk-go-v2/service/rekognition v1.45.7 + github.com/aws/constructs-go/constructs/v10 v10.4.2 + github.com/aws/jsii-runtime-go v1.104.0 +) + +require ( + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect + github.com/aws/smithy-go v1.22.1 // indirect + github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.208 // indirect + github.com/cdklabs/awscdk-asset-kubectl-go/kubectlv20/v2 v2.1.3 // indirect + github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0 // indirect + github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v38 v38.0.1 // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/yuin/goldmark v1.4.13 // indirect + golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/tools v0.26.0 // indirect +) diff --git a/go/rekognition-lambda-s3-trigger/go.sum b/go/rekognition-lambda-s3-trigger/go.sum new file mode 100644 index 0000000000..3ca4cfeb3b --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/go.sum @@ -0,0 +1,103 @@ +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/aws/aws-cdk-go/awscdk/v2 v2.167.1 h1:bOahXz/UjFBn8yBpUlV/xUgwR6OE8Cu68hV4CXujeZY= +github.com/aws/aws-cdk-go/awscdk/v2 v2.167.1/go.mod h1:rBXrKmhrluYikJ2BNzOc9Ngs2F3W5gZYiiVsKiIoBZk= +github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2 v2.167.1-alpha.0 h1:irBcUS6ZJapU2dbNR6cOWQ8+bkmU3iuejzsw3J6XwpY= +github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2 v2.167.1-alpha.0/go.mod h1:MX70Y0OT3ulL7vr5wLLCWV9TdSge4Sajr9NG5SsJJgA= +github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI= +github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= +github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= +github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= +github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.37.1 h1:vucMirlM6D+RDU8ncKaSZ/5dGrXNajozVwpmWNPn2gQ= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.37.1/go.mod h1:fceORfs010mNxZbQhfqUjUeHlTwANmIT4mvHamuUaUg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.5 h1:3Y457U2eGukmjYjeHG6kanZpDzJADa2m0ADqnuePYVQ= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.5/go.mod h1:CfwEHGkTjYZpkQ/5PvcbEtT7AJlG68KkEvmtwU8z3/U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= +github.com/aws/aws-sdk-go-v2/service/rekognition v1.45.7 h1:kC6qaN1AVzhBzDo/0sUCdkJVcamuMslppfys4oGtxR4= +github.com/aws/aws-sdk-go-v2/service/rekognition v1.45.7/go.mod h1:/OYd+ham4lcrARFxWjW+TzBZ0u1gprKqbOUAnX4e0D4= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= +github.com/aws/constructs-go/constructs/v10 v10.4.2 h1:+hDLTsFGLJmKIn0Dg20vWpKBrVnFrEWYgTEY5UiTEG8= +github.com/aws/constructs-go/constructs/v10 v10.4.2/go.mod h1:cXsNCKDV+9eR9zYYfwy6QuE4uPFp6jsq6TtH1MwBx9w= +github.com/aws/jsii-runtime-go v1.104.0 h1:651Sh6J2FtatfnVzlOQ3/Ye1WWPAseZ6E/tSQxEKdSI= +github.com/aws/jsii-runtime-go v1.104.0/go.mod h1:7ZmQXxV0AAhhvv/GaHX4n6zbgA1tSRVdnQYAJbIhXHk= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.208 h1:paqCb41y89OFA3+APC7buPjM6Izq8Q0yMgkGSGf/QJI= +github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.208/go.mod h1:zydxApP2CyfXc3jMpJdyb1ERPNVXaCH/LJTKfWfzUBA= +github.com/cdklabs/awscdk-asset-kubectl-go/kubectlv20/v2 v2.1.3 h1:u63i5K09xASBNGEM0hpD2z0JwO+iNLVzG74VG85OjDo= +github.com/cdklabs/awscdk-asset-kubectl-go/kubectlv20/v2 v2.1.3/go.mod h1:0xP6iiSIKPKsShb6T2kadnTq61wL+kwmHTHnEjkFZFI= +github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0 h1:kElXjprC8wkpJu58vp+WFH6z0AJw4zitg5iSKJPKe3c= +github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0/go.mod h1:JY4UnvNa1YDGQ4H5wohXTHl6YVY3uCDUWl4JYUrQfb8= +github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v38 v38.0.1 h1:EJ0N5jiEm1bet7Mu8IU5ccERvOpki10wI0zOhIQCO1U= +github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v38 v38.0.1/go.mod h1:WMWAzkRBUPWJ5Ord1ZL2KOTdqByf01PoL5EV9K9PYKQ= +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/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go/rekognition-lambda-s3-trigger/lambda-handler/main.go b/go/rekognition-lambda-s3-trigger/lambda-handler/main.go new file mode 100644 index 0000000000..372c4197ae --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/lambda-handler/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/aws/aws-lambda-go/lambda" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/aws/aws-sdk-go-v2/service/rekognition" + rekognitionTypes "github.com/aws/aws-sdk-go-v2/service/rekognition/types" +) + +type Event struct { + Records []struct { + S3 struct { + Object struct { + Key string `json:"key"` + } `json:"object"` + } `json:"s3"` + } `json:"Records"` +} + +func handler(event Event) (*dynamodb.PutItemOutput, error) { + // Load config + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(fmt.Errorf("unable to load SDK config, %v", err)) + } + + // Create clients + dynamodbClient := dynamodb.NewFromConfig(cfg) + rekognitionClient := rekognition.NewFromConfig(cfg) + + key := event.Records[0].S3.Object.Key + + // Detect labels in the image + detectLabelsInput := &rekognition.DetectLabelsInput{ + Image: &rekognitionTypes.Image{ + S3Object: &rekognitionTypes.S3Object{ + Bucket: aws.String(os.Getenv("BUCKET_NAME")), + Name: aws.String(key), + }, + }, + MaxLabels: aws.Int32(10), + MinConfidence: aws.Float32(70), + } + detectLabelsOutput, err := rekognitionClient.DetectLabels(context.TODO(), detectLabelsInput) + if err != nil { + panic(fmt.Errorf("unable to detect labels, %v", err)) + } + + // Get the list of labels + labels := make([]string, 0, len(detectLabelsOutput.Labels)) + for _, label := range detectLabelsOutput.Labels { + labels = append(labels, *label.Name) + } + fmt.Println(labels) + + // Write the image name and labels to DynamoDB + tableName := os.Getenv("TABLE_NAME") + putItemInput := &dynamodb.PutItemInput{ + TableName: aws.String(tableName), + Item: map[string]types.AttributeValue{ + "image_name": &types.AttributeValueMemberS{Value: key}, + "labels": &types.AttributeValueMemberS{Value: fmt.Sprintf("%v", labels)}, + }, + ConditionExpression: aws.String("attribute_not_exists(image_name)"), + } + output, err := dynamodbClient.PutItem(context.TODO(), putItemInput) + if err != nil { + panic(fmt.Errorf("unable to put item, %v", err)) + } + return output, nil +} + +func main() { + lambda.Start(handler) +} diff --git a/go/rekognition-lambda-s3-trigger/rekognition-lambda-s3-trigger.go b/go/rekognition-lambda-s3-trigger/rekognition-lambda-s3-trigger.go new file mode 100644 index 0000000000..9a55efda35 --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/rekognition-lambda-s3-trigger.go @@ -0,0 +1,119 @@ +package main + +import ( + "log" + "os" + + "github.com/aws/aws-cdk-go/awscdk/v2" + "github.com/aws/aws-cdk-go/awscdk/v2/awsdynamodb" + "github.com/aws/aws-cdk-go/awscdk/v2/awsiam" + "github.com/aws/aws-cdk-go/awscdk/v2/awslambda" + "github.com/aws/aws-cdk-go/awscdk/v2/awss3" + "github.com/aws/aws-cdk-go/awscdk/v2/awss3notifications" + "github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2" + "github.com/aws/aws-sdk-go/aws" + + "github.com/aws/constructs-go/constructs/v10" + "github.com/aws/jsii-runtime-go" +) + +type RekognitionLambdaS3TriggerStackProps struct { + awscdk.StackProps +} + +func NewRekognitionLambdaS3TriggerStack(scope constructs.Construct, id string, props *RekognitionLambdaS3TriggerStackProps) awscdk.Stack { + var sprops awscdk.StackProps + if props != nil { + sprops = props.StackProps + } + _, err := os.Getwd() + if err != nil { + log.Println(err) + } + stack := awscdk.NewStack(scope, &id, &sprops) + + // Create S3 Bucket + bucket := awss3.NewBucket(stack, aws.String("Bucket"), &awss3.BucketProps{ + RemovalPolicy: awscdk.RemovalPolicy_DESTROY, + }) + + // Create DynamoDb table + table := awsdynamodb.NewTable(stack, aws.String("Classifications"), &awsdynamodb.TableProps{ + PartitionKey: &awsdynamodb.Attribute{ + Name: aws.String("image_name"), + Type: awsdynamodb.AttributeType_STRING, + }, + RemovalPolicy: awscdk.RemovalPolicy_DESTROY, + }) + + //Create Lambda function + function := awscdklambdagoalpha.NewGoFunction(stack, jsii.String("main"), &awscdklambdagoalpha.GoFunctionProps{ + Runtime: awslambda.Runtime_PROVIDED_AL2023(), + Entry: jsii.String("./lambda-handler"), + Bundling: &awscdklambdagoalpha.BundlingOptions{ + GoBuildFlags: jsii.Strings(`-ldflags "-s -w"`), + }, + Environment: &map[string]*string{ + "BUCKET_NAME": bucket.BucketName(), + "TABLE_NAME": table.TableName(), + }, + }) + + statement := awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{ + Actions: &[]*string{jsii.String("rekognition:DetectLabels")}, + Resources: &[]*string{jsii.String("*")}, + }) + + function.AddToRolePolicy(statement) + + //Add event notifications for bucket + bucket.AddEventNotification(awss3.EventType_OBJECT_CREATED, awss3notifications.NewLambdaDestination(function), &awss3.NotificationKeyFilter{ + Suffix: jsii.String(".jpg"), + }) + bucket.AddEventNotification(awss3.EventType_OBJECT_CREATED, awss3notifications.NewLambdaDestination(function), &awss3.NotificationKeyFilter{ + Suffix: jsii.String(".jpeg"), + }) + bucket.AddEventNotification(awss3.EventType_OBJECT_CREATED, awss3notifications.NewLambdaDestination(function), &awss3.NotificationKeyFilter{ + Suffix: jsii.String(".png"), + }) + + table.GrantReadWriteData(function) + bucket.GrantReadWrite(function, nil) + + // Create CloudFormation outputs + awscdk.NewCfnOutput(stack, jsii.String("UploadImageToS3"), &awscdk.CfnOutputProps{ + Value: jsii.Sprintf("aws s3 cp s3://%s/", aws.StringValue(bucket.BucketName())), + Description: jsii.String("Upload an image to S3 (using AWS CLI) to trigger Rekognition"), + }) + awscdk.NewCfnOutput(stack, jsii.String("DynamoDBTable"), &awscdk.CfnOutputProps{ + Value: table.TableName(), + Description: jsii.String("This is where the image Rekognition results will be stored."), + }) + awscdk.NewCfnOutput(stack, jsii.String("LambdaFunction"), &awscdk.CfnOutputProps{ + Value: function.FunctionName(), + }) + awscdk.NewCfnOutput(stack, jsii.String("LambdaFunctionLogs"), &awscdk.CfnOutputProps{ + Value: function.LogGroup().LogGroupName(), + }) + + return stack +} + +func main() { + defer jsii.Close() + + app := awscdk.NewApp(nil) + + NewRekognitionLambdaS3TriggerStack(app, "RekognitionLambdaS3TriggerStack", &RekognitionLambdaS3TriggerStackProps{ + awscdk.StackProps{ + Env: env(), + }, + }) + + app.Synth(nil) +} + +func env() *awscdk.Environment { + return nil + +} diff --git a/go/rekognition-lambda-s3-trigger/rekognition-lambda-s3-trigger_test.go b/go/rekognition-lambda-s3-trigger/rekognition-lambda-s3-trigger_test.go new file mode 100644 index 0000000000..b557f838e6 --- /dev/null +++ b/go/rekognition-lambda-s3-trigger/rekognition-lambda-s3-trigger_test.go @@ -0,0 +1,35 @@ +package main + +import ( + "testing" + + "github.com/aws/aws-cdk-go/awscdk/v2" + "github.com/aws/aws-cdk-go/awscdk/v2/assertions" + "github.com/aws/jsii-runtime-go" +) + +func TestRekognitionLambdaS3TriggerStack(t *testing.T) { + // GIVEN + app := awscdk.NewApp(nil) + + // WHEN + stack := NewRekognitionLambdaS3TriggerStack(app, "MyTestStack", nil) + + // THEN + template := assertions.Template_FromStack(stack, nil) + + // Test S3 Bucket + template.HasResourceProperties(jsii.String("AWS::S3::Bucket"), map[string]interface{}{}) + template.ResourceCountIs(jsii.String("AWS::S3::Bucket"), jsii.Number(1)) + + // Test DynamoDB Table + template.HasResourceProperties(jsii.String("AWS::DynamoDB::Table"), map[string]interface{}{ + "AttributeDefinitions": []map[string]interface{}{ + { + "AttributeName": "image_name", + "AttributeType": "S", + }, + }, + }) + +}