:*`
213 |
214 | ### Developers
215 | If you are interested in contributing to this project, please note that current development can be found in the SNAPSHOT branch of the coming release. When making pull requests, please create them against this branch.
216 |
217 | A test harness has been provided which can be run with `mvn test` Please use
218 | this and feel free to add additional tests. Note that the basic-pom.xml file
219 | requires you to add your role arn in order to work. As such, basic-pom.xml
220 | has been added to .gitignore so that you don't accidentally commit your role
221 | to the file. If you add more pom's as part of enhancing the test suite,
222 | please remember to add them to .gitignore.
223 |
224 | ### Releases
225 | 2.3.5
226 | * Ability to set skill Id for Alexa Skills Kit trigger. Thanks [mphartman1@gmail.com](mailto:mphartman1@gmail.com)
227 | * Auto force update if version contains SNAPSHOT. Thanks [dimeo@elderresearch.com](mailto:dimeo@elderresearch.com)
228 |
229 | 2.3.4
230 | * Resolves [Issue 117](https://github.com/SeanRoy/lambda-maven-plugin/issues/117) https://github.com/juger89
231 | * Thanks [juger89@gmail.com](mailto:juger89@gmail.com)
232 |
233 | 2.3.3
234 | * Added Support for SQS Trigger
235 |
236 | 2.3.2
237 | * Resolves [Issue 89](https://github.com/SeanRoy/lambda-maven-plugin/issues/89), allowing for encryption of environment variables defined on the command line. See kmsEncryptionKey and encryptedPassThrough above.
238 |
239 | 2.3.1
240 | * Resolves [Issue 87](https://github.com/SeanRoy/lambda-maven-plugin/issues/87), which was introduced in 2.3.0.
241 |
242 | 2.3.0
243 | * Resolves [Issue 84](https://github.com/SeanRoy/lambda-maven-plugin/issues/84), Environment variables respect a hierarchy of definition and plugin will no longer wipe out existing variables
244 |
245 | 2.2.9
246 | * Added ability to set http proxy on AWS clients. [Issue 39](https://github.com/SeanRoy/lambda-maven-plugin/issues/39)
247 |
248 | 2.2.8
249 | * Added the ability to set an alias for the new function version, provided publish=true. Fixes [Issue 74](https://github.com/SeanRoy/lambda-maven-plugin/issues/74)
250 |
251 | 2.2.7
252 | * Added SNS & Kinesis trigger orphan handling. This resolves [Issue 50](https://github.com/SeanRoy/lambda-maven-plugin/issues/50)
253 |
254 | 2.2.6
255 | * Fixed another potential NPE, added orphan trigger cleanup for DynamoDB integrations.
256 |
257 | 2.2.5
258 | * Fixed [Issue 77](https://github.com/SeanRoy/lambda-maven-plugin/issues/77)
259 |
260 | 2.2.4
261 | * Smarter orphaned permission handling.
262 |
263 | 2.2.3
264 | * Fixed [Issue 71](https://github.com/SeanRoy/lambda-maven-plugin/issues/71)
265 | * Fixed [Issue 72](https://github.com/SeanRoy/lambda-maven-plugin/issues/72) By adding Lex integration
266 |
267 | 2.2.2
268 | * Fixed [Issue 66](https://github.com/SeanRoy/lambda-maven-plugin/issues/66)
269 | * Fixed sources of potential NPEs, thank [Jean Blanchard](https://github.com/jeanblanchard)
270 |
271 | 2.2.1
272 | * Added passThrough environment variables feature, allowing you to pass environment variables from the command line.
273 | * Added cleanup code to remove cloudwatch event rules that have become orphaned on when the function is being deleted. More triggers will be added to the cleanup list in a later revision.
274 |
275 | 2.2.0
276 | * Added Keep Alive functionality
277 | * Fixed broken update schedule code.
278 | * Added Kinesis trigger, thanks [Matt Van](https://github.com/mattvv)
279 | * Deletion of triggers on lambda delete and the update code needs serious re-working.
280 | * Need to work on cleaning up after orphaned resources.
281 |
282 | 2.1.7
283 | * Fixed critical credentials bug introduced in 2.1.6.
284 |
285 | 2.1.6
286 | * Removed some deprecated code
287 | * functionNameSuffix is no longer automatically hyphenated. If you want a hyphen, specify it in the functionNameSuffix directive.
288 |
289 | 2.1.5
290 | * Add support for environment variables [Issue 48](https://github.com/SeanRoy/lambda-maven-plugin/issues/48)
291 | * Thanks [krzysztof@flowlab.no](mailto:krzysztof@flowlab.no)
292 |
293 | 2.1.4
294 | * Fixed [Issue 46] (https://github.com/SeanRoy/lambda-maven-plugin/issues/46)
295 | * Thanks [krzysztof@flowlab.no](mailto:krzysztof@flowlab.no)
296 |
297 | 2.1.3
298 | * Fixed [Issue 42] (https://github.com/SeanRoy/lambda-maven-plugin/issues/42)
299 | * Thanks [krzysztof@flowlab.no](mailto:krzysztof@flowlab.no)
300 |
301 | 2.1.2
302 | * Added trigger to allow Alexa Skills Kit Integration.
303 |
304 | 2.1.1
305 | * Remove deprecated `scheduledRules` and `topics` functionality
306 | * Thanks [krzysztof@flowlab.no](mailto:krzysztof@flowlab.no)
307 |
308 | 2.1.0
309 | * Add support for triggers. Deprecated `scheduledRules` and `topics` as thouse have been moved to triggers
310 | * Add support for DynamoDB stream. `lambdaRoleArn` requires AWSLambdaDynamoDBExecutionRole policy
311 | * Update to AWS SDK 1.11.41
312 | * Thanks [krzysztof@flowlab.no](mailto:krzysztof@flowlab.no)
313 |
314 | 2.0.1
315 | * Fixed [Issue 33] (https://github.com/SeanRoy/lambda-maven-plugin/pull/33) Thank Vũ Mạnh Tú.
316 |
317 | 2.0.0
318 | * Add support for configuration many lambda functions in one deliverable, supports config in JSON, each lumbda function configuration can be fully customized
319 | * Add support for version aliases when publish is activated
320 | * Change defaults
321 | * Fixed some mainor code smells
322 | * Remove support for annotations
323 | * Refactor code to java8
324 | * Add publish flag, which controls Lambda versioning in AWS
325 | * Force update support
326 | * Add support for SNS topics
327 | * Add support for scheduled rules, cron jobs which trigger lambda function
328 | * Thanks [krzysztof@flowlab.no](mailto:krzysztof@flowlab.no)
329 |
330 | 1.1.6
331 | * Removed debugging related code.
332 |
333 | 1.1.5
334 | * Fixed bug where default value of functionNameSuffix evaluating to null instead of a blank string.
335 |
336 | 1.1.4
337 | * Added functionNameSuffix optional property.
338 |
339 | 1.1.3
340 | * Fixed [Issue 28] (https://github.com/SeanRoy/lambda-maven-plugin/issues/28)
341 |
342 | 1.1.2
343 | * Fixed invalid dependency to lambda-maven-annotations
344 |
345 | 1.1.1
346 | * Added support for Virtual Private Clouds. Thanks Jem Rayfield.
347 | * Added ability to designate functions for deployment via LambduhFunction annotations. (Details coming soon).
348 |
349 | 1.1.0
350 | * Added delete goal.
351 |
352 | 1.0.6
353 | * Issue 19 Added test harness.
354 | * Update function code if code or configuration has changed instead of
355 | deleting and recreating every time. Thanks Guillermo Menendez
356 |
357 | 1.0.5
358 | * Accidental deployment of release. Should be functionally equivalent to
359 | 1.0.4.
360 |
361 | 1.0.4
362 | * Fixed issue 8
363 | * No longer uploads function code to S3 when no changes have been made to speed up
364 | development cycle over slow connections. Thanks Philip M. White.
365 | * Fixed logging.
366 |
367 | 1.0.3
368 | * Fixed a bug where getting a bucket fails if existing. Thanks buri17
369 | * Fixed problem with region specification. Thanks buri17
370 | * Adding ability to pull creds from the default provider. Thanks Chris Weiss
371 |
372 | 1.0.2
373 | * Fixed PatternSyntaxException on windows https://github.com/SeanRoy/lambda-maven-plugin/issues/1
374 |
--------------------------------------------------------------------------------
/src/main/java/com/github/seanroy/plugins/AbstractLambdaMojo.java:
--------------------------------------------------------------------------------
1 | package com.github.seanroy.plugins;
2 |
3 | import static com.amazonaws.util.CollectionUtils.isNullOrEmpty;
4 | import static java.util.Collections.emptyList;
5 | import static java.util.Optional.of;
6 | import static java.util.Optional.ofNullable;
7 | import static java.util.stream.Collectors.toList;
8 |
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.lang.reflect.Type;
13 | import java.util.*;
14 | import java.util.function.BiFunction;
15 | import java.util.function.Function;
16 | import java.util.regex.Pattern;
17 | import java.util.stream.Collectors;
18 | import java.util.stream.Stream;
19 |
20 | import com.amazonaws.services.lambda.model.GetFunctionRequest;
21 | import com.amazonaws.services.lambda.model.GetFunctionResult;
22 | import com.amazonaws.services.lambda.model.UpdateFunctionCodeRequest;
23 | import com.amazonaws.services.lambda.model.UpdateFunctionCodeResult;
24 | import com.amazonaws.services.s3.model.*;
25 | import org.apache.commons.codec.digest.DigestUtils;
26 | import org.apache.maven.plugin.AbstractMojo;
27 | import org.apache.maven.plugin.MojoExecutionException;
28 | import org.apache.maven.plugins.annotations.Parameter;
29 |
30 | import com.amazonaws.AmazonWebServiceClient;
31 | import com.amazonaws.ClientConfiguration;
32 | import com.amazonaws.Protocol;
33 | import com.amazonaws.auth.AWSCredentials;
34 | import com.amazonaws.auth.AWSStaticCredentialsProvider;
35 | import com.amazonaws.auth.BasicAWSCredentials;
36 | import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
37 | import com.amazonaws.client.builder.AwsClientBuilder;
38 | import com.amazonaws.regions.Regions;
39 | import com.amazonaws.services.cloudwatchevents.AmazonCloudWatchEvents;
40 | import com.amazonaws.services.cloudwatchevents.AmazonCloudWatchEventsClientBuilder;
41 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreams;
42 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsClientBuilder;
43 | import com.amazonaws.services.kinesis.AmazonKinesis;
44 | import com.amazonaws.services.kinesis.AmazonKinesisClientBuilder;
45 | import com.amazonaws.services.lambda.AWSLambda;
46 | import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
47 | import com.amazonaws.services.lambda.model.GetFunctionConfigurationRequest;
48 | import com.amazonaws.services.lambda.model.ResourceNotFoundException;
49 | import com.amazonaws.services.s3.AmazonS3;
50 | import com.amazonaws.services.s3.AmazonS3ClientBuilder;
51 | import com.amazonaws.services.sns.AmazonSNS;
52 | import com.amazonaws.services.sns.AmazonSNSClientBuilder;
53 | import com.amazonaws.services.sqs.AmazonSQS;
54 | import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
55 | import com.github.seanroy.utils.AWSEncryption;
56 | import com.github.seanroy.utils.JsonUtil;
57 | import com.google.gson.GsonBuilder;
58 | import com.google.gson.reflect.TypeToken;
59 |
60 | /**
61 | * Abstracts all common parameter handling and initiation of AWS service clients.
62 | *
63 | * @author sean, Krzysztof Grodzicki 11/08/16.
64 | */
65 | @SuppressWarnings("ClassWithTooManyFields")
66 | public abstract class AbstractLambdaMojo extends AbstractMojo {
67 | public static final String TRIG_INT_LABEL_CLOUDWATCH_EVENTS = "CloudWatch Events - Schedule";
68 | public static final String TRIG_INT_LABEL_DYNAMO_DB = "DynamoDB";
69 | public static final String TRIG_INT_LABEL_KINESIS = "Kinesis";
70 | public static final String TRIG_INT_LABEL_SNS = "SNS";
71 | public static final String TRIG_INT_LABEL_ALEXA_SK = "Alexa Skills Kit";
72 | public static final String TRIG_INT_LABEL_LEX = "Lex";
73 | public static final String TRIG_INT_LABEL_SQS = "SQS";
74 |
75 | public static final String PERM_LAMBDA_INVOKE = "lambda:InvokeFunction";
76 |
77 | public static final String PRINCIPAL_ALEXA = "alexa-appkit.amazon.com";
78 | public static final String PRINCIPAL_LEX = "lex.amazonaws.com";
79 | public static final String PRINCIPAL_SNS = "sns.amazonaws.com";
80 | public static final String PRINCIPAL_EVENTS = "events.amazonaws.com"; // Cloudwatch events
81 | public static final String PRINCIPAL_SQS = "sqs.amazonaws.com";
82 |
83 | @Parameter(property = "skip", defaultValue = "false")
84 | public boolean skip;
85 |
86 | /**
87 | * The AWS access key.
88 | */
89 | @Parameter(property = "accessKey", defaultValue = "${accessKey}")
90 | public String accessKey;
91 | /**
92 | * The AWS secret access key.
93 | */
94 | @Parameter(property = "secretKey", defaultValue = "${secretKey}")
95 | public String secretKey;
96 | /**
97 | * The path to deliverable.
98 | */
99 | @Parameter(property = "functionCode", defaultValue = "${functionCode}", required = true)
100 | public String functionCode;
101 | /**
102 | * The version of deliverable. Example value can be 1.0-SNAPSHOT.
103 | */
104 | @Parameter(property = "version", defaultValue = "${version}", required = true)
105 | public String version;
106 |
107 | @Parameter(property = "alias")
108 | public String alias;
109 |
110 | /**
111 | * Amazon region. Default value is us-east-1.
112 | */
113 | @Parameter(property = "region", alias = "region", defaultValue = "us-east-1")
114 | public String regionName;
115 | /**
116 | *
117 | * Amazon S3 bucket name where the .zip file containing your deployment
118 | * package is stored. This bucket must reside in the same AWS region where
119 | * you are creating the Lambda function.
120 | *
121 | */
122 | @Parameter(property = "s3Bucket", defaultValue = "lambda-function-code")
123 | public String s3Bucket;
124 | /**
125 | *
126 | * AWS S3 Server Side Encryption (SSE)
127 | *
128 | */
129 | @Parameter(property = "sse", defaultValue = "false")
130 | public boolean sse;
131 | /**
132 | *
133 | * AWS KMS Key ID for S3 Server Side Encryption (SSE)
134 | *
135 | */
136 | @Parameter(property = "sseKmsEncryptionKeyArn")
137 | public String sseKmsEncryptionKeyArn;
138 | /**
139 | *
140 | * S3 key prefix for the uploaded jar
141 | *
142 | */
143 | @Parameter(property = "keyPrefix", defaultValue = "/")
144 | public String keyPrefix;
145 | /**
146 | *
147 | * The runtime environment for the Lambda function.
148 | *
149 | *
150 | * To use the Node.js runtime v4.3, set the value to "nodejs4.3". To use
151 | * earlier runtime (v0.10.42), set the value to "nodejs".
152 | *
153 | */
154 | @Parameter(property = "runtime", defaultValue = "java8")
155 | public String runtime;
156 | /**
157 | * The Amazon Resource Name (ARN) of the IAM role that Lambda will assume when it executes your function.
158 | */
159 | @Parameter(property = "lambdaRoleArn", defaultValue = "${lambdaRoleArn}")
160 | public String lambdaRoleArn;
161 | /**
162 | * The JSON confuguration for Lambda functions. @see {@link LambdaFunction}.
163 | */
164 | @Parameter(property = "lambdaFunctionsJSON")
165 | public String lambdaFunctionsJSON;
166 | /**
167 | * The confuguration for Lambda functions. @see {@link LambdaFunction}. Can be configured in pom.xml. Automaticall parsed from JSON configuration.
168 | */
169 | @Parameter(property = "lambdaFunctions", defaultValue = "${lambdaFunctions}")
170 | public List lambdaFunctions;
171 | /**
172 | *
173 | * The function execution time at which AWS Lambda should terminate the
174 | * function. Because the execution time has cost implications, we recommend
175 | * you set this value based on your expected execution time. The default is 30 seconds.
176 | *
177 | */
178 | @Parameter(property = "timeout", defaultValue = "30")
179 | public int timeout;
180 | /**
181 | *
182 | * The amount of memory, in MB, your Lambda function is given. AWS Lambda
183 | * uses this memory size to infer the amount of CPU allocated to your
184 | * function. Your function use-case determines your CPU and memory
185 | * requirements. For example, a database operation might need less memory
186 | * compared to an image processing function. The default value is 1024 MB.
187 | * The value must be a multiple of 64 MB.
188 | *
189 | */
190 | @Parameter(property = "memorySize", defaultValue = "1024")
191 | public int memorySize;
192 | /**
193 | * A list of one or more security groups IDs in your VPC.
194 | */
195 | @Parameter(property = "vpcSecurityGroupIds", defaultValue = "${vpcSecurityGroupIds}")
196 | public List vpcSecurityGroupIds;
197 | /**
198 | * A list of one or more subnet IDs in your VPC.
199 | */
200 | @Parameter(property = "vpcSubnetIds", defaultValue = "${vpcSubnetIds}")
201 | public List vpcSubnetIds;
202 | /**
203 | * This boolean parameter can be used to request AWS Lambda to update the
204 | * Lambda function and publish a version as an atomic operation.
205 | */
206 | @Parameter(property = "publish", defaultValue = "true")
207 | public boolean publish;
208 | /**
209 | * The suffix for the lambda function.
210 | */
211 | @Parameter(property = "functionNameSuffix")
212 | public String functionNameSuffix;
213 | /**
214 | * This boolean parameter can be used to force update of existing configuration. Use it when you don't publish a function and want to replece code in your Lambda function.
215 | * The default value is {@code true} iff the version contains {@code SNAPSHOT} (case insensitive).
216 | */
217 | @Parameter(property = "forceUpdate")
218 | public Boolean forceUpdate;
219 | /**
220 | * This map parameter can be used to define environment variables for Lambda functions enable you to dynamically pass settings to your function code and libraries, without making changes to your code. Deployment functionality merges those variables with the one provided in json configuration.
221 | */
222 | @Parameter(property = "environmentVariables", defaultValue = "${environmentVariables}")
223 | public Map environmentVariables;
224 |
225 | @Parameter(property = "passThrough")
226 | public String passThrough;
227 |
228 | @Parameter(property = "kmsEncryptionKeyArn")
229 | public String kmsEncryptionKeyArn;
230 |
231 | @Parameter(property = "encryptedPassThrough")
232 | public String encryptedPassThrough;
233 |
234 | /**
235 | * Allows for proxy settings to passed to the lambda client.
236 | */
237 | @Parameter(property = "clientConfiguration")
238 | public Map clientConfiguration;
239 |
240 | public String fileName;
241 | public AWSCredentials credentials;
242 | public AmazonS3 s3Client;
243 | public AWSLambda lambdaClient;
244 | public AmazonSNS snsClient;
245 | public AmazonCloudWatchEvents eventsClient;
246 | public AmazonDynamoDBStreams dynamoDBStreamsClient;
247 | public AmazonKinesis kinesisClient;
248 | public AmazonCloudWatchEvents cloudWatchEventsClient;
249 | public AmazonSQS sqsClient;
250 |
251 | protected boolean checkSkip() {
252 | if(skip) {
253 | getLog().info("Execution skipped.");
254 | }
255 | return skip;
256 | }
257 |
258 | @Override
259 | public void execute() throws MojoExecutionException {
260 | initAWSCredentials();
261 | initAWSClients();
262 | try {
263 | initFileName();
264 | initVersion();
265 | initLambdaFunctionsConfiguration();
266 |
267 | lambdaFunctions.forEach(lambdaFunction -> getLog().debug(lambdaFunction.toString()));
268 | } catch (Exception e) {
269 | getLog().error("Initialization of configuration failed", e);
270 | throw new MojoExecutionException(e.getMessage());
271 | }
272 | }
273 |
274 | void uploadJarToS3() throws Exception {
275 | String bucket = getBucket();
276 | File file = new File(functionCode);
277 | String localmd5 = DigestUtils.md5Hex(new FileInputStream(file));
278 | getLog().debug(String.format("Local file's MD5 hash is %s.", localmd5));
279 |
280 | ofNullable(getObjectMetadata(bucket))
281 | .map(ObjectMetadata::getETag)
282 | .map(remoteMD5 -> {
283 | getLog().info(fileName + " exists in S3 with MD5 hash " + remoteMD5);
284 | // This comparison will no longer work if we ever go to multipart uploads. Etags are not
285 | // computed as MD5 sums for multipart uploads in s3.
286 | return localmd5.equals(remoteMD5);
287 | })
288 | .map(isTheSame -> {
289 | if (isTheSame) {
290 | getLog().info(fileName + " is up to date in AWS S3 bucket " + s3Bucket + ". Not uploading...");
291 | return true;
292 | }
293 | return null; // file should be imported
294 | })
295 | .orElseGet(() -> {
296 | upload(file);
297 | return true;
298 | });
299 | }
300 |
301 | Function updateFunctionCode = (LambdaFunction lambdaFunction) -> {
302 | getLog().info("About to update functionCode for " + lambdaFunction.getFunctionName());
303 | UpdateFunctionCodeRequest updateFunctionRequest = new UpdateFunctionCodeRequest()
304 | .withFunctionName(lambdaFunction.getFunctionName())
305 | .withS3Bucket(s3Bucket)
306 | .withS3Key(fileName)
307 | .withPublish(lambdaFunction.isPublish());
308 | UpdateFunctionCodeResult updateFunctionCodeResult = lambdaClient.updateFunctionCode(updateFunctionRequest);
309 |
310 | // wait until the UpdateFunctionCode finishes processing to avoid com.amazonaws.services.lambda.model.ResourceConflictException. See: https://docs.aws.amazon.com/lambda/latest/dg/functions-states.html
311 | GetFunctionRequest getFunctionRequest = new GetFunctionRequest()
312 | .withFunctionName(lambdaFunction.getFunctionName());
313 | GetFunctionResult getFunctionResult = lambdaClient.getFunction(getFunctionRequest);
314 |
315 | while (!getFunctionResult.getConfiguration().getState().equals("Active")
316 | || !getFunctionResult.getConfiguration().getLastUpdateStatus().equals("Successful")) {
317 | try {
318 | getLog().info(String.format("UpdateFunctionCode for %s is still processing , waiting... ", lambdaFunction.getFunctionName(), getFunctionResult.getConfiguration().getState(), getFunctionResult.getConfiguration().getLastUpdateStatus()));
319 | Thread.sleep(3000);
320 | getFunctionResult = lambdaClient.getFunction(getFunctionRequest);
321 | } catch (InterruptedException e) {
322 | e.printStackTrace();
323 | }
324 | }
325 | getLog().info("UpdateFunctionCode finished successfully for " + lambdaFunction.getFunctionName());
326 |
327 | return lambdaFunction
328 | .withVersion(updateFunctionCodeResult.getVersion())
329 | .withFunctionArn(updateFunctionCodeResult.getFunctionArn());
330 | };
331 |
332 | private ObjectMetadata getObjectMetadata(String bucket) {
333 | try {
334 | return s3Client.getObjectMetadata(bucket, fileName);
335 | } catch (AmazonS3Exception ignored) {
336 | return null;
337 | }
338 | }
339 |
340 | private String getBucket() {
341 | if (s3Client.listBuckets().stream().noneMatch(p -> Objects.equals(p.getName(), s3Bucket))) {
342 | getLog().info("Created bucket s3://" + s3Client.createBucket(s3Bucket).getName());
343 | }
344 | return s3Bucket;
345 | }
346 |
347 | private PutObjectResult upload(File file) {
348 | getLog().info("Uploading " + functionCode + " to AWS S3 bucket " + s3Bucket);
349 | PutObjectRequest putObjectRequest = new PutObjectRequest(s3Bucket, fileName, file);
350 | if (sse) {
351 | if (sseKmsEncryptionKeyArn != null && sseKmsEncryptionKeyArn.length() > 0) {
352 | putObjectRequest.setSSEAwsKeyManagementParams(new SSEAwsKeyManagementParams(sseKmsEncryptionKeyArn));
353 | } else {
354 | ObjectMetadata objectMetadata = new ObjectMetadata();
355 | objectMetadata.setSSEAlgorithm(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION);
356 | putObjectRequest.setMetadata(objectMetadata);
357 | }
358 | }
359 | PutObjectResult putObjectResult = s3Client.putObject(putObjectRequest);
360 | getLog().info("Upload complete...");
361 | return putObjectResult;
362 | }
363 |
364 | private void initAWSCredentials() throws MojoExecutionException {
365 | DefaultAWSCredentialsProviderChain defaultChain = new DefaultAWSCredentialsProviderChain();
366 | if (accessKey != null && secretKey != null) {
367 | credentials = new BasicAWSCredentials(accessKey, secretKey);
368 | } else if (defaultChain.getCredentials() != null) {
369 | credentials = defaultChain.getCredentials();
370 | }
371 |
372 | if (credentials == null) {
373 | getLog().error("Unable to initialize AWS Credentials. Set BasicAWSCredentials with accessKey and secretKey or configure DefaultAWSCredentialsProviderChain");
374 | throw new MojoExecutionException("AWS Credentials config error");
375 | }
376 | }
377 |
378 | private void initFileName() {
379 | String pattern = Pattern.quote(File.separator);
380 | String[] pieces = functionCode.split(pattern);
381 | if (!ofNullable(keyPrefix).orElse("/").endsWith("/")) {
382 | keyPrefix += "/";
383 | }
384 | fileName = keyPrefix + pieces[pieces.length - 1];
385 | }
386 |
387 | private void initVersion() {
388 | version = version.replace(".", "-");
389 | }
390 |
391 | @SuppressWarnings("rawtypes")
392 | BiFunction clientFactory = (builder, clientConfig) -> {
393 | Regions region = Regions.fromName(regionName);
394 |
395 | return (AmazonWebServiceClient) of(credentials)
396 | .map(credentials -> builder.withCredentials(new AWSStaticCredentialsProvider(credentials))
397 | .withClientConfiguration(clientConfig)
398 | .withRegion(region).build())
399 | .orElse(builder.withRegion(region).withCredentials(new DefaultAWSCredentialsProviderChain()).build());
400 | };
401 |
402 | private void initAWSClients() {
403 | ClientConfiguration clientConfig = clientConfiguration();
404 | s3Client = (AmazonS3) clientFactory.apply(AmazonS3ClientBuilder.standard(), clientConfig);
405 | lambdaClient = (AWSLambda) clientFactory.apply(AWSLambdaClientBuilder.standard(), clientConfig);
406 | snsClient = (AmazonSNS) clientFactory.apply(AmazonSNSClientBuilder.standard(), clientConfig);
407 | eventsClient = (AmazonCloudWatchEvents) clientFactory.apply(AmazonCloudWatchEventsClientBuilder.standard(), clientConfig);
408 | dynamoDBStreamsClient = (AmazonDynamoDBStreams) clientFactory.apply(AmazonDynamoDBStreamsClientBuilder.standard(), clientConfig);
409 | kinesisClient = (AmazonKinesis) clientFactory.apply(AmazonKinesisClientBuilder.standard(), clientConfig);
410 | cloudWatchEventsClient = (AmazonCloudWatchEvents) clientFactory.apply(AmazonCloudWatchEventsClientBuilder.standard(), clientConfig);
411 | sqsClient = (AmazonSQS) clientFactory.apply(AmazonSQSClientBuilder.standard(), clientConfig);
412 | }
413 |
414 | private void initLambdaFunctionsConfiguration() throws MojoExecutionException, IOException {
415 | if (lambdaFunctionsJSON != null) {
416 | this.lambdaFunctions = JsonUtil.fromJson(lambdaFunctionsJSON);
417 | }
418 | validate(lambdaFunctions);
419 |
420 | lambdaFunctions = lambdaFunctions.stream().map(lambdaFunction -> {
421 | String functionName = ofNullable(lambdaFunction.getFunctionName()).orElseThrow(() -> new IllegalArgumentException("Configuration error. LambdaFunction -> 'functionName' is required"));
422 |
423 | lambdaFunction.withFunctionName(addSuffix(functionName))
424 | .withHandler(ofNullable(lambdaFunction.getHandler()).orElseThrow(() -> new IllegalArgumentException("Configuration error. LambdaFunction -> 'handler' is required")))
425 | .withDescription(ofNullable(lambdaFunction.getDescription()).orElse(""))
426 | .withTimeout(ofNullable(lambdaFunction.getTimeout()).orElse(timeout))
427 | .withMemorySize(ofNullable(lambdaFunction.getMemorySize()).orElse(memorySize))
428 | .withSubnetIds(ofNullable(vpcSubnetIds).orElse(new ArrayList<>()))
429 | .withSecurityGroupsIds(ofNullable(vpcSecurityGroupIds).orElse(new ArrayList<>()))
430 | .withVersion(version)
431 | .withPublish(ofNullable(lambdaFunction.isPublish()).orElse(publish))
432 | .withLambdaRoleArn(ofNullable(lambdaFunction.getLambdaRoleArn()).orElse(lambdaRoleArn))
433 | .withAliases(aliases(lambdaFunction.isPublish()))
434 | .withTriggers(ofNullable(lambdaFunction.getTriggers()).map(triggers -> triggers.stream()
435 | .map(trigger -> {
436 | trigger.withRuleName(addSuffix(trigger.getRuleName()));
437 | trigger.withSNSTopic(addSuffix(trigger.getSNSTopic()));
438 | trigger.withDynamoDBTable(addSuffix(trigger.getDynamoDBTable()));
439 | trigger.withLexBotName(addSuffix(trigger.getLexBotName()));
440 | trigger.withStandardQueue(addSuffix(trigger.getStandardQueue()));
441 | return trigger;
442 | })
443 | .collect(toList()))
444 | .orElse(new ArrayList<>()))
445 | .withEnvironmentVariables(environmentVariables(lambdaFunction));
446 |
447 | return lambdaFunction;
448 | }).collect(toList());
449 | }
450 |
451 | @SuppressWarnings("unchecked")
452 | private Map environmentVariables(LambdaFunction lambdaFunction) {
453 | // Get existing environment variables to interleave them with the new ones or replacements.
454 | Map awsDefinedEnvVars = new HashMap();
455 |
456 | try {
457 | awsDefinedEnvVars = ofNullable(lambdaClient.getFunctionConfiguration(new GetFunctionConfigurationRequest()
458 | .withFunctionName(lambdaFunction.getFunctionName())
459 | .withQualifier(lambdaFunction.getQualifier())).getEnvironment()).flatMap(x -> {
460 | return of(x.getVariables());}).orElse(new HashMap<>());
461 | } catch(ResourceNotFoundException rnfe) {
462 | getLog().debug("Lambda function doesn't exist yet, no existing environment variables retrieved.");
463 | } catch(Exception e) {
464 | getLog().error("Could not retrieve existing environment variables " + e.getMessage());
465 | }
466 |
467 | Map configurationEnvVars = ofNullable(environmentVariables).orElse(new HashMap<>());
468 | Map functionEnvVars = ofNullable(lambdaFunction.getEnvironmentVariables()).orElse(new HashMap<>());
469 | Type type = new TypeToken