diff --git a/src/provisdom/boot_lambda.clj b/src/provisdom/boot_lambda.clj index e1fec92..9754dc0 100644 --- a/src/provisdom/boot_lambda.clj +++ b/src/provisdom/boot_lambda.clj @@ -3,6 +3,7 @@ (:require [clojure.string :as str] [clojure.java.io :as io] + [clojure.java.shell :as java-shell] [boot.core :as core] [boot.util :as util])) @@ -34,6 +35,30 @@ [input-files opts command & args] (-> opts (task-opts->cmd-opts input-files) (cmd-opts->string (format "aws lambda %s" command)) (str " " (str/join " " args)))) +(defn- last-lambda-update-status + [function-name] + (let [cmd-string (format "aws lambda get-function-configuration --function-name %s --query \"LastUpdateStatus\" --output text" function-name)] + (util/info (str cmd-string "\n")) + (-> (apply java-shell/sh (str/split cmd-string #" ")) + (:out) + (str/trim)))) + +(defn- await-configuration-update-success + [function-name] + (let [retry-after-seconds 10 + timeout-seconds 90] ; AWS docs say it should complete within a minute; give it a little longer (https://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-configuration.html) + (loop [elapsed-seconds 0] + (when (>= elapsed-seconds timeout-seconds) + (throw (Exception. "Function configuration update did not complete in time\n"))) + (let [status (last-lambda-update-status function-name)] + (case status + "InProgress" (do (util/info "Waiting until lambda function configuration has completed successfully...\n") + (Thread/sleep (* retry-after-seconds 1000)) + (recur (+ elapsed-seconds retry-after-seconds))) + "Failed" (throw (Exception. "Function configuration update failed\n")) + "Successful" (util/info "Function configuration update was successful\n") + (throw (Exception. (str "Unexpected function configuration update status: " status)))))))) + (core/deftask create-function [f function-name VAL str "The name you want to assign to the function you are uploading" r runtime VAL str "The runtime environment for the Lambda function you are uploading" @@ -59,6 +84,34 @@ (shell cmd-string)) fileset)) +(core/deftask update-function-configuration + [f function-name VAL str "The name you want to assign to the function you are uploading" + r runtime VAL str "The runtime environment for the Lambda function you are uploading" + i role VAL str "The ARN of the IAM role that Lambda assumes when it executes your function" + e handler VAL sym "The function within your code that Lambda calls to begin execution" + d description VAL str "A short, user-defined function description" + t timeout VAL int "The function execution time at which Lambda should terminate the function" + m memory-size VAL int "The amount of memory, in MB, your Lambda function is given" + v vpc-config VAL str "Identifies the list of security group IDs and subnet IDs" + j cli-input-json VAL str "Performs service operation based on the JSON string provided" + g generate-cli-skeleton bool "Prints a sample input JSON to standard output" + _ environment VAL str "The parent object that contains your environment's configuration settings" + _ dead-letter-config VAL str "The parent object that contains the target ARN (Amazon Resource Name) of an Amazon SQS queue or Amazon SNS topic" + _ kms-key-arn VAL str "The Amazon Resource Name (ARN) of the KMS key used to encrypt your function's environment variables" + _ tracing-config VAL str "The parent object that contains your function's tracing settings" + _ revision-id VAL str "Used to ensure you are updating the latest update of the function version or alias" + _ layers VAL str "A string containing zero or more function layers specified by their ARNs and separated by a space"] + (when-not function-name + (throw (Exception. "Required function-name to update function configuration"))) + (core/with-pre-wrap fileset + (let [input-files (core/input-files fileset) + cmd-string (lambda-cmd-string input-files *opts* "update-function-configuration")] + (util/info "Updating lambda function configuration...\n") + (util/info (str cmd-string "\n")) + (shell cmd-string) + (await-configuration-update-success function-name)) + fileset)) + (core/deftask update-function [f function-name VAL str "The name you want to assign to the function you are uploading" l local-file VAL str "The path to the local file of the code you are uploading" @@ -112,7 +165,8 @@ (try ;; throws if function does not exist (shell (format "aws lambda get-function --function-name %s" function-name)) - (apply update-function (mapcat identity (select-task-keys #'update-function opts))) + (comp (apply update-function-configuration (mapcat identity (select-task-keys #'update-function-configuration opts))) + (apply update-function (mapcat identity (select-task-keys #'update-function opts)))) (catch Exception ex (util/info (format "Function %s not found. Creating function..." function-name)) (apply create-function (mapcat identity (select-task-keys #'create-function opts))))))) @@ -135,4 +189,4 @@ out-dir (core/tmp-dir!) out-file (io/file out-dir out-file-name)] (spit out-file file-content) - (core/commit! (core/add-resource fileset out-dir))))) \ No newline at end of file + (core/commit! (core/add-resource fileset out-dir)))))