diff --git a/.github/issue_template.md b/.github/issue_template.md index 9626f3ce..cb8c1477 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -16,8 +16,10 @@ e.g. ## Description with failing test case Please describe what you are trying to do, what you expect to see, and -what you're seeing instead. Include a code example that we can paste -as/is into a REPL, execute, and watch fail. +what you're seeing instead. + +*Important*: Please include a complete minimal code example that we +can paste as/is into a REPL, execute, and watch fail. ## Stack traces diff --git a/CHANGES.md b/CHANGES.md index 41427859..819fab63 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,70 @@ -# aws-api 0.8 +# aws-api -### DEV +## 0.8.484 / 2020-11-05 + +* make date parsing more tolerant [#155](https://github.com/cognitect-labs/aws-api/issues/155) + +## 0.8.474 / 2020-08-15 + +* fix bug decoding GetBucketPolicy response [#148](https://github.com/cognitect-labs/aws-api/issues/148) + +## 0.8.469 / 2020-07-10 + +* fix bug parsing iso8601 dates with fractional seconds [#144](https://github.com/cognitect-labs/aws-api/issues/144) +* fix memory leak when validating requests [#143](https://github.com/cognitect-labs/aws-api/issues/143) + +## 0.8.456 / 2020-03-27 + +* upgrade to tools.logging-1.0.0, data.json-1.0.0, and core.async-1.0.567 +* let core.async manage the threadpool for fetching credentials and region + * fixes deadlock with concurrent credentials fetch [#137](https://github.com/cognitect-labs/aws-api/issues/137) + +## 0.8.445 / 2020-02-25 + +* fix deadlock using composite credentials providers (e.g. assume role example) [#130](https://github.com/cognitect-labs/aws-api/issues/130) + +## 0.8.437 / 2020-02-14 + +* fix issue with `invoke` hanging when no region or creds are found [#124](https://github.com/cognitect-labs/aws-api/issues/124) + +## 0.8.430 / 2020-02-10 + +* upgrade to com.cognitect/http-client 0.1.104 [#115](https://github.com/cognitect-labs/aws-api/issues/115) +* all aws clients use shared http-client, credentials-provider, and region-provider by default + * addresses [#109](https://github.com/cognitect-labs/aws-api/issues/109) + * first call to invoke takes hit of fetching region and credentials +* `com.cognitect.aws.api/stop` will not stop the shared http-client, but stop any other instance + +See [Upgrade Notes](https://github.com/cognitect-labs/aws-api/blob/master/UPGRADE.md) for more +information about upgrading to this version. + +## 0.8.423 / 2020-01-17 + +* Remove dep on commons-codec [#113](https://github.com/cognitect-labs/aws-api/issues/113) +* Convey anomaly from http-client as/is [#114](https://github.com/cognitect-labs/aws-api/issues/114) + +## 0.8.408 / 2019-11-25 + +* Reduce noise from reflection warnings in java 9+ [#106](https://github.com/cognitect-labs/aws-api/issues/106) +* Get signing region from endpoint config [#105](https://github.com/cognitect-labs/aws-api/issues/105) +* Add documentationUrl when available [#108](https://github.com/cognitect-labs/aws-api/issues/108) + +## 0.8.391 / 2019-10-25 + +* Fix: S3 HeadObject fails with large files [#97](https://github.com/cognitect-labs/aws-api/issues/97) + * This was fixed in cognitect/http-client 0.1.101 +* Fix concurrency 4 limit introduced in 0.8.383 + +## ~~0.8.383 / 2019-10-24~~ + +* Make http calls to fetch credentials async / non-blocking. + +## 0.8.378 / 2019-10-19 + +* Include service full names in latest-releases.edn [#32](https://github.com/cognitect-labs/aws-api/issues/32) +* Wrap dynamic require of protocol ns in locking form [#92](https://github.com/cognitect-labs/aws-api/issues/92) + +## 0.8.352 / 2019-07-26 * Use custom dynaload for http-client [#88](https://github.com/cognitect-labs/aws-api/issues/88) * Restores compatibility with Clojure-1.9 diff --git a/README.md b/README.md index c180920d..988f78ca 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,9 @@ of your choice, e.g. `com.cognitect.aws/s3`. To use, for example, the s3 api, add the following to deps.edn ``` clojure -{:deps {com.cognitect.aws/api {:mvn/version "0.8.345"} - com.cognitect.aws/endpoints {:mvn/version "1.1.11.592"} - com.cognitect.aws/s3 {:mvn/version "726.2.488.0"}}} +{:deps {com.cognitect.aws/api {:mvn/version "0.8.498"} + com.cognitect.aws/endpoints {:mvn/version "1.1.11.934"} + com.cognitect.aws/s3 {:mvn/version "810.2.817.0"}}} ``` * See [latest releases](latest-releases.edn) for a list of the latest releases of @@ -175,7 +175,7 @@ involved example using AWS STS. ## Region lookup -The aws-api client looks up the region the same with the [java +The aws-api client looks up the region the same way the [java SDK](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-region-selection.html) does, with an additional check for a System property named "aws.region" after it checks for the AWS_REGION environment variable @@ -216,27 +216,16 @@ the `:path` in the `:endpoint-override` map. ## http-client +NOTE: the behavior of `com.cognitect.aws.api/client` and `com.cognitect.aws.api/stop` +changed as of release 0.8.430. See [Upgrade +Notes](https://github.com/cognitect-labs/aws-api/blob/master/UPGRADE.md) +for more information. + The aws-api client uses an http-client to send requests to AWS, including any operations you invoke _and_ fetching the region and credentials when you're running in EC2 or ECS. By default, each -aws-api client creates its own http-client, which, in turn, manages -its own resources. Invoke `cognitect.aws.client.api/stop` on the -client if you want it to shut down any resources it and its -http-client are using. - -If you're creating multiple aws-api clients, you can, optionally, -create a single http-client and share it across aws-api clients e.g. - -``` clojure -(require '[cognitect.aws.client.api :as aws]) -(def http-client (aws/default-http-client)) -(def s3-client (aws/client {:api :s3 :http-client http-client})) -(def ssm-client (aws/client {:api :ssm :http-client http-client})) -;; etc -``` - -If you call `stop` on `s3-client` or `ssm-client` in this example, the -single http-client gets shut down for both. +aws-api client uses a single, shared http-client, whose resources +are managed by aws-api. ## Contributing @@ -246,6 +235,27 @@ aws-api is incorporated into products and client projects, we prefer to do development internally and are not accepting pull requests or patches. +## Contributors + +`aws-api` was extracted from an internal project at Cognitect, and +some contributors are missing from the commit log. Here are all the +folks from Cognitect who either committed code directly, or +contributed significantly to research and design: + +[Timothy Baldridge](https://github.com/halgari)
+[David Chelimsky](https://github.com/dchelimsky)
+[BenoƮt Fleury](https://github.com/benfle)
+[Fogus](https://github.com/fogus)
+[Stuart Halloway](https://github.com/stuarthalloway)
+[Rich Hickey](https://github.com/richhickey)
+[George Kierstein](https://github.com/MissInterpret)
+[Carin Meier](https://github.com/gigasquid)
+[Alex Miller](https://github.com/puredanger)
+[Michael Nygard](https://github.com/mtnygard)
+[Ghadi Shayban](https://github.com/ghadishayban)
+[Joseph Smith](https://github.com/solussd)
+[Marshall Thompson](https://github.com/Glassonion) + ## Troubleshooting ### General @@ -276,6 +286,17 @@ access. Remedy: check [AWS Regions and Endpoints](https://docs.aws.amazon.com/general/latest/gr/rande.html), and supply the correct endpoint as described in [nodename nor servname provided, or not known](#nodename-nor-servname-provided-or-not-known), above. +#### Ops limit reached + +The underlying http-client has a `:pending-ops-limit` configuration +which, when reached, results in an exception with the message "Ops +limit reached". As of this writing, aws-api does not provide access to +the http-client's configuration. Programs that encounter "Ops limit +reached" can avoid it by creating separate http-clients for each +aws-client. You may wish to explicitly stop +(`com.cognitect.aws.api/stop`) these aws-clients when the are not +longer in use to conserve resources. + ### S3 Issues #### "Invalid 'Location' header: null" diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 00000000..a31b4edf --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,48 @@ +# Upgrade Notes + +## 0.8.430 + +This release changed the behavior of the following functions: + +### com.cognitect.aws.api/client + +As of 0.8.430, each aws-api client uses a single shared http-client by +default. Before this release, each aws-client got its own instance of +http-client by default, which caused the number of threads consumed to +increase linearly in relation to the number of aws-clients created. +To reduce resource consumption in the case of many aws-clients, we +recommended that you create a single instance of the http-client and +explicitly share it across all aws-clients. This is no longer +necessary. + +### com.cognitect.aws.api/stop + +With the introduction of a shared http-client, this function was +updated so that it has no effect when using the shared http-client, +but will continue to call `cognitect.aws.http/stop` on any other +http-client instance. + +### effects + +These changes have the following effects: + +Programs that were creating multiple aws-clients without supplying +an http-client, and without ever calling stop, will see a reduction +in resource consumption. + +Programs that were creating an instance of +`cognitect.aws.client.api/default-http-client` and sharing it across +aws-clients should see no change. You can, however, safely stop doing +that. + +For programs that were using the default aws-client constructor and +calling stop on each aws-client, the shared http-client will not be +shut down. This should have no negative impact on resource consumption, +as there is only one http-client in this case, and its resources are +managed by aws-api. + +For programs that were creating multiple aws-clients in order to get +around an ["Ops limit reached" +error](https://github.com/cognitect-labs/aws-api/issues/98), this is a +breaking change. For this case, we recommend, for now, that you supply +a new http-client for each aws-client. diff --git a/build/doc b/build/doc index df05b9d1..d37559a3 100755 --- a/build/doc +++ b/build/doc @@ -4,7 +4,7 @@ set -e echo "Building docs...\n" rm -rf ./target/autodoc -mvn clojure:autodoc +mvn -Pautodoc clojure:autodoc echo -e "Checking out gh-pages branch\n" rm -rf gh-pages diff --git a/build/release b/build/release index 0746884f..ca5642cc 100755 --- a/build/release +++ b/build/release @@ -5,6 +5,15 @@ set -e echo "Cleaning..." rm -rf ./target +echo "Aligning pom.xml with deps.edn ..." +clj -Srepro -Spom +if [ "$(git diff pom.xml)" = "" ]; then + echo "nothing to change" +else + git add pom.xml + git commit -m "Update deps in pom.xml" +fi + echo "Calculating version..." prefix=`cat VERSION_PREFIX` suffix=`build/revision` @@ -23,7 +32,7 @@ git tag -a v${version} -m "Release ${version}" git push origin v${version} echo "Updating README.md, CHANGES.md, and latest-releases.edn with ${version}" -clj -A:update-versions +clj -A:update-versions --update-latest-releases --update-changelog --update-readme git commit -m "Update README.md, CHANGES.md, and latest-releases.edn with ${version}" \ README.md CHANGES.md latest-releases.edn git push diff --git a/build/src/cognitect/aws/release_updater.clj b/build/src/cognitect/aws/release_updater.clj deleted file mode 100644 index 377f44e8..00000000 --- a/build/src/cognitect/aws/release_updater.clj +++ /dev/null @@ -1,68 +0,0 @@ -(ns cognitect.aws.release-updater - (:require [clojure.java.io :as io] - [clojure.string :as str] - [clojure.edn :as edn] - [clojure.pprint :as pprint] - [clojure.java.shell :as shell]) - (:import (java.util Date))) - -(defn version-prefix [] - (read-string (slurp (io/file "VERSION_PREFIX")))) - -(defn git-revision [] - (-> (:out (shell/sh "build/revision")) - (str/split #"\n") - first)) - -(defn version [] - (str (version-prefix) "." (git-revision))) - -(defn update-version-in-line [version line] - (if (re-find (re-pattern (str "com.cognitect.aws\\/api")) line) - (str/replace-first line (re-pattern "\\d+.\\d+.\\d+") version) - line)) - -(defn update-file [fname ext xform] - (let [f (io/file (str fname "." ext)) - cp (java.io.File/createTempFile fname (str "." ext))] - (io/copy f cp) - (with-open [r (io/reader cp) - w (io/writer f)] - (doseq [l (line-seq r)] - (.write w (xform l)) - (.newLine w))))) - -(defn update-readme [version] - (update-file "README" - "md" - #(if (re-find (re-pattern (str "com.cognitect.aws\\/api")) %) - (str/replace-first % (re-pattern "\\d+.\\d+.\\d+") version) - %))) - -(defn update-changelog [version] - (update-file "CHANGES" - "md" - #(if (re-find (re-pattern "## DEV") %) - (str/replace-first % - (re-pattern "DEV") - (str version " / " (.format (java.text.SimpleDateFormat. "yyyy-MM-dd") (Date.)))) - %))) - -(defn update-latest-releases [version] - (let [f (io/file "latest-releases.edn") - data (edn/read-string (slurp f))] - (binding [*print-namespace-maps* false - *print-length* 500] - (spit f - (with-out-str - (clojure.pprint/pprint - (-> data - (assoc-in [:api 'com.cognitect.aws/api] version) - (update :services #(into (sorted-map) %))))))))) - -(defn -main [] - (let [v (version)] - (update-readme v) - (update-changelog v) - (update-latest-releases v)) - (System/exit 0)) diff --git a/build/src/cognitect/aws/version_updater.clj b/build/src/cognitect/aws/version_updater.clj new file mode 100644 index 00000000..6e594697 --- /dev/null +++ b/build/src/cognitect/aws/version_updater.clj @@ -0,0 +1,111 @@ +(ns cognitect.aws.version-updater + (:require [clojure.java.io :as io] + [clojure.string :as str] + [clojure.edn :as edn] + [clojure.pprint :as pprint] + [clojure.java.shell :as shell]) + (:import (java.util Date))) + +(set! *print-namespace-maps* false) + +(defn release-log-map [] + (sorted-map-by (-> (fn [a b] + (cond (= "api" (name a) (name b)) 0 + (= "api" (name a)) -1 + (= "api" (name b)) 1 + :else 0)) + (.thenComparing + (fn [a b] + (cond (= "endpoints" (name a) (name b)) 0 + (= "endpoints" (name a)) -1 + (= "endpoints" (name b)) 1 + :else 0))) + (.thenComparing compare)))) + +(defn version-prefix [] + (read-string (slurp (io/file "VERSION_PREFIX")))) + +(defn git-revision [] + (-> (:out (shell/sh "build/revision")) + (str/split #"\n") + first)) + +(defn version [] + (str (version-prefix) "." (git-revision))) + +(defn update-file [fname* xform] + (let [[fname ext] (str/split fname* #"\.") + f (io/file fname*) + cp (java.io.File/createTempFile fname (str "." ext))] + (io/copy f cp) + (with-open [r (io/reader cp) + w (io/writer f)] + (doseq [l (line-seq r)] + (.write w (xform l)) + (.newLine w))))) + +(defn latest-releases [] + (-> (io/file "latest-releases.edn") + slurp + edn/read-string)) + +(defn update-version-in [fname latest libname] + (let [version (get-in latest [(symbol "com.cognitect.aws" libname) :mvn/version])] + version + (update-file fname + #(if (re-find (re-pattern (str "com.cognitect.aws\\/" libname "\\s+\\{:mvn\\/version")) %) + (str/replace-first % (re-pattern "\\d+(.\\d+)+") version) + %)))) + +(defn update-versions-in-readme [] + (let [latest (latest-releases)] + (doseq [svc ["api" "endpoints" "s3"]] + (update-version-in "README.md" latest svc)))) + +(defn update-versions-in-deps [] + (let [latest (latest-releases)] + (doseq [svc ["endpoints" "dynamodb" "ec2" "iam" "lambda" "s3" "ssm" "sts"]] + (update-version-in "deps.edn" latest svc)))) + +(defn update-changelog [version] + (update-file "CHANGES.md" + #(if (re-find (re-pattern "## DEV") %) + (str/replace-first % + (re-pattern "DEV") + (str version " / " (.format (java.text.SimpleDateFormat. "yyyy-MM-dd") (Date.)))) + %))) + +(defn update-api-version-in-latest-releases [version] + (let [f (io/file "latest-releases.edn") + data (edn/read-string (slurp f))] + (binding [*print-namespace-maps* false + *print-length* 500] + (spit f + (with-out-str + (clojure.pprint/pprint + (into (release-log-map) + (assoc-in data ['com.cognitect.aws/api :mvn/version] version)))))))) + +(defn -main [& argv] + (let [args (set argv) + v (version)] + (when (contains? args "--update-latest-releases") + (prn "Updating api version in latest releases") + (update-api-version-in-latest-releases v)) + (when (contains? args "--update-changelog") + (println "Updating changelog") + (update-changelog v)) + (when (contains? args "--update-readme") + (println "Updating README") + (update-versions-in-readme)) + (when (contains? args "--update-deps") + (println "Updating deps.edn") + (update-versions-in-deps))) + (System/exit 0)) + +(comment + (version) + + (git-revision) + + ) diff --git a/deps.edn b/deps.edn index 96aaaaa8..b5345148 100644 --- a/deps.edn +++ b/deps.edn @@ -3,33 +3,34 @@ {:paths ["src" "resources"] :deps {org.clojure/clojure {:mvn/version "1.10.1"} - org.clojure/core.async {:mvn/version "0.4.490"} - org.clojure/tools.logging {:mvn/version "0.4.0"} + org.clojure/core.async {:mvn/version "1.3.610"} + org.clojure/tools.logging {:mvn/version "1.1.0"} + org.clojure/data.json {:mvn/version "1.0.0"} commons-codec/commons-codec {:mvn/version "1.11"} - byte-streams {:mvn/version "0.2.5-alpha2"} + byte-streams/byte-streams {:mvn/version "0.2.5-alpha2"} metosin/jsonista {:mvn/version "0.2.3"} org.clojure/data.xml {:mvn/version "0.2.0-alpha6"} - com.cognitect/http-client {:mvn/version "0.1.99"} + com.cognitect/http-client {:mvn/version "0.1.105"} ;NVD org.eclipse.jetty/jetty-client {:mvn/version "9.4.20.v20190813"}} :aliases {:update-versions {:extra-paths ["build/src"] - :main-opts ["-m" "cognitect.aws.release-updater"]} - :project-dev {:extra-paths ["dev/src" "dev/resources" "test/src" "test/resources"] - :extra-deps {commons-io/commons-io {:mvn/version "2.5"} - org.clojure/test.check {:mvn/version "0.10.0-RC1"} - org.slf4j/slf4j-log4j12 {:mvn/version "1.7.25"} - http-kit {:mvn/version "2.3.0"}}} - :test {:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" - :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"}} - :main-opts ["-m" "cognitect.test-runner"]} + :main-opts ["-m" "cognitect.aws.version-updater"]} + :dev {:extra-paths ["dev/src" "dev/resources" "test/src" "test/resources"] + :extra-deps {commons-io/commons-io {:mvn/version "2.8.0"} + org.clojure/test.check {:mvn/version "1.1.0"} + org.slf4j/slf4j-log4j12 {:mvn/version "1.7.30"} + http-kit/http-kit {:mvn/version "2.5.0"} + com.cognitect.aws/endpoints {:mvn/version "1.1.11.926"} + com.cognitect.aws/s3 {:mvn/version "810.2.817.0"}}} + :test {:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" + :sha "cb96e80f6f3d3b307c59cbeb49bb0dcb3a2a780b"}} + :main-opts ["-m" "cognitect.test-runner"]} :examples {:extra-paths ["examples" "examples/resources" "dev/resources"] - :extra-deps {org.clojure/test.check {:mvn/version "0.10.0-RC1"} - org.slf4j/slf4j-log4j12 {:mvn/version "1.7.25"} - com.cognitect.aws/endpoints {:mvn/version "1.1.11.586"} - com.cognitect.aws/dynamodb {:mvn/version "726.2.484.0"} - com.cognitect.aws/ec2 {:mvn/version "726.2.488.0"} - com.cognitect.aws/iam {:mvn/version "726.2.480.0"} - com.cognitect.aws/lambda {:mvn/version "718.2.454.0"} - com.cognitect.aws/s3 {:mvn/version "726.2.488.0"} - com.cognitect.aws/ssm {:mvn/version "726.2.481.0"} - com.cognitect.aws/sts {:mvn/version "722.2.464.0"}}}}} + :extra-deps {org.clojure/test.check {:mvn/version "1.1.0"} + org.slf4j/slf4j-log4j12 {:mvn/version "1.7.30"} + com.cognitect.aws/dynamodb {:mvn/version "810.2.801.0"} + com.cognitect.aws/ec2 {:mvn/version "810.2.817.0"} + com.cognitect.aws/iam {:mvn/version "801.2.704.0"} + com.cognitect.aws/lambda {:mvn/version "810.2.817.0"} + com.cognitect.aws/ssm {:mvn/version "810.2.817.0"} + com.cognitect.aws/sts {:mvn/version "809.2.784.0"}}}}} diff --git a/examples/assume_role_example.clj b/examples/assume_role_example.clj index d09a4392..806ffaf7 100644 --- a/examples/assume_role_example.clj +++ b/examples/assume_role_example.clj @@ -24,7 +24,8 @@ "Principal" {"AWS" [(:Arn me)]} "Action" ["sts:AssumeRole"]}]})}}) -(def new-role (:Role *1)) +(def new-role (:Role (aws/invoke iam {:op :GetRole + :request {:RoleName "aws-api-example-role"}}))) ;; make a policy to use for this example (aws/invoke iam {:op :CreatePolicy @@ -36,29 +37,32 @@ "Action" ["iam:GetUser"] "Resource" [(:Arn me)]}]})}}) -(def policy (:Policy *1)) +(def policy (->> (aws/invoke iam {:op :ListPolicies}) + :Policies + (filter #(re-find #"IAMGetMe" (:Arn %))) + first)) ;; attach the new policy to the new role (aws/invoke iam {:op :AttachRolePolicy :request {:RoleName (:RoleName new-role) :PolicyArn (:Arn policy)}}) ;; make a credentials provider that can assume a role -(defn assumed-role-credentials-provider [role-arn session-name refresh-every-n-seconds] +(defn assumed-role-credentials-provider [role-arn] (let [sts (aws/client {:api :sts})] - (credentials/auto-refreshing-credentials + (credentials/cached-credentials-with-auto-refresh (reify credentials/CredentialsProvider (fetch [_] (when-let [creds (:Credentials (aws/invoke sts {:op :AssumeRole :request {:RoleArn role-arn - :RoleSessionName session-name}}))] + :RoleSessionName (str (gensym "example-session-"))}}))] {:aws/access-key-id (:AccessKeyId creds) :aws/secret-access-key (:SecretAccessKey creds) :aws/session-token (:SessionToken creds) - ::credentials/ttl refresh-every-n-seconds})))))) + ::credentials/ttl (credentials/calculate-ttl creds)})))))) -(def provider (assumed-role-credentials-provider (:Arn new-role) "example-session" 600)) +(def provider (assumed-role-credentials-provider (:Arn new-role))) ;; make a client using the assumed role credentials provider (def iam-with-assumed-role (aws/client {:api :iam :credentials-provider provider})) @@ -67,7 +71,6 @@ (aws/invoke iam-with-assumed-role {:op :GetUser :request {:UserName (:UserName me)}}) ;; clean up -(credentials/stop provider) (aws/invoke iam {:op :DetachRolePolicy :request {:RoleName (:RoleName new-role) :PolicyArn (:Arn policy)}}) (aws/invoke iam {:op :DeletePolicy :request {:PolicyArn (:Arn policy)}}) (aws/invoke iam {:op :DeleteRole :request {:RoleName "aws-api-example-role"}}) diff --git a/examples/autoscaling.repl b/examples/autoscaling.repl new file mode 100644 index 00000000..bc73c4b3 --- /dev/null +++ b/examples/autoscaling.repl @@ -0,0 +1,5 @@ +(require '[cognitect.aws.client.api :as aws]) + +(def client (aws/client {:api :autoscaling})) +(aws/invoke client {:op :DescribeAutoScalingGroups + :request {}}) diff --git a/examples/deps.edn b/examples/deps.edn new file mode 100644 index 00000000..623dd860 --- /dev/null +++ b/examples/deps.edn @@ -0,0 +1,10 @@ +{:deps {com.cognitect.aws/api {:mvn/version "0.8.391" + :exclusions [org.slf4j/slf4j-log4j12]} + com.cognitect.aws/dynamodb {:mvn/version "746.2.533.0"} + com.cognitect.aws/ec2 {:mvn/version "759.2.556.0"} + com.cognitect.aws/endpoints {:mvn/version "1.1.11.664"} + com.cognitect.aws/logs {:mvn/version "747.2.533.0"} + com.cognitect.aws/autoscaling {:mvn/version "770.2.568.0"} + com.cognitect.aws/cloudformation {:mvn/version "746.2.533.0"} + com.cognitect.aws/s3 {:mvn/version "762.2.558.0"} + com.cognitect.aws/resourcegroupstaggingapi {:mvn/version "747.2.533.0"}}} diff --git a/examples/dynamodb_examples.clj b/examples/dynamodb_examples.clj index dd2e9f0c..876ac4cd 100644 --- a/examples/dynamodb_examples.clj +++ b/examples/dynamodb_examples.clj @@ -21,7 +21,7 @@ ;; doc! (aws/doc ddb :ListTables) (aws/doc ddb :CreateTable) -(aws/doc ddb :Scan) ;; this one has references in the request/response +(aws/doc ddb :Scan) ;; this one has a Given section with shape/AttributeValue ;; 1. Create Example Tables diff --git a/latest-releases.edn b/latest-releases.edn index a90e4d2d..9edf3e36 100644 --- a/latest-releases.edn +++ b/latest-releases.edn @@ -1,498 +1,442 @@ -{com.cognitect.aws/api {:mvn/version "0.8.345"}, - com.cognitect.aws/endpoints {:mvn/version "1.1.11.592"}, +{com.cognitect.aws/api {:mvn/version "0.8.498"}, + com.cognitect.aws/endpoints {:mvn/version "1.1.11.934"}, + com.cognitect.aws/AWS242AppRegistry + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Service Catalog App Registry"}, com.cognitect.aws/AWSMigrationHub - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Migration Hub"}, + {:mvn/version "796.2.657.0", :aws/serviceFullName "AWS Migration Hub"}, + com.cognitect.aws/accessanalyzer + {:mvn/version "809.2.784.0", :aws/serviceFullName "Access Analyzer"}, com.cognitect.aws/acm - {:mvn/version "715.2.434.0", - :aws/serviceFullName "AWS Certificate Manager"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Certificate Manager"}, com.cognitect.aws/acm-pca - {:mvn/version "726.2.479.0", - :aws/serviceFullName - "AWS Certificate Manager Private Certificate Authority"}, + {:mvn/version "811.2.824.0", + :aws/serviceFullName "AWS Certificate Manager Private Certificate Authority"}, com.cognitect.aws/alexaforbusiness - {:mvn/version "726.2.485.0", - :aws/serviceFullName "Alexa For Business"}, - com.cognitect.aws/amplify - {:mvn/version "729.2.490.0", :aws/serviceFullName "AWS Amplify"}, + {:mvn/version "802.2.713.0", :aws/serviceFullName "Alexa For Business"}, + com.cognitect.aws/amp + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Prometheus Service"}, + com.cognitect.aws/amplify {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS Amplify"}, + com.cognitect.aws/amplifybackend + {:mvn/version "810.2.805.0", :aws/serviceFullName "AmplifyBackend"}, com.cognitect.aws/apigateway - {:mvn/version "726.2.481.0", - :aws/serviceFullName "Amazon API Gateway"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon API Gateway"}, com.cognitect.aws/apigatewaymanagementapi - {:mvn/version "701.2.393.0", - :aws/serviceFullName "AmazonApiGatewayManagementApi"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AmazonApiGatewayManagementApi"}, com.cognitect.aws/apigatewayv2 - {:mvn/version "736.2.493.0", - :aws/serviceFullName "AmazonApiGatewayV2"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AmazonApiGatewayV2"}, + com.cognitect.aws/appconfig {:mvn/version "801.2.697.0", :aws/serviceFullName "Amazon AppConfig"}, + com.cognitect.aws/appflow {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Appflow"}, + com.cognitect.aws/appintegrations + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon AppIntegrations Service"}, com.cognitect.aws/application-autoscaling - {:mvn/version "711.2.412.0", - :aws/serviceFullName "Application Auto Scaling"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "Application Auto Scaling"}, com.cognitect.aws/application-insights - {:mvn/version "726.2.488.0", - :aws/serviceFullName "Amazon CloudWatch Application Insights"}, - com.cognitect.aws/appmesh - {:mvn/version "723.2.475.0", :aws/serviceFullName "AWS App Mesh"}, - com.cognitect.aws/appstream - {:mvn/version "726.2.487.0", :aws/serviceFullName "Amazon AppStream"}, - com.cognitect.aws/appsync - {:mvn/version "717.2.451.0", :aws/serviceFullName "AWS AppSync"}, - com.cognitect.aws/athena - {:mvn/version "707.2.409.0", :aws/serviceFullName "Amazon Athena"}, - com.cognitect.aws/autoscaling - {:mvn/version "736.2.494.0", :aws/serviceFullName "Auto Scaling"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon CloudWatch Application Insights"}, + com.cognitect.aws/appmesh {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS App Mesh"}, + com.cognitect.aws/appstream {:mvn/version "809.2.734.0", :aws/serviceFullName "Amazon AppStream"}, + com.cognitect.aws/appsync {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS AppSync"}, + com.cognitect.aws/athena {:mvn/version "801.2.687.0", :aws/serviceFullName "Amazon Athena"}, + com.cognitect.aws/auditmanager + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Audit Manager"}, + com.cognitect.aws/autoscaling {:mvn/version "811.2.824.0", :aws/serviceFullName "Auto Scaling"}, com.cognitect.aws/autoscaling-plans - {:mvn/version "711.2.413.0", - :aws/serviceFullName "AWS Auto Scaling Plans"}, - com.cognitect.aws/backup - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS Backup"}, - com.cognitect.aws/batch - {:mvn/version "717.2.443.0", :aws/serviceFullName "AWS Batch"}, - com.cognitect.aws/budgets - {:mvn/version "719.2.461.0", :aws/serviceFullName "AWS Budgets"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AWS Auto Scaling Plans"}, + com.cognitect.aws/backup {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS Backup"}, + com.cognitect.aws/batch {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Batch"}, + com.cognitect.aws/braket {:mvn/version "810.2.797.0", :aws/serviceFullName "Braket"}, + com.cognitect.aws/budgets {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Budgets"}, com.cognitect.aws/ce - {:mvn/version "729.2.489.0", - :aws/serviceFullName "AWS Cost Explorer Service"}, - com.cognitect.aws/chime - {:mvn/version "722.2.464.0", :aws/serviceFullName "Amazon Chime"}, - com.cognitect.aws/cloud9 - {:mvn/version "707.2.409.0", :aws/serviceFullName "AWS Cloud9"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AWS Cost Explorer Service"}, + com.cognitect.aws/chime {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Chime"}, + com.cognitect.aws/cloud9 {:mvn/version "809.2.734.0", :aws/serviceFullName "AWS Cloud9"}, com.cognitect.aws/clouddirectory - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon CloudDirectory"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon CloudDirectory"}, com.cognitect.aws/cloudformation - {:mvn/version "717.2.442.0", - :aws/serviceFullName "AWS CloudFormation"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "AWS CloudFormation"}, com.cognitect.aws/cloudfront - {:mvn/version "723.2.476.0", - :aws/serviceFullName "Amazon CloudFront"}, - com.cognitect.aws/cloudhsm - {:mvn/version "697.2.391.0", :aws/serviceFullName "Amazon CloudHSM"}, - com.cognitect.aws/cloudhsmv2 - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS CloudHSM V2"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "Amazon CloudFront"}, + com.cognitect.aws/cloudhsm {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon CloudHSM"}, + com.cognitect.aws/cloudhsmv2 {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS CloudHSM V2"}, com.cognitect.aws/cloudsearch - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon CloudSearch"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "Amazon CloudSearch"}, com.cognitect.aws/cloudsearchdomain - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon CloudSearch Domain"}, - com.cognitect.aws/cloudtrail - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS CloudTrail"}, - com.cognitect.aws/codebuild - {:mvn/version "723.2.472.0", :aws/serviceFullName "AWS CodeBuild"}, - com.cognitect.aws/codecommit - {:mvn/version "726.2.483.0", :aws/serviceFullName "AWS CodeCommit"}, - com.cognitect.aws/codedeploy - {:mvn/version "736.2.495.0", :aws/serviceFullName "AWS CodeDeploy"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon CloudSearch Domain"}, + com.cognitect.aws/cloudtrail {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS CloudTrail"}, + com.cognitect.aws/codeartifact {:mvn/version "810.2.801.0", :aws/serviceFullName "CodeArtifact"}, + com.cognitect.aws/codebuild {:mvn/version "810.2.802.0", :aws/serviceFullName "AWS CodeBuild"}, + com.cognitect.aws/codecommit {:mvn/version "801.2.704.0", :aws/serviceFullName "AWS CodeCommit"}, + com.cognitect.aws/codedeploy {:mvn/version "799.2.681.0", :aws/serviceFullName "AWS CodeDeploy"}, + com.cognitect.aws/codeguru-reviewer + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon CodeGuru Reviewer"}, + com.cognitect.aws/codeguruprofiler + {:mvn/version "803.2.717.0", :aws/serviceFullName "Amazon CodeGuru Profiler"}, com.cognitect.aws/codepipeline - {:mvn/version "718.2.456.0", :aws/serviceFullName "AWS CodePipeline"}, - com.cognitect.aws/codestar - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS CodeStar"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AWS CodePipeline"}, + com.cognitect.aws/codestar {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS CodeStar"}, + com.cognitect.aws/codestar-connections + {:mvn/version "810.2.801.0", :aws/serviceFullName "AWS CodeStar connections"}, + com.cognitect.aws/codestar-notifications + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS CodeStar Notifications"}, com.cognitect.aws/cognito-identity - {:mvn/version "712.2.425.0", - :aws/serviceFullName "Amazon Cognito Identity"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Cognito Identity"}, com.cognitect.aws/cognito-idp - {:mvn/version "717.2.449.0", - :aws/serviceFullName "Amazon Cognito Identity Provider"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Cognito Identity Provider"}, com.cognitect.aws/cognito-sync - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Cognito Sync"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Cognito Sync"}, com.cognitect.aws/comprehend - {:mvn/version "736.2.495.0", - :aws/serviceFullName "Amazon Comprehend"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Comprehend"}, com.cognitect.aws/comprehendmedical - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Comprehend Medical"}, - com.cognitect.aws/config - {:mvn/version "736.2.494.0", :aws/serviceFullName "AWS Config"}, + {:mvn/version "801.2.708.0", :aws/serviceFullName "AWS Comprehend Medical"}, + com.cognitect.aws/compute-optimizer + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Compute Optimizer"}, + com.cognitect.aws/config {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Config"}, com.cognitect.aws/connect - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Connect Service"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Connect Service"}, + com.cognitect.aws/connect-contact-lens + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Connect Contact Lens"}, + com.cognitect.aws/connectparticipant + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Connect Participant Service"}, com.cognitect.aws/cur - {:mvn/version "711.2.411.0", - :aws/serviceFullName "AWS Cost and Usage Report Service"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Cost and Usage Report Service"}, + com.cognitect.aws/customer-profiles + {:mvn/version "810.2.802.0", :aws/serviceFullName "Amazon Connect Customer Profiles"}, + com.cognitect.aws/databrew {:mvn/version "810.2.797.0", :aws/serviceFullName "AWS Glue DataBrew"}, + com.cognitect.aws/dataexchange + {:mvn/version "801.2.698.0", :aws/serviceFullName "AWS Data Exchange"}, com.cognitect.aws/datapipeline - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Data Pipeline"}, - com.cognitect.aws/datasync - {:mvn/version "719.2.460.0", :aws/serviceFullName "AWS DataSync"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Data Pipeline"}, + com.cognitect.aws/datasync {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS DataSync"}, com.cognitect.aws/dax - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon DynamoDB Accelerator (DAX)"}, - com.cognitect.aws/devicefarm - {:mvn/version "726.2.480.0", :aws/serviceFullName "AWS Device Farm"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon DynamoDB Accelerator (DAX)"}, + com.cognitect.aws/detective {:mvn/version "796.2.650.0", :aws/serviceFullName "Amazon Detective"}, + com.cognitect.aws/devicefarm {:mvn/version "784.2.595.0", :aws/serviceFullName "AWS Device Farm"}, com.cognitect.aws/devices - {:mvn/version "715.2.437.0", - :aws/serviceFullName "AWS IoT 1-Click Devices Service"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS IoT 1-Click Devices Service"}, + com.cognitect.aws/devops-guru + {:mvn/version "811.2.824.0", :aws/serviceFullName "Amazon DevOps Guru"}, com.cognitect.aws/directconnect - {:mvn/version "726.2.484.0", - :aws/serviceFullName "AWS Direct Connect"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Direct Connect"}, com.cognitect.aws/discovery - {:mvn/version "716.2.440.0", - :aws/serviceFullName "AWS Application Discovery Service"}, + {:mvn/version "788.2.608.0", :aws/serviceFullName "AWS Application Discovery Service"}, com.cognitect.aws/dlm - {:mvn/version "722.2.465.0", - :aws/serviceFullName "Amazon Data Lifecycle Manager"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Data Lifecycle Manager"}, com.cognitect.aws/dms - {:mvn/version "736.2.494.0", - :aws/serviceFullName "AWS Database Migration Service"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Database Migration Service"}, com.cognitect.aws/docdb - {:mvn/version "726.2.486.0", - :aws/serviceFullName "Amazon DocumentDB with MongoDB compatibility"}, - com.cognitect.aws/ds - {:mvn/version "707.2.406.0", - :aws/serviceFullName "AWS Directory Service"}, - com.cognitect.aws/dynamodb - {:mvn/version "726.2.484.0", :aws/serviceFullName "Amazon DynamoDB"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon DocumentDB with MongoDB compatibility"}, + com.cognitect.aws/ds {:mvn/version "810.2.805.0", :aws/serviceFullName "AWS Directory Service"}, + com.cognitect.aws/dynamodb {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon DynamoDB"}, + com.cognitect.aws/ebs + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Elastic Block Store"}, com.cognitect.aws/ec2 - {:mvn/version "726.2.488.0", - :aws/serviceFullName "Amazon Elastic Compute Cloud"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Elastic Compute Cloud"}, com.cognitect.aws/ec2-instance-connect - {:mvn/version "726.2.488.0", - :aws/serviceFullName "AWS EC2 Instance Connect"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS EC2 Instance Connect"}, com.cognitect.aws/ecr - {:mvn/version "701.2.394.0", - :aws/serviceFullName "Amazon EC2 Container Registry"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon EC2 Container Registry"}, + com.cognitect.aws/ecr-public + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Elastic Container Registry Public"}, com.cognitect.aws/ecs - {:mvn/version "736.2.495.0", - :aws/serviceFullName "Amazon EC2 Container Service"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon EC2 Container Service"}, com.cognitect.aws/eks - {:mvn/version "726.2.482.0", - :aws/serviceFullName "Amazon Elastic Kubernetes Service"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Elastic Kubernetes Service"}, + com.cognitect.aws/elastic-inference + {:mvn/version "796.2.663.0", :aws/serviceFullName "Amazon Elastic Inference"}, com.cognitect.aws/elasticache - {:mvn/version "736.2.495.0", - :aws/serviceFullName "Amazon ElastiCache"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "Amazon ElastiCache"}, com.cognitect.aws/elasticbeanstalk - {:mvn/version "712.2.419.0", - :aws/serviceFullName "AWS Elastic Beanstalk"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "AWS Elastic Beanstalk"}, com.cognitect.aws/elasticfilesystem - {:mvn/version "729.2.490.0", - :aws/serviceFullName "Amazon Elastic File System"}, + {:mvn/version "802.2.711.0", :aws/serviceFullName "Amazon Elastic File System"}, com.cognitect.aws/elasticloadbalancing - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Elastic Load Balancing"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Elastic Load Balancing"}, com.cognitect.aws/elasticloadbalancingv2 - {:mvn/version "714.2.430.0", - :aws/serviceFullName "Elastic Load Balancing"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Elastic Load Balancing"}, com.cognitect.aws/elasticmapreduce - {:mvn/version "715.2.433.0", - :aws/serviceFullName "Amazon Elastic MapReduce"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Elastic MapReduce"}, com.cognitect.aws/elastictranscoder - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Elastic Transcoder"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Elastic Transcoder"}, com.cognitect.aws/email - {:mvn/version "722.2.470.0", - :aws/serviceFullName "Amazon Simple Email Service"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Simple Email Service"}, + com.cognitect.aws/emr-containers + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon EMR Containers"}, com.cognitect.aws/entitlement-marketplace - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Marketplace Entitlement Service"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Marketplace Entitlement Service"}, com.cognitect.aws/es - {:mvn/version "736.2.493.0", - :aws/serviceFullName "Amazon Elasticsearch Service"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Elasticsearch Service"}, com.cognitect.aws/eventbridge - {:mvn/version "729.2.492.0", - :aws/serviceFullName "Amazon EventBridge"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon EventBridge"}, com.cognitect.aws/events - {:mvn/version "729.2.492.0", - :aws/serviceFullName "Amazon CloudWatch Events"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon CloudWatch Events"}, com.cognitect.aws/firehose - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Kinesis Firehose"}, + {:mvn/version "807.2.729.0", :aws/serviceFullName "Amazon Kinesis Firehose"}, com.cognitect.aws/fms - {:mvn/version "712.2.428.0", - :aws/serviceFullName "Firewall Management Service"}, - com.cognitect.aws/fsx - {:mvn/version "726.2.481.0", :aws/serviceFullName "Amazon FSx"}, - com.cognitect.aws/gamelift - {:mvn/version "729.2.490.0", :aws/serviceFullName "Amazon GameLift"}, - com.cognitect.aws/glacier - {:mvn/version "729.2.491.0", :aws/serviceFullName "Amazon Glacier"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Firewall Management Service"}, + com.cognitect.aws/forecast + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Forecast Service"}, + com.cognitect.aws/forecastquery + {:mvn/version "789.2.612.0", :aws/serviceFullName "Amazon Forecast Query Service"}, + com.cognitect.aws/frauddetector + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Fraud Detector"}, + com.cognitect.aws/fsx {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon FSx"}, + com.cognitect.aws/gamelift {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon GameLift"}, + com.cognitect.aws/glacier {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Glacier"}, com.cognitect.aws/globalaccelerator - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Global Accelerator"}, - com.cognitect.aws/glue - {:mvn/version "726.2.479.0", :aws/serviceFullName "AWS Glue"}, - com.cognitect.aws/greengrass - {:mvn/version "715.2.432.0", :aws/serviceFullName "AWS Greengrass"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Global Accelerator"}, + com.cognitect.aws/glue {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Glue"}, + com.cognitect.aws/greengrass {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Greengrass"}, + com.cognitect.aws/greengrassv2 + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT Greengrass V2"}, com.cognitect.aws/groundstation - {:mvn/version "722.2.470.0", - :aws/serviceFullName "AWS Ground Station"}, - com.cognitect.aws/guardduty - {:mvn/version "723.2.475.0", :aws/serviceFullName "Amazon GuardDuty"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Ground Station"}, + com.cognitect.aws/guardduty {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon GuardDuty"}, com.cognitect.aws/health - {:mvn/version "726.2.479.0", - :aws/serviceFullName "AWS Health APIs and Notifications"}, + {:mvn/version "807.2.729.0", :aws/serviceFullName "AWS Health APIs and Notifications"}, + com.cognitect.aws/healthlake + {:mvn/version "811.2.824.0", :aws/serviceFullName "Amazon HealthLake"}, + com.cognitect.aws/honeycode {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Honeycode"}, com.cognitect.aws/iam - {:mvn/version "736.2.493.0", - :aws/serviceFullName "AWS Identity and Access Management"}, + {:mvn/version "801.2.704.0", :aws/serviceFullName "AWS Identity and Access Management"}, + com.cognitect.aws/identitystore + {:mvn/version "810.2.797.0", :aws/serviceFullName "AWS SSO Identity Store"}, + com.cognitect.aws/imagebuilder + {:mvn/version "810.2.817.0", :aws/serviceFullName "EC2 Image Builder"}, com.cognitect.aws/importexport - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Import/Export"}, - com.cognitect.aws/inspector - {:mvn/version "718.2.443.0", :aws/serviceFullName "Amazon Inspector"}, - com.cognitect.aws/iot - {:mvn/version "712.2.426.0", :aws/serviceFullName "AWS IoT"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Import/Export"}, + com.cognitect.aws/inspector {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Inspector"}, + com.cognitect.aws/iot {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT"}, com.cognitect.aws/iot-data - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS IoT Data Plane"}, + {:mvn/version "801.2.695.0", :aws/serviceFullName "AWS IoT Data Plane"}, com.cognitect.aws/iot-jobs-data - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS IoT Jobs Data Plane"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS IoT Jobs Data Plane"}, com.cognitect.aws/iot1click-projects - {:mvn/version "712.2.427.0", - :aws/serviceFullName "AWS IoT 1-Click Projects Service"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS IoT 1-Click Projects Service"}, com.cognitect.aws/iotanalytics - {:mvn/version "722.2.466.0", - :aws/serviceFullName "AWS IoT Analytics"}, - com.cognitect.aws/iotevents - {:mvn/version "736.2.496.0", :aws/serviceFullName "AWS IoT Events"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT Analytics"}, + com.cognitect.aws/iotdeviceadvisor + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT Core Device Advisor"}, + com.cognitect.aws/iotevents {:mvn/version "796.2.667.0", :aws/serviceFullName "AWS IoT Events"}, com.cognitect.aws/iotevents-data - {:mvn/version "726.2.479.0", - :aws/serviceFullName "AWS IoT Events Data"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS IoT Events Data"}, + com.cognitect.aws/iotfleethub + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT Fleet Hub"}, + com.cognitect.aws/iotsecuretunneling + {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS IoT Secure Tunneling"}, + com.cognitect.aws/iotsitewise + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT SiteWise"}, com.cognitect.aws/iotthingsgraph - {:mvn/version "722.2.470.0", - :aws/serviceFullName "AWS IoT Things Graph"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS IoT Things Graph"}, + com.cognitect.aws/iotwireless + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS IoT Wireless"}, + com.cognitect.aws/ivs + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Interactive Video Service"}, com.cognitect.aws/kafka - {:mvn/version "722.2.466.0", - :aws/serviceFullName "Managed Streaming for Kafka"}, - com.cognitect.aws/kinesis - {:mvn/version "697.2.391.0", :aws/serviceFullName "Amazon Kinesis"}, + {:mvn/version "810.2.805.0", :aws/serviceFullName "Managed Streaming for Kafka"}, + com.cognitect.aws/kendra + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWSKendraFrontendService"}, + com.cognitect.aws/kinesis {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Kinesis"}, com.cognitect.aws/kinesis-video-archived-media - {:mvn/version "729.2.490.0", - :aws/serviceFullName "Amazon Kinesis Video Streams Archived Media"}, + {:mvn/version "796.2.665.0", :aws/serviceFullName "Amazon Kinesis Video Streams Archived Media"}, com.cognitect.aws/kinesis-video-media - {:mvn/version "726.2.480.0", - :aws/serviceFullName "Amazon Kinesis Video Streams Media"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Kinesis Video Streams Media"}, + com.cognitect.aws/kinesis-video-signaling + {:mvn/version "781.2.585.0", :aws/serviceFullName "Amazon Kinesis Video Signaling Channels"}, com.cognitect.aws/kinesisanalytics - {:mvn/version "718.2.452.0", - :aws/serviceFullName "Amazon Kinesis Analytics"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Kinesis Analytics"}, com.cognitect.aws/kinesisanalyticsv2 - {:mvn/version "718.2.452.0", - :aws/serviceFullName "Amazon Kinesis Analytics"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Kinesis Analytics"}, com.cognitect.aws/kinesisvideo - {:mvn/version "729.2.490.0", - :aws/serviceFullName "Amazon Kinesis Video Streams"}, + {:mvn/version "796.2.665.0", :aws/serviceFullName "Amazon Kinesis Video Streams"}, com.cognitect.aws/kms - {:mvn/version "718.2.448.0", - :aws/serviceFullName "AWS Key Management Service"}, - com.cognitect.aws/lambda - {:mvn/version "718.2.454.0", :aws/serviceFullName "AWS Lambda"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Key Management Service"}, + com.cognitect.aws/lakeformation + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Lake Formation"}, + com.cognitect.aws/lambda {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Lambda"}, com.cognitect.aws/lex-models - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Lex Model Building Service"}, + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Lex Model Building Service"}, com.cognitect.aws/license-manager - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS License Manager"}, - com.cognitect.aws/lightsail - {:mvn/version "712.2.426.0", :aws/serviceFullName "Amazon Lightsail"}, + {:mvn/version "810.2.805.0", :aws/serviceFullName "AWS License Manager"}, + com.cognitect.aws/lightsail {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Lightsail"}, + com.cognitect.aws/location + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Location Service"}, com.cognitect.aws/logs - {:mvn/version "722.2.470.0", - :aws/serviceFullName "Amazon CloudWatch Logs"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon CloudWatch Logs"}, + com.cognitect.aws/lookoutvision + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Lookout for Vision"}, com.cognitect.aws/machinelearning - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Machine Learning"}, - com.cognitect.aws/macie - {:mvn/version "697.2.391.0", :aws/serviceFullName "Amazon Macie"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Machine Learning"}, + com.cognitect.aws/macie {:mvn/version "801.2.684.0", :aws/serviceFullName "Amazon Macie"}, + com.cognitect.aws/macie2 {:mvn/version "811.2.824.0", :aws/serviceFullName "Amazon Macie 2"}, com.cognitect.aws/managedblockchain - {:mvn/version "718.2.457.0", - :aws/serviceFullName "Amazon Managed Blockchain"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Managed Blockchain"}, + com.cognitect.aws/marketplace-catalog + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Marketplace Catalog Service"}, com.cognitect.aws/marketplacecommerceanalytics - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Marketplace Commerce Analytics"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Marketplace Commerce Analytics"}, com.cognitect.aws/mediaconnect - {:mvn/version "722.2.470.0", :aws/serviceFullName "AWS MediaConnect"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS MediaConnect"}, com.cognitect.aws/mediaconvert - {:mvn/version "718.2.449.0", - :aws/serviceFullName "AWS Elemental MediaConvert"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AWS Elemental MediaConvert"}, com.cognitect.aws/medialive - {:mvn/version "718.2.449.0", - :aws/serviceFullName "AWS Elemental MediaLive"}, + {:mvn/version "810.2.805.0", :aws/serviceFullName "AWS Elemental MediaLive"}, com.cognitect.aws/mediapackage - {:mvn/version "726.2.480.0", - :aws/serviceFullName "AWS Elemental MediaPackage"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Elemental MediaPackage"}, com.cognitect.aws/mediapackage-vod - {:mvn/version "722.2.470.0", - :aws/serviceFullName "AWS Elemental MediaPackage VOD"}, + {:mvn/version "801.2.690.0", :aws/serviceFullName "AWS Elemental MediaPackage VOD"}, com.cognitect.aws/mediastore - {:mvn/version "726.2.487.0", - :aws/serviceFullName "AWS Elemental MediaStore"}, + {:mvn/version "796.2.650.0", :aws/serviceFullName "AWS Elemental MediaStore"}, com.cognitect.aws/mediastore-data - {:mvn/version "719.2.463.0", - :aws/serviceFullName "AWS Elemental MediaStore Data Plane"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Elemental MediaStore Data Plane"}, com.cognitect.aws/mediatailor - {:mvn/version "718.2.442.0", :aws/serviceFullName "AWS MediaTailor"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS MediaTailor"}, com.cognitect.aws/meteringmarketplace - {:mvn/version "719.2.459.0", - :aws/serviceFullName "AWSMarketplace Metering"}, - com.cognitect.aws/mobile - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS Mobile"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "AWSMarketplace Metering"}, + com.cognitect.aws/migrationhub-config + {:mvn/version "796.2.656.0", :aws/serviceFullName "AWS Migration Hub Config"}, + com.cognitect.aws/mobile {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Mobile"}, com.cognitect.aws/mobileanalytics - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Mobile Analytics"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Mobile Analytics"}, com.cognitect.aws/monitoring - {:mvn/version "729.2.490.0", - :aws/serviceFullName "Amazon CloudWatch"}, - com.cognitect.aws/mq - {:mvn/version "716.2.438.0", :aws/serviceFullName "AmazonMQ"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon CloudWatch"}, + com.cognitect.aws/mq {:mvn/version "809.2.797.0", :aws/serviceFullName "AmazonMQ"}, com.cognitect.aws/mturk-requester - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Mechanical Turk"}, - com.cognitect.aws/neptune - {:mvn/version "726.2.477.0", :aws/serviceFullName "Amazon Neptune"}, - com.cognitect.aws/opsworks - {:mvn/version "726.2.479.0", :aws/serviceFullName "AWS OpsWorks"}, - com.cognitect.aws/opsworkscm - {:mvn/version "719.2.463.0", - :aws/serviceFullName "AWS OpsWorks for Chef Automate"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Mechanical Turk"}, + com.cognitect.aws/mwaa {:mvn/version "810.2.801.0", :aws/serviceFullName "AmazonMWAA"}, + com.cognitect.aws/neptune {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Neptune"}, + com.cognitect.aws/network-firewall + {:mvn/version "810.2.797.0", :aws/serviceFullName "AWS Network Firewall"}, + com.cognitect.aws/networkmanager + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Network Manager"}, + com.cognitect.aws/opsworks {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS OpsWorks"}, + com.cognitect.aws/opsworkscm {:mvn/version "801.2.701.0", :aws/serviceFullName "AWS OpsWorks CM"}, com.cognitect.aws/organizations - {:mvn/version "726.2.486.0", - :aws/serviceFullName "AWS Organizations"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Organizations"}, + com.cognitect.aws/outposts {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Outposts"}, com.cognitect.aws/personalize - {:mvn/version "723.2.476.0", - :aws/serviceFullName "Amazon Personalize"}, + {:mvn/version "807.2.729.0", :aws/serviceFullName "Amazon Personalize"}, com.cognitect.aws/personalize-events - {:mvn/version "723.2.476.0", - :aws/serviceFullName "Amazon Personalize Events"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Personalize Events"}, com.cognitect.aws/personalize-runtime - {:mvn/version "723.2.476.0", - :aws/serviceFullName "Amazon Personalize Runtime"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Personalize Runtime"}, com.cognitect.aws/pi - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Performance Insights"}, - com.cognitect.aws/pinpoint - {:mvn/version "726.2.484.0", :aws/serviceFullName "Amazon Pinpoint"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Performance Insights"}, + com.cognitect.aws/pinpoint {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Pinpoint"}, com.cognitect.aws/pinpoint-email - {:mvn/version "722.2.466.0", - :aws/serviceFullName "Amazon Pinpoint Email Service"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Pinpoint Email Service"}, com.cognitect.aws/pinpoint-sms-voice - {:mvn/version "701.2.394.0", - :aws/serviceFullName "Amazon Pinpoint SMS and Voice Service"}, - com.cognitect.aws/polly - {:mvn/version "716.2.439.0", :aws/serviceFullName "Amazon Polly"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Pinpoint SMS and Voice Service"}, + com.cognitect.aws/polly {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Polly"}, com.cognitect.aws/pricing - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Price List Service"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Price List Service"}, + com.cognitect.aws/qldb {:mvn/version "801.2.698.0", :aws/serviceFullName "Amazon QLDB"}, + com.cognitect.aws/qldb-session + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon QLDB Session"}, com.cognitect.aws/quicksight - {:mvn/version "729.2.491.0", - :aws/serviceFullName "Amazon QuickSight"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon QuickSight"}, com.cognitect.aws/ram - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Resource Access Manager"}, + {:mvn/version "796.2.662.0", :aws/serviceFullName "AWS Resource Access Manager"}, com.cognitect.aws/rds - {:mvn/version "726.2.488.0", - :aws/serviceFullName "Amazon Relational Database Service"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Relational Database Service"}, com.cognitect.aws/rds-data - {:mvn/version "722.2.466.0", - :aws/serviceFullName "AWS RDS DataService"}, - com.cognitect.aws/redshift - {:mvn/version "726.2.485.0", :aws/serviceFullName "Amazon Redshift"}, + {:mvn/version "795.2.645.0", :aws/serviceFullName "AWS RDS DataService"}, + com.cognitect.aws/redshift {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Redshift"}, + com.cognitect.aws/redshift-data + {:mvn/version "810.2.801.0", :aws/serviceFullName "Redshift Data API Service"}, com.cognitect.aws/rekognition - {:mvn/version "712.2.419.0", - :aws/serviceFullName "Amazon Rekognition"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Rekognition"}, com.cognitect.aws/resource-groups - {:mvn/version "716.2.441.0", - :aws/serviceFullName "AWS Resource Groups"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Resource Groups"}, com.cognitect.aws/resourcegroupstaggingapi - {:mvn/version "726.2.481.0", - :aws/serviceFullName "AWS Resource Groups Tagging API"}, - com.cognitect.aws/robomaker - {:mvn/version "736.2.493.0", :aws/serviceFullName "AWS RoboMaker"}, - com.cognitect.aws/route53 - {:mvn/version "718.2.442.0", :aws/serviceFullName "Amazon Route 53"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Resource Groups Tagging API"}, + com.cognitect.aws/robomaker {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS RoboMaker"}, + com.cognitect.aws/route53 {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Route 53"}, com.cognitect.aws/route53domains - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Route 53 Domains"}, + {:mvn/version "796.2.660.0", :aws/serviceFullName "Amazon Route 53 Domains"}, com.cognitect.aws/route53resolver - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Route 53 Resolver"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Route 53 Resolver"}, com.cognitect.aws/runtime-lex - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Lex Runtime Service"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Lex Runtime Service"}, com.cognitect.aws/runtime-sagemaker - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon SageMaker Runtime"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon SageMaker Runtime"}, com.cognitect.aws/s3 - {:mvn/version "726.2.488.0", - :aws/serviceFullName "Amazon Simple Storage Service"}, - com.cognitect.aws/s3control - {:mvn/version "718.2.446.0", :aws/serviceFullName "AWS S3 Control"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Simple Storage Service"}, + com.cognitect.aws/s3control {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS S3 Control"}, + com.cognitect.aws/s3outposts + {:mvn/version "810.2.797.0", :aws/serviceFullName "Amazon S3 on Outposts"}, com.cognitect.aws/sagemaker - {:mvn/version "723.2.473.0", - :aws/serviceFullName "Amazon SageMaker Service"}, - com.cognitect.aws/sdb - {:mvn/version "697.2.391.0", :aws/serviceFullName "Amazon SimpleDB"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon SageMaker Service"}, + com.cognitect.aws/sagemaker-a2i-runtime + {:mvn/version "796.2.657.0", :aws/serviceFullName "Amazon Augmented AI Runtime"}, + com.cognitect.aws/sagemaker-edge + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Sagemaker Edge Manager"}, + com.cognitect.aws/sagemaker-featurestore-runtime + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon SageMaker Feature Store Runtime"}, + com.cognitect.aws/savingsplans + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Savings Plans"}, + com.cognitect.aws/schemas {:mvn/version "809.2.784.0", :aws/serviceFullName "Schemas"}, + com.cognitect.aws/sdb {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon SimpleDB"}, com.cognitect.aws/secretsmanager - {:mvn/version "707.2.405.0", - :aws/serviceFullName "AWS Secrets Manager"}, + {:mvn/version "802.2.713.0", :aws/serviceFullName "AWS Secrets Manager"}, com.cognitect.aws/securityhub - {:mvn/version "726.2.481.0", :aws/serviceFullName "AWS SecurityHub"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS SecurityHub"}, com.cognitect.aws/serverlessrepo - {:mvn/version "712.2.420.0", - :aws/serviceFullName "AWSServerlessApplicationRepository"}, + {:mvn/version "794.2.637.0", :aws/serviceFullName "AWSServerlessApplicationRepository"}, com.cognitect.aws/service-quotas - {:mvn/version "726.2.488.0", :aws/serviceFullName "Service Quotas"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Service Quotas"}, com.cognitect.aws/servicecatalog - {:mvn/version "729.2.491.0", - :aws/serviceFullName "AWS Service Catalog"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AWS Service Catalog"}, com.cognitect.aws/servicediscovery - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS Cloud Map"}, - com.cognitect.aws/shield - {:mvn/version "704.2.397.0", :aws/serviceFullName "AWS Shield"}, - com.cognitect.aws/signer - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS Signer"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Cloud Map"}, + com.cognitect.aws/sesv2 + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Simple Email Service"}, + com.cognitect.aws/shield {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS Shield"}, + com.cognitect.aws/signer {:mvn/version "810.2.801.0", :aws/serviceFullName "AWS Signer"}, com.cognitect.aws/sms - {:mvn/version "697.2.391.0", - :aws/serviceFullName "AWS Server Migration Service"}, + {:mvn/version "807.2.729.0", :aws/serviceFullName "AWS Server Migration Service"}, com.cognitect.aws/snowball - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon Import/Export Snowball"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Import/Export Snowball"}, com.cognitect.aws/sns - {:mvn/version "718.2.444.0", - :aws/serviceFullName "Amazon Simple Notification Service"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Simple Notification Service"}, com.cognitect.aws/sqs - {:mvn/version "736.2.496.0", - :aws/serviceFullName "Amazon Simple Queue Service"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Simple Queue Service"}, com.cognitect.aws/ssm - {:mvn/version "726.2.481.0", - :aws/serviceFullName "Amazon Simple Systems Manager (SSM)"}, - com.cognitect.aws/states - {:mvn/version "707.2.409.0", - :aws/serviceFullName "AWS Step Functions"}, + {:mvn/version "810.2.817.0", :aws/serviceFullName "Amazon Simple Systems Manager (SSM)"}, + com.cognitect.aws/sso {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS Single Sign-On"}, + com.cognitect.aws/sso-admin + {:mvn/version "810.2.801.0", :aws/serviceFullName "AWS Single Sign-On Admin"}, + com.cognitect.aws/sso-oidc {:mvn/version "770.2.568.0", :aws/serviceFullName "AWS SSO OIDC"}, + com.cognitect.aws/states {:mvn/version "810.2.801.0", :aws/serviceFullName "AWS Step Functions"}, com.cognitect.aws/storagegateway - {:mvn/version "722.2.468.0", - :aws/serviceFullName "AWS Storage Gateway"}, + {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS Storage Gateway"}, com.cognitect.aws/streams-dynamodb - {:mvn/version "697.2.391.0", - :aws/serviceFullName "Amazon DynamoDB Streams"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon DynamoDB Streams"}, com.cognitect.aws/sts - {:mvn/version "722.2.464.0", - :aws/serviceFullName "AWS Security Token Service"}, - com.cognitect.aws/support - {:mvn/version "697.2.391.0", :aws/serviceFullName "AWS Support"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS Security Token Service"}, + com.cognitect.aws/support {:mvn/version "801.2.700.0", :aws/serviceFullName "AWS Support"}, com.cognitect.aws/swf - {:mvn/version "726.2.488.0", - :aws/serviceFullName "Amazon Simple Workflow Service"}, - com.cognitect.aws/textract - {:mvn/version "718.2.442.0", :aws/serviceFullName "Amazon Textract"}, + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon Simple Workflow Service"}, + com.cognitect.aws/synthetics {:mvn/version "809.2.797.0", :aws/serviceFullName "Synthetics"}, + com.cognitect.aws/textract {:mvn/version "809.2.797.0", :aws/serviceFullName "Amazon Textract"}, + com.cognitect.aws/timestream-query + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Timestream Query"}, + com.cognitect.aws/timestream-write + {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Timestream Write"}, com.cognitect.aws/transcribe - {:mvn/version "722.2.464.0", - :aws/serviceFullName "Amazon Transcribe Service"}, + {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon Transcribe Service"}, com.cognitect.aws/transfer - {:mvn/version "718.2.445.0", - :aws/serviceFullName "AWS Transfer for SFTP"}, - com.cognitect.aws/translate - {:mvn/version "697.2.391.0", :aws/serviceFullName "Amazon Translate"}, - com.cognitect.aws/waf - {:mvn/version "729.2.490.0", :aws/serviceFullName "AWS WAF"}, + {:mvn/version "811.2.824.0", :aws/serviceFullName "AWS Transfer Family"}, + com.cognitect.aws/translate {:mvn/version "810.2.801.0", :aws/serviceFullName "Amazon Translate"}, + com.cognitect.aws/waf {:mvn/version "796.2.666.0", :aws/serviceFullName "AWS WAF"}, com.cognitect.aws/waf-regional - {:mvn/version "729.2.490.0", :aws/serviceFullName "AWS WAF Regional"}, - com.cognitect.aws/workdocs - {:mvn/version "707.2.408.0", :aws/serviceFullName "Amazon WorkDocs"}, - com.cognitect.aws/worklink - {:mvn/version "719.2.461.0", :aws/serviceFullName "Amazon WorkLink"}, - com.cognitect.aws/workmail - {:mvn/version "718.2.449.0", :aws/serviceFullName "Amazon WorkMail"}, + {:mvn/version "796.2.666.0", :aws/serviceFullName "AWS WAF Regional"}, + com.cognitect.aws/wafv2 {:mvn/version "809.2.784.0", :aws/serviceFullName "AWS WAFV2"}, + com.cognitect.aws/wellarchitected + {:mvn/version "810.2.817.0", :aws/serviceFullName "AWS Well-Architected Tool"}, + com.cognitect.aws/workdocs {:mvn/version "793.2.629.0", :aws/serviceFullName "Amazon WorkDocs"}, + com.cognitect.aws/worklink {:mvn/version "801.2.687.0", :aws/serviceFullName "Amazon WorkLink"}, + com.cognitect.aws/workmail {:mvn/version "809.2.784.0", :aws/serviceFullName "Amazon WorkMail"}, + com.cognitect.aws/workmailmessageflow + {:mvn/version "770.2.568.0", :aws/serviceFullName "Amazon WorkMail Message Flow"}, com.cognitect.aws/workspaces - {:mvn/version "726.2.485.0", - :aws/serviceFullName "Amazon WorkSpaces"}, - com.cognitect.aws/xray - {:mvn/version "718.2.447.0", :aws/serviceFullName "AWS X-Ray"}} + {:mvn/version "810.2.805.0", :aws/serviceFullName "Amazon WorkSpaces"}, + com.cognitect.aws/xray {:mvn/version "809.2.797.0", :aws/serviceFullName "AWS X-Ray"}} diff --git a/pom.xml b/pom.xml index e484ef29..e15121c2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,190 +1,189 @@ - 4.0.0 - com.cognitect.aws - api - DEV - jar - aws-api - A Clojure API for the AWS API - http://github.com/cognitect-labs/aws-api - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - Cognitect - labs@cognitect.com - Cognitect - http://cognitect.com - - - - scm:git:git@github.com:cognitect-labs/aws-api.git - scm:git:git@github.com:cognitect-labs/aws-api.git - git@github.com:cognitect-labs/aws-api.git - - + 4.0.0 + com.cognitect.aws + api + DEV + jar + aws-api + A Clojure API for the AWS API + http://github.com/cognitect-labs/aws-api + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Cognitect + labs@cognitect.com + Cognitect + http://cognitect.com + + + + scm:git:git@github.com:cognitect-labs/aws-api.git + scm:git:git@github.com:cognitect-labs/aws-api.git + git@github.com:cognitect-labs/aws-api.git + + + + org.clojure + data.json + 1.0.0 + + + org.clojure + clojure + 1.10.1 + + + org.clojure + tools.logging + 1.1.0 + + + com.cognitect + http-client + 0.1.105 + + + org.clojure + data.xml + 0.2.0-alpha6 + + + org.clojure + core.async + 1.3.610 + + + + + autodoc + - org.clojure - clojure - 1.10.1 + autodoc + autodoc + 0.9.0 + test - - org.clojure - core.async - 0.4.490 - - - org.clojure - tools.logging - 0.4.0 - - - commons-codec - commons-codec - 1.11 - - - org.clojure - data.json - 0.2.6 - - - org.clojure - data.xml - 0.2.0-alpha6 - - - com.cognitect - http-client - 0.1.99 - - - autodoc - autodoc - 0.9.0 - test - - - - - - resources - - - src - - - - - dev-resources - - - resources - - - target - target/classes - - - com.theoryinpractise - clojure-maven-plugin - 1.7.1 - true - - - src - - - test - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.2 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - - jar - - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - gpg - - --pinentry-mode - loopback - - ${gpg.passphrase} - - - - - - - - org.springframework.build - aws-maven - 5.0.0.RELEASE - - - com.gkatzioura.maven.cloud - s3-storage-wagon - 1.0 - - - src - - - - clojars - https://repo.clojars.org/ - - - + + + + + + + resources + + + src + + + + + dev-resources + + + resources + + + target + target/classes + + + com.theoryinpractise + clojure-maven-plugin + 1.7.1 + true + + + src + + + test + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.2 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + gpg + + --pinentry-mode + loopback + + ${gpg.passphrase} + + + + + + + + org.springframework.build + aws-maven + 5.0.0.RELEASE + + + com.gkatzioura.maven.cloud + s3-storage-wagon + 1.0 + + + src + + + + clojars + https://repo.clojars.org/ + + diff --git a/src/cognitect/aws/client.clj b/src/cognitect/aws/client.clj index 4be0dd3a..49c076da 100644 --- a/src/cognitect/aws/client.clj +++ b/src/cognitect/aws/client.clj @@ -5,11 +5,12 @@ "Impl, don't call directly." (:require [clojure.core.async :as a] [byte-streams :as byte-streams] - [cognitect.aws - [credentials :as credentials] - [http :as http] - [util :as util] - [interceptors :as interceptors]])) + [cognitect.aws.http :as http] + [cognitect.aws.util :as util] + [cognitect.aws.interceptors :as interceptors] + [cognitect.aws.endpoint :as endpoint] + [cognitect.aws.region :as region] + [cognitect.aws.credentials :as credentials])) (set! *warn-on-reflection* true) @@ -36,7 +37,7 @@ (defmulti sign-http-request "Sign the HTTP request." - (fn [service region credentials http-request] + (fn [service endpoint credentials http-request] (get-in service [:metadata :signatureVersion]))) ;; TODO convey throwable back from impl @@ -66,38 +67,66 @@ (defn http-request "Creates a Ring request map that needs to be signed before being sent by `cognitect.http-client/submit`." [client op-map] - (let [{:keys [service endpoint]} (-get-info client)] - (-> (build-http-request service op-map) - (with-endpoint endpoint) - (update :body #(some-> % byte-streams/to-byte-buffer)) - ((partial interceptors/modify-http-request service op-map))))) + (let [{:keys [service endpoint-provider region-provider]} (-get-info client) + region-ch (region/fetch-async region-provider)] + (a/go + (let [region (a/ (build-http-request service op-map) + (with-endpoint endpoint) + (update :body #(some-> % byte-streams/to-byte-buffer)) + ((partial interceptors/modify-http-request service op-map)))))))) (defn sign-http-request-with-client "Signs a request with a client and a request returned by `http-request`." [client request] - (let [{:keys [service region credentials]} (-get-info client)] - (sign-http-request service region (credentials/fetch credentials) request))) + (let [{:keys [service region-provider credentials-provider]} (-get-info client) + creds-ch (credentials/fetch-async credentials-provider) + region-ch (region/fetch-async region-provider)] + (a/go + (let [region (a/ (handle-http-response service op-map %) - with-meta? (with-meta (assoc @result-meta :http-response (dissoc % :body))))))] - (swap! result-meta assoc :http-request req) - (send-http req client op-map result-chan) - result-chan) - (catch Throwable t - (let [err-ch (a/chan 1)] - (a/put! err-ch (cond-> {:cognitect.anomalies/category :cognitect.anomalies/fault - ::throwable t} - with-meta? (with-meta @result-meta))) - err-ch))))) + (let [{:keys [service http-client] :as client-info} (-get-info client) + response-meta (atom {}) + result-ch (a/promise-chan (map #(cond-> (handle-http-response service op-map %) + with-meta? (with-meta (assoc @response-meta :http-response (dissoc % :body)))))) + send-http (or (:send-http op-map) + (:send-http client-info))] + + (a/go + (let [http-request (a/! result-ch http-request) + :else + (try + (swap! response-meta assoc :http-request http-request) + (send-http http-request http-client op-map result-ch) + result-ch + (catch Throwable t + (let [err-ch (a/promise-chan)] + (a/put! err-ch (cond-> {:cognitect.anomalies/category :cognitect.anomalies/fault + ::throwable t} + with-meta? (with-meta (swap! response-meta assoc :op-map op-map)))) + err-ch)))))))) diff --git a/src/cognitect/aws/client/api.clj b/src/cognitect/aws/client/api.clj index 4fc0015b..fdfb9f64 100644 --- a/src/cognitect/aws/client/api.clj +++ b/src/cognitect/aws/client/api.clj @@ -6,8 +6,10 @@ (:require [clojure.core.async :as async] [clojure.tools.logging :as log] [clojure.string :as str] + [cognitect.aws.dynaload :as dynaload] [cognitect.aws.client :as client] [cognitect.aws.retry :as retry] + [cognitect.aws.client.shared :as shared] [cognitect.aws.credentials :as credentials] [cognitect.aws.endpoint :as endpoint] [cognitect.aws.http :as http] @@ -37,9 +39,14 @@ :api - required, this or api-descriptor required, the name of the api you want to interact with e.g. :s3, :cloudformation, etc + :http-client - optional, to share http-clients across aws-clients. + See default-http-client. + :region-provider - optional, implementation of aws-clojure.region/RegionProvider + protocol, defaults to cognitect.aws.region/default-region-provider. + Ignored if :region is also provided :region - optional, the aws region serving the API endpoints you want to interact with, defaults to region provided by - by the default region provider (see `cognitect.aws.region`) + by the region-provider :credentials-provider - optional, implementation of `cognitect.aws.credentials/CredentialsProvider` protocol, defaults to @@ -76,6 +83,9 @@ (if the request is retriable?), or nil if it should stop. Defaults to `cognitect.aws.retry/default-backoff`. + By default, all clients use shared http-client, credentials-provider, and + region-provider instances which use a small collection of daemon threads. + Alpha. Subject to change." [{:keys [api region region-provider retriable? backoff http-client send-http credentials-provider endpoint-override] :or {endpoint-override {}} @@ -85,38 +95,38 @@ (format "DEPRECATION NOTICE: :endpoint-override string is deprecated.\nUse {:endpoint-override {:hostname \"%s\"}} instead." endpoint-override))) - (let [service (service/service-description (name api)) - http-client (cond - http-client http-client - (not send-http) (http/resolve-http-client http-client)) - region (keyword - (or region - (region/fetch - (or region-provider - (region/default-region-provider (or http-client send-http))))))] - (require (symbol (str "cognitect.aws.protocols." (get-in service [:metadata :protocol])))) - (with-meta - (client/->Client - (atom {}) - {:service service - :region region - :endpoint (if-let [ep (endpoint/resolve (-> service :metadata :endpointPrefix keyword) region)] - (merge ep (if (string? endpoint-override) - {:hostname endpoint-override} - endpoint-override)) - (throw (ex-info "No known endpoint." {:service api :region region}))) - :retriable? (or retriable? retry/default-retriable?) - :backoff (or backoff retry/default-backoff) - :send-http send-http - :http-client http-client - :credentials (or credentials-provider (credentials/global-provider (or send-http http-client)))}) - {'clojure.core.protocols/datafy (fn [c] - (-> c - client/-get-info - (select-keys [:region :endpoint :service :send-http]) - (update :endpoint select-keys [:hostname :protocols :signatureVersions]) - (update :service select-keys [:metadata]) - (assoc :ops (ops c))))}))) + (let [service (service/service-description (name api)) + http-client (cond + http-client http-client + (not send-http) (shared/http-client)) + region-provider (cond region (reify region/RegionProvider (fetch [_] region)) + region-provider region-provider + :else (region/default-region-provider (or http-client send-http))) + credentials-provider (or credentials-provider (credentials/global-provider (or send-http http-client))) + endpoint-provider (endpoint/default-endpoint-provider + api + (get-in service [:metadata :endpointPrefix]) + endpoint-override)] + (dynaload/load-ns (symbol (str "cognitect.aws.protocols." (get-in service [:metadata :protocol])))) + (client/->Client + (atom {'clojure.core.protocols/datafy (fn [c] + (let [i (client/-get-info c)] + (-> i + (select-keys [:service]) + (assoc :region (-> i :region-provider region/fetch) + :endpoint (-> i :endpoint-provider endpoint/fetch)) + (update :endpoint select-keys [:hostname :protocols :signatureVersions]) + (update :service select-keys [:metadata]) + (assoc :ops (ops c)))))}) + {:service service + :retriable? (or retriable? retry/default-retriable?) + :backoff (or backoff retry/default-backoff) + :http-client http-client + :send-http send-http + :endpoint-provider endpoint-provider + :region-provider region-provider + :credentials-provider credentials-provider + :validate-requests? (atom nil)}))) (defn default-http-client "Create an http-client to share across multiple aws-api clients." @@ -148,14 +158,14 @@ Alpha. Subject to change." [client op-map] - (client/http-request client op-map)) + (async/ client client/-get-info :service) op)) -(def ^:private pprint-ref (delay (util/dynaload 'clojure.pprint/pprint))) +(def ^:private pprint-ref (delay (dynaload/load-var 'clojure.pprint/pprint))) (defn ^:skip-wiki pprint "For internal use. Don't call directly." [& args] @@ -202,13 +212,16 @@ representation. Alpha. Subject to change." - [{:keys [documentation request required response refs] :as doc}] + [{:keys [documentation documentationUrl request required response refs] :as doc}] (when doc (str/join "\n" (cond-> ["-------------------------" (:name doc) "" documentation] + documentationUrl + (into ["" + documentationUrl]) request (into ["" "-------------------------" @@ -240,15 +253,14 @@ (str "No docs for " (name operation))))) (defn stop - "Shuts down the underlying http-client, releasing resources. + "Has no effect when the underlying http-client is the shared + instance. - NOTE: if you're sharing an http-client across aws-api clients, - this will shut down the shared client for all aws-api clients - that are using it. + If you explicitly provided any other instance of http-client, stops + it, releasing resources. Alpha. Subject to change." - [client] - (some-> client - client/-get-info - :http-client - http/stop)) + [aws-client] + (let [{:keys [http-client]} (client/-get-info aws-client)] + (when-not (#'shared/shared-http-client? http-client) + (http/stop http-client)))) diff --git a/src/cognitect/aws/client/api/async.clj b/src/cognitect/aws/client/api/async.clj index caa2c0e9..3f89c25d 100644 --- a/src/cognitect/aws/client/api/async.clj +++ b/src/cognitect/aws/client/api/async.clj @@ -7,29 +7,34 @@ [cognitect.aws.client :as client] [cognitect.aws.retry :as retry] [cognitect.aws.service :as service] - [cognitect.aws.util :as util])) + [cognitect.aws.dynaload :as dynaload])) -(def ^:private validate-requests? (atom {})) +(set! *warn-on-reflection* true) + +(defn ^:skip-wiki validate-requests? + "For internal use. Don't call directly." + [client] + (some-> client client/-get-info :validate-requests? deref)) (defn ^:skip-wiki validate-requests "For internal use. Don't call directly." [client tf] - (swap! validate-requests? assoc client tf) + (reset! (-> client client/-get-info :validate-requests?) tf) (when tf (service/load-specs (-> client client/-get-info :service))) tf) -(def ^:private registry-ref (delay (util/dynaload 'clojure.spec.alpha/registry))) +(def ^:private registry-ref (delay (dynaload/load-var 'clojure.spec.alpha/registry))) (defn ^:skip-wiki registry "For internal use. Don't call directly." [& args] (apply @registry-ref args)) -(def ^:private valid?-ref (delay (util/dynaload 'clojure.spec.alpha/valid?))) +(def ^:private valid?-ref (delay (dynaload/load-var 'clojure.spec.alpha/valid?))) (defn ^:skip-wiki valid? "For internal use. Don't call directly." [& args] (apply @valid?-ref args)) -(def ^:private explain-data-ref (delay (util/dynaload 'clojure.spec.alpha/explain-data))) +(def ^:private explain-data-ref (delay (dynaload/load-var 'clojure.spec.alpha/explain-data))) (defn ^:skip-wiki explain-data "For internal use. Don't call directly." [& args] (apply @explain-data-ref args)) @@ -55,18 +60,16 @@ [client op-map] (let [result-chan (or (:ch op-map) (a/promise-chan)) {:keys [service retriable? backoff]} (client/-get-info client) - validation-error (and (get @validate-requests? client) + validation-error (and (validate-requests? client) (validate service op-map))] (when-not (contains? (:operations service) (:op op-map)) (throw (ex-info "Operation not supported" {:service (keyword (service/service-name service)) :operation (:op op-map)}))) (if validation-error - (do - (a/put! result-chan validation-error) - result-chan) - (let [send #(client/send-request client op-map) - retriable? (or (:retriable? op-map) retriable?) - backoff (or (:backoff op-map) backoff) - response-chan (retry/with-retry send (a/promise-chan) retriable? backoff)] - (a/take! response-chan (partial a/put! result-chan)) - result-chan)))) + (a/put! result-chan validation-error) + (retry/with-retry + #(client/send-request client op-map) + result-chan + (or (:retriable? op-map) retriable?) + (or (:backoff op-map) backoff))) + result-chan)) diff --git a/src/cognitect/aws/client/shared.clj b/src/cognitect/aws/client/shared.clj new file mode 100644 index 00000000..46506637 --- /dev/null +++ b/src/cognitect/aws/client/shared.clj @@ -0,0 +1,49 @@ +(ns cognitect.aws.client.shared + (:require [cognitect.aws.http :as http] + [cognitect.aws.credentials :as credentials] + [cognitect.aws.region :as region])) + +(set! *warn-on-reflection* true) + +(declare http-client) + +(def ^:private shared-http-client + (delay (http/resolve-http-client nil))) + +(def ^:private shared-credentials-provider + (delay (credentials/default-credentials-provider (http-client)))) + +(def ^:private shared-region-provider + (delay (region/default-region-provider (http-client)))) + +(defn http-client + "Returns the globally shared instance of http-client (created on the + first call). + + Alpha. Subject to change." + [] + @shared-http-client) + +(defn credentials-provider + "Returns the globally shared instance of credentials-provider, which + uses the globally shared instance of http-client. + + Alpha. Subject to change." + [] + @shared-credentials-provider) + +(defn region-provider + "Returns the globally shared instance of region-provider, which + uses the globally shared instance of http-client. + + Alpha. Subject to change." + [] + @shared-region-provider) + +(defn ^:private shared-http-client? + "For internal use. + + Alpha. Subject to change." + [candidate-http-client] + (identical? candidate-http-client + (and (realized? shared-http-client) @shared-http-client))) diff --git a/src/cognitect/aws/credentials.clj b/src/cognitect/aws/credentials.clj index 78d5924e..c72f8536 100644 --- a/src/cognitect/aws/credentials.clj +++ b/src/cognitect/aws/credentials.clj @@ -6,19 +6,22 @@ Alpha. Subject to change." (:require [clojure.java.io :as io] - [clojure.string :as str] [clojure.tools.logging :as log] + [clojure.string :as str] [cognitect.aws.util :as u] [cognitect.aws.config :as config] [cognitect.aws.ec2-metadata-utils :as ec2]) - (:import (java.util.concurrent Executors ScheduledExecutorService) - (java.util.concurrent TimeUnit ThreadFactory) + (:import (java.util Date) + (java.util.concurrent Executors ExecutorService ScheduledExecutorService + ScheduledFuture TimeUnit ThreadFactory) (java.io File) (java.net URI) (java.time Duration Instant))) +(set! *warn-on-reflection* true) + (defprotocol CredentialsProvider - (fetch [_] + (fetch [provider] "Return the credentials found by this provider, or nil. Credentials should be a map with the following keys: @@ -37,59 +40,73 @@ ;; Credentials subsystem -(defn ^:skip-wiki auto-refresh-fn - "For internal use. Don't call directly. - - Return the function to auto-refresh the `credentials` atom using the given `provider`. - - If the credentials return a ::ttl, schedule refresh after ::ttl seconds using `scheduler`. - - If the credentials returned by the provider are not valid, an error will be logged and - the automatic refresh process will stop." - [credentials provider scheduler] - (fn refresh! [] - (try - (let [{:keys [::ttl] :as new-creds} (fetch provider)] - (reset! credentials new-creds) - (when ttl - (.schedule ^ScheduledExecutorService scheduler - ^Runnable refresh! - ^long ttl - TimeUnit/SECONDS)) - new-creds) - (catch Throwable t - (log/error t "Error fetching the credentials."))))) - -(defn auto-refreshing-credentials - "Create auto-refreshing credentials using the specified provider. +(defonce ^:private scheduled-executor-service + (delay + (Executors/newScheduledThreadPool 1 (reify ThreadFactory + (newThread [_ r] + (doto (Thread. r) + (.setName "cognitect.aws-api.credentials-provider") + (.setDaemon true))))))) - Return a derefable containing the credentials. - - Call `stop` to stop the auto-refreshing process. +(defn ^:skip-wiki refresh! + "For internal use. Don't call directly. - The default ScheduledExecutorService uses a ThreadFactory - that spawns daemon threads. You can override this by - providing your own ScheduledExecutorService. + Invokes `(fetch provider)`, resets the `credentials-atom` with and + returns the result. + + If the result includes a ::ttl, schedules a refresh after ::ttl + seconds using `scheduler`, resetting `scheduled-refresh` with the + resulting `ScheduledFuture`. + + If the credentials returned by the provider are not valid, resets + both atoms to nil and returns nil." + [credentials-atom scheduled-refresh-atom provider scheduler] + (try + (let [{:keys [::ttl] :as new-creds} (fetch provider)] + (reset! scheduled-refresh-atom + (when ttl + (.schedule ^ScheduledExecutorService scheduler + ^Runnable #(refresh! credentials-atom scheduled-refresh-atom provider scheduler) + ^long ttl + TimeUnit/SECONDS))) + (reset! credentials-atom new-creds)) + (catch Throwable t + (reset! scheduled-refresh-atom nil) + (log/error t "Error fetching credentials.")))) + +(defn cached-credentials-with-auto-refresh + "Returns a CredentialsProvider which wraps `provider`, caching + credentials returned by `fetch`, and auto-refreshing the cached + credentials in a background thread when the credentials include a + ::ttl. + + Call `stop` to cancel future auto-refreshes. + + The default ScheduledExecutorService uses a ThreadFactory that + spawns daemon threads. You can override this by providing your own + ScheduledExecutorService. Alpha. Subject to change." ([provider] - (auto-refreshing-credentials - provider - (Executors/newScheduledThreadPool 1 (reify ThreadFactory - (newThread [_ r] - (doto (Thread. r) - (.setName "cognitect.aws-api.credentials.refresh") - (.setDaemon true))))))) + (cached-credentials-with-auto-refresh provider @scheduled-executor-service)) ([provider scheduler] - (let [credentials (atom nil) - auto-refresh! (auto-refresh-fn credentials provider scheduler)] + (let [credentials-atom (atom nil) + scheduled-refresh-atom (atom nil)] (reify CredentialsProvider - (fetch [_] (or @credentials (auto-refresh!))) + (fetch [_] + (or @credentials-atom + (refresh! credentials-atom scheduled-refresh-atom provider scheduler))) Stoppable (-stop [_] (-stop provider) - (.shutdownNow ^ScheduledExecutorService scheduler)))))) + (when-let [r @scheduled-refresh-atom] + (.cancel ^ScheduledFuture r true))))))) + +(defn ^:deprecated auto-refreshing-credentials + "Deprecated. Use cached-credentials-with-auto-refresh" + ([provider] (cached-credentials-with-auto-refresh provider)) + ([provider scheduler] (cached-credentials-with-auto-refresh provider scheduler))) (defn stop "Stop auto-refreshing the credentials. @@ -105,29 +122,24 @@ (valid-credentials credentials nil)) ([{:keys [aws/access-key-id aws/secret-access-key] :as credentials} credential-source] - (cond (and (not (str/blank? access-key-id)) - (not (str/blank? secret-access-key))) - credentials - - (or (str/blank? access-key-id) (str/blank? secret-access-key)) - (do - (when-not (nil? credential-source) - (log/debug (str "Unable to fetch credentials from " credential-source "."))) - nil) - - :else - nil))) + (if (and (not (str/blank? access-key-id)) + (not (str/blank? secret-access-key))) + credentials + (when credential-source + (log/info (str "Unable to fetch credentials from " credential-source ".")) + nil)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Providers (defn chain-credentials-provider - "Chain together multiple credentials provider. + "Returns a credentials-provider which chains together multiple + credentials providers. - Calls each provider in order until one return a non-nil result. This - provider is then cached for future calls to `fetch`. + `fetch` calls each provider in order until one returns a non-nil + result. This provider is then cached for future calls to `fetch`. - Returns nil if none of the providers return credentials. + `fetch` returns nil if none of the providers return credentials. Alpha. Subject to change." [providers] @@ -162,13 +174,14 @@ Alpha. Subject to change." [] - (reify CredentialsProvider - (fetch [_] - (valid-credentials - {:aws/access-key-id (u/getenv "AWS_ACCESS_KEY_ID") - :aws/secret-access-key (u/getenv "AWS_SECRET_ACCESS_KEY") - :aws/session-token (u/getenv "AWS_SESSION_TOKEN")} - "environment variables")))) + (cached-credentials-with-auto-refresh + (reify CredentialsProvider + (fetch [_] + (valid-credentials + {:aws/access-key-id (u/getenv "AWS_ACCESS_KEY_ID") + :aws/secret-access-key (u/getenv "AWS_SECRET_ACCESS_KEY") + :aws/session-token (u/getenv "AWS_SESSION_TOKEN")} + "environment variables"))))) (defn system-property-credentials-provider "Return the credentials from the system properties. @@ -184,12 +197,14 @@ Alpha. Subject to change. " [] - (reify CredentialsProvider - (fetch [_] - (valid-credentials - {:aws/access-key-id (u/getProperty "aws.accessKeyId") - :aws/secret-access-key (u/getProperty "aws.secretKey")} - "system properties")))) + (cached-credentials-with-auto-refresh + (reify CredentialsProvider + (fetch [_] + (valid-credentials + {:aws/access-key-id (u/getProperty "aws.accessKeyId") + :aws/secret-access-key (u/getProperty "aws.secretKey") + :aws/session-token (u/getProperty "aws.sessionToken")} + "system properties"))))) (defn profile-credentials-provider "Return credentials in an AWS configuration profile. @@ -215,25 +230,35 @@ (profile-credentials-provider profile-name (or (io/file (u/getenv "AWS_CREDENTIAL_PROFILES_FILE")) (io/file (u/getProperty "user.home") ".aws" "credentials")))) ([profile-name ^File f] - (reify CredentialsProvider - (fetch [_] - (when (.exists f) - (try - (let [profile (get (config/parse f) profile-name)] - (valid-credentials - {:aws/access-key-id (get profile "aws_access_key_id") - :aws/secret-access-key (get profile "aws_secret_access_key") - :aws/session-token (get profile "aws_session_token")} - "aws profiles file")) - (catch Throwable t - (log/error t "Error fetching credentials from aws profiles file")))))))) - -(defn ^:skip-wiki calculate-ttl - "For internal use. Don't call directly." - [credentials] - (if-let [expiration (some-> credentials :Expiration Instant/parse)] - (max (- (.getSeconds (Duration/between (Instant/now) ^Instant expiration)) 300) - 60) + (cached-credentials-with-auto-refresh + (reify CredentialsProvider + (fetch [_] + (when (.exists f) + (try + (let [profile (get (config/parse f) profile-name)] + (valid-credentials + {:aws/access-key-id (get profile "aws_access_key_id") + :aws/secret-access-key (get profile "aws_secret_access_key") + :aws/session-token (get profile "aws_session_token")} + "aws profiles file")) + (catch Throwable t + (log/error t "Error fetching credentials from aws profiles file"))))))))) + +(defn calculate-ttl + "Primarily for internal use, returns time to live (ttl, in seconds), + based on `:Expiration` in credentials. If `credentials` contains no + `:Expiration`, defaults to 3600. + + `:Expiration` can be a string parsable by java.time.Instant/parse + (returned by ec2/ecs instance credentials) or a java.util.Date + (returned from :AssumeRole on aws sts client)." + [{:keys [Expiration] :as credentials}] + (if Expiration + (let [expiration (if (instance? Date Expiration) + (.toInstant ^Date Expiration) + (Instant/parse Expiration))] + (max (- (.getSeconds (Duration/between (Instant/now) ^Instant expiration)) 300) + 60)) 3600)) (defn container-credentials-provider @@ -245,7 +270,7 @@ Alpha. Subject to change." [client-or-send-http] - (auto-refreshing-credentials + (cached-credentials-with-auto-refresh (reify CredentialsProvider (fetch [_] (when-let [creds (ec2/container-credentials client-or-send-http)] @@ -266,7 +291,7 @@ Alpha. Subject to change." [client-or-send-http] - (auto-refreshing-credentials + (cached-credentials-with-auto-refresh (reify CredentialsProvider (fetch [_] (when-let [creds (ec2/instance-credentials client-or-send-http)] @@ -278,7 +303,7 @@ "ec2 instance")))))) (defn default-credentials-provider - "Return a chain-credentials-provider comprising, in order: + "Returns a chain-credentials-provider with (in order): environment-credentials-provider system-property-credentials-provider @@ -311,3 +336,11 @@ (fetch [_] {:aws/access-key-id access-key-id :aws/secret-access-key secret-access-key}))) + +(defn fetch-async + "Returns a channel that will produce the result of calling fetch on + the provider. + + Alpha. Subject to change." + [provider] + (u/fetch-async fetch provider "credentials")) diff --git a/src/cognitect/aws/dynaload.clj b/src/cognitect/aws/dynaload.clj new file mode 100644 index 00000000..3d54ad47 --- /dev/null +++ b/src/cognitect/aws/dynaload.clj @@ -0,0 +1,17 @@ +(ns ^:skip-wiki cognitect.aws.dynaload) + +(set! *warn-on-reflection* true) + +(defonce ^:private dynalock (Object.)) + +(defn load-ns [ns] + (locking dynalock + (require (symbol ns)))) + +(defn load-var + [s] + (let [ns (namespace s)] + (assert ns) + (load-ns ns) + (or (resolve s) + (throw (RuntimeException. (str "Var " s " is not on the classpath")))))) diff --git a/src/cognitect/aws/ec2_metadata_utils.clj b/src/cognitect/aws/ec2_metadata_utils.clj index 59e8fe4a..caa6e6a6 100644 --- a/src/cognitect/aws/ec2_metadata_utils.clj +++ b/src/cognitect/aws/ec2_metadata_utils.clj @@ -11,6 +11,8 @@ [cognitect.aws.retry :as retry]) (:import (java.net URI))) +(set! *warn-on-reflection* true) + (def ^:const ec2-metadata-service-override-system-property "com.amazonaws.sdk.ec2MetadataServiceEndpointOverride") (def ^:const dynamic-data-root "/latest/dynamic/") (def ^:const security-credentials-path "/latest/meta-data/iam/security-credentials/") diff --git a/src/cognitect/aws/endpoint.clj b/src/cognitect/aws/endpoint.clj index b744b585..e1e79404 100644 --- a/src/cognitect/aws/endpoint.clj +++ b/src/cognitect/aws/endpoint.clj @@ -7,9 +7,13 @@ (:require [clojure.string :as str] [clojure.java.io :as io] [clojure.edn :as edn] + [clojure.core.async :as a] [cognitect.aws.service :as service] + [cognitect.aws.region :as region] [cognitect.aws.util :as util])) +(set! *warn-on-reflection* true) + (defn descriptor-resource-path [] (format "%s/endpoints.edn" service/base-resource-path)) (defn read-endpoints-description [] @@ -30,28 +34,28 @@ (defn service-resolve "Resolve the endpoint for the given service." - [partition service-name service endpoint-key] - (let [endpoint (get-in service [:endpoints endpoint-key]) - endpoint-name (name endpoint-key) - result (merge (:defaults partition) - (:defaults service) - endpoint - {:partition (:partition partition) - :endpoint-name endpoint-name - :dnsSuffix (:dnsSuffix partition)})] + [partition service-name service region-key] + (let [endpoint (get-in service [:endpoints region-key]) + region (name region-key) + result (merge (:defaults partition) + (:defaults service) + endpoint + {:partition (:partition partition) + :region region + :dnsSuffix (:dnsSuffix partition)})] (util/map-vals #(render-template % {"service" service-name - "region" endpoint-name + "region" region "dnsSuffix" (:dnsSuffix partition)}) result [:hostname :sslCommonName]))) (defn partition-resolve [{:keys [services] :as partition} service-key region-key] - (let [{:keys [partitionEndpoint isRegionalized] :as service} (get services service-key) - endpoint-key (if (and partitionEndpoint (not isRegionalized)) - (keyword partitionEndpoint) - region-key)] - (when (contains? (-> partition :regions keys set) (keyword region-key)) + (when (contains? (-> partition :regions keys set) region-key) + (let [{:keys [partitionEndpoint isRegionalized] :as service} (get services service-key) + endpoint-key (if (and partitionEndpoint (not isRegionalized)) + (keyword partitionEndpoint) + region-key)] (service-resolve partition (name service-key) service endpoint-key)))) (defn resolve* @@ -63,14 +67,32 @@ Return a map with the following keys: :partition The name of the partition. - :endpoint-name The name of the endpoint. + :region The region of the endpoint. :hostname The hostname to use. - :ssl-common-name The ssl-common-name to use (optional). - :credential-scope The Signature v4 credential scope (optional). - :signature-versions A list of possible signature versions (optional). + :sslCommonName The sslCommonName to use (optional). + :credentialScope The Signature v4 credential scope (optional). + :signatureVersions A list of possible signature versions (optional). :protocols A list of supported protocols." - [service region] - (some #(partition-resolve % service region) + [service-key region] + (some #(partition-resolve % service-key region) (:partitions (resolver)))) (def resolve (memoize resolve*)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defprotocol EndpointProvider + (-fetch [provider region])) + +(defn default-endpoint-provider [api endpointPrefix endpoint-override] + (reify EndpointProvider + (-fetch [_ region] + (if-let [ep (resolve (keyword endpointPrefix) (keyword region))] + (merge ep (if (string? endpoint-override) + {:hostname endpoint-override} + endpoint-override)) + {:cognitect.anomalies/category :cognitect.anomalies/fault + :cognitect.anomalies/message "No known endpoint."})))) + +(defn fetch [provider region] + (-fetch provider region)) diff --git a/src/cognitect/aws/http.clj b/src/cognitect/aws/http.clj index a7cf3caf..adc15d0a 100644 --- a/src/cognitect/aws/http.clj +++ b/src/cognitect/aws/http.clj @@ -1,7 +1,10 @@ (ns ^:skip-wiki cognitect.aws.http "Impl, don't call directly." (:require [clojure.edn :as edn] - [clojure.core.async :as a])) + [clojure.core.async :as a] + [cognitect.aws.dynaload :as dynaload])) + +(set! *warn-on-reflection* true) (defprotocol HttpClient (-submit [_ request channel] @@ -42,6 +45,9 @@ (-submit client request channel))) (defn stop + "Stops the client, releasing resources. + + Alpha. Subject to change." [client] (-stop client)) @@ -53,18 +59,6 @@ [url] (-> url slurp edn/read-string)) -(defonce ^:private dynalock (Object.)) - -(defn- dynaload - [s] - (let [ns (namespace s)] - (assert ns) - (locking dynalock - (require (symbol ns))) - (if-let [v (resolve s)] - @v - (throw (RuntimeException. (str "Var " s " is not on the classpath")))))) - ;; TODO consider providing config arguments to http constructor (defn- configured-client "If a single cognitect_aws_http.edn is found on the classpath, @@ -84,9 +78,11 @@ (defn resolve-http-client [http-client-or-sym] (let [c (or (when (symbol? http-client-or-sym) - ((dynaload http-client-or-sym))) + (let [ctor @(dynaload/load-var http-client-or-sym)] + (ctor))) http-client-or-sym - ((dynaload (configured-client))))] + (let [ctor @(dynaload/load-var (configured-client))] + (ctor)))] (when-not (client? c) (throw (ex-info "not an http client" {:provided http-client-or-sym :resolved c}))) diff --git a/src/cognitect/aws/http/cognitect.clj b/src/cognitect/aws/http/cognitect.clj index 6bb7d5ab..e9f40960 100644 --- a/src/cognitect/aws/http/cognitect.clj +++ b/src/cognitect/aws/http/cognitect.clj @@ -2,6 +2,8 @@ (:require [cognitect.http-client :as impl] [cognitect.aws.http :as aws])) +(set! *warn-on-reflection* true) + (defn create [] (let [c (impl/create {:trust-all true})] ;; FIX :trust-all diff --git a/src/cognitect/aws/interceptors.clj b/src/cognitect/aws/interceptors.clj index e4faed1a..61e494bf 100644 --- a/src/cognitect/aws/interceptors.clj +++ b/src/cognitect/aws/interceptors.clj @@ -6,6 +6,8 @@ (:require [cognitect.aws.service :as service] [cognitect.aws.util :as util])) +(set! *warn-on-reflection* true) + (defmulti modify-http-request (fn [service op-map http-request] (service/service-name service))) diff --git a/src/cognitect/aws/protocols/common.clj b/src/cognitect/aws/protocols/common.clj index 77f15f97..5909ea9a 100644 --- a/src/cognitect/aws/protocols/common.clj +++ b/src/cognitect/aws/protocols/common.clj @@ -6,6 +6,8 @@ (:require [cognitect.aws.util :as util]) (:import (java.util Date))) +(set! *warn-on-reflection* true) + (def status-codes->anomalies {403 :cognitect.anomalies/forbidden 404 :cognitect.anomalies/not-found diff --git a/src/cognitect/aws/protocols/ec2.clj b/src/cognitect/aws/protocols/ec2.clj index 8f696f2e..5777b6ee 100644 --- a/src/cognitect/aws/protocols/ec2.clj +++ b/src/cognitect/aws/protocols/ec2.clj @@ -11,11 +11,13 @@ [cognitect.aws.protocols.common :as common] [cognitect.aws.protocols.query :as query])) +(set! *warn-on-reflection* true) + (defn serialized-name [shape default] (or (:queryName shape) (when-let [name (:locationName shape)] - (apply str (Character/toUpperCase (first name)) (rest name))) + (apply str (Character/toUpperCase ^Character (first name)) (rest name))) default)) (defmulti serialize diff --git a/src/cognitect/aws/protocols/json.clj b/src/cognitect/aws/protocols/json.clj index e689d1e2..588d4258 100644 --- a/src/cognitect/aws/protocols/json.clj +++ b/src/cognitect/aws/protocols/json.clj @@ -9,6 +9,8 @@ [cognitect.aws.shape :as shape] [cognitect.aws.protocols.common :as common])) +(set! *warn-on-reflection* true) + (defmulti serialize (fn [_ shape data] (:type shape))) diff --git a/src/cognitect/aws/protocols/query.clj b/src/cognitect/aws/protocols/query.clj index 948129b3..fb10cb21 100644 --- a/src/cognitect/aws/protocols/query.clj +++ b/src/cognitect/aws/protocols/query.clj @@ -10,6 +10,8 @@ [cognitect.aws.util :as util] [cognitect.aws.protocols.common :as common])) +(set! *warn-on-reflection* true) + ; ---------------------------------------------------------------------------------------- ;; Serializer ;; ---------------------------------------------------------------------------------------- diff --git a/src/cognitect/aws/protocols/rest.clj b/src/cognitect/aws/protocols/rest.clj index d3780a9f..90a99ea1 100644 --- a/src/cognitect/aws/protocols/rest.clj +++ b/src/cognitect/aws/protocols/rest.clj @@ -14,6 +14,8 @@ [cognitect.aws.shape :as shape]) (:import java.util.Date)) +(set! *warn-on-reflection* true) + ;; ---------------------------------------------------------------------------------------- ;; Serializer ;; ---------------------------------------------------------------------------------------- @@ -27,13 +29,13 @@ [uri-template {:keys [required] :as input-shape} args] (str/replace uri-template #"\{([^}]+)\}" - (fn [[_ param]] + (fn [[_ ^String param]] (or (if (.endsWith param "+") (some-> args (get (keyword (.substring param 0 (dec (count param))))) util/url-encode - (.replace "%2F" "/") - (.replace "%7E" "~") + (str/replace "%2F" "/") + (str/replace "%7E" "~") remove-leading-slash) (some-> args (get (keyword param)) @@ -52,7 +54,7 @@ (defn append-querystring "Append the map of arguments args to the uri's querystring." - [uri shape args] + [^String uri shape args] (if-let [qs (util/query-string (mapcat (fn [[k v]] (when-let [member-shape (shape/member-shape shape k)] (serialize-qs-args member-shape @@ -194,10 +196,10 @@ :else data)) (defmethod parse-header-value "character" [_ data] (or data "")) (defmethod parse-header-value "boolean" [_ data] (= data "true")) -(defmethod parse-header-value "double" [_ data] (Double. data)) -(defmethod parse-header-value "float" [_ data] (Double. data)) -(defmethod parse-header-value "long" [_ data] (Long. data)) -(defmethod parse-header-value "integer" [_ data] (Long. data)) +(defmethod parse-header-value "double" [_ data] (Double/parseDouble ^String data)) +(defmethod parse-header-value "float" [_ data] (Double/parseDouble ^String data)) +(defmethod parse-header-value "long" [_ data] (Long/parseLong ^String data)) +(defmethod parse-header-value "integer" [_ data] (Long/parseLong ^String data)) (defmethod parse-header-value "blob" [_ data] (util/base64-decode data)) (defmethod parse-header-value "timestamp" [shape data] diff --git a/src/cognitect/aws/protocols/rest_json.clj b/src/cognitect/aws/protocols/rest_json.clj index bf5115e9..94c0b96a 100644 --- a/src/cognitect/aws/protocols/rest_json.clj +++ b/src/cognitect/aws/protocols/rest_json.clj @@ -10,6 +10,8 @@ [cognitect.aws.protocols.common :as common] [cognitect.aws.protocols.rest :as rest])) +(set! *warn-on-reflection* true) + (defmulti serialize (fn [_ shape data] (:type shape))) diff --git a/src/cognitect/aws/protocols/rest_xml.clj b/src/cognitect/aws/protocols/rest_xml.clj index 4f0b97f9..0e4b7879 100644 --- a/src/cognitect/aws/protocols/rest_xml.clj +++ b/src/cognitect/aws/protocols/rest_xml.clj @@ -8,6 +8,8 @@ [cognitect.aws.protocols.common :as common] [cognitect.aws.protocols.rest :as rest])) +(set! *warn-on-reflection* true) + (defmethod client/build-http-request "rest-xml" [{:keys [shapes operations metadata] :as service} op-map] (rest/build-http-request service diff --git a/src/cognitect/aws/region.clj b/src/cognitect/aws/region.clj index 42452e4a..d7264d2c 100644 --- a/src/cognitect/aws/region.clj +++ b/src/cognitect/aws/region.clj @@ -6,10 +6,13 @@ (:require [clojure.string :as str] [clojure.java.io :as io] [clojure.tools.logging :as log] + [clojure.core.async :as a] [cognitect.aws.util :as u] [cognitect.aws.config :as config] [cognitect.aws.ec2-metadata-utils :as ec2]) - (:import [java.io File])) + (:import (java.io File))) + +(set! *warn-on-reflection* true) (defn ^:skip-wiki valid-region "For internal use. Don't call directly. @@ -25,9 +28,8 @@ (defn chain-region-provider "Chain together multiple region providers. - Calls each provider in order until one return a non-nil result. - - Returns nil if none of the providers return a region. + `fetch` calls each provider in order until one returns a non-nil result, + or returns nil. Alpha. Subject to change." [providers] @@ -38,7 +40,7 @@ {:providers (map class providers)})))))) (defn environment-region-provider - "Return the region from the AWS_REGION env var, or nil if not present. + "Returns the region from the AWS_REGION env var, or nil if not present. Alpha. Subject to change." [] @@ -46,7 +48,7 @@ (fetch [_] (valid-region (u/getenv "AWS_REGION"))))) (defn system-property-region-provider - "Return the region from the aws.region system property, or nil if not present. + "Returns the region from the aws.region system property, or nil if not present. Alpha. Subject to change." [] @@ -54,7 +56,7 @@ (fetch [_] (valid-region (u/getProperty "aws.region"))))) (defn profile-region-provider - "Return the region in a AWS configuration profile. + "Returns the region from an AWS configuration profile. Arguments: @@ -84,17 +86,19 @@ (log/error t "Unable to fetch region from the AWS config file " (str f))))))))) (defn instance-region-provider - "Return the region from the ec2 instance's metadata service, + "Returns the region from the ec2 instance's metadata service, or nil if the service can not be found. Alpha. Subject to change." [http-client] - (reify RegionProvider - (fetch [_] (valid-region (ec2/get-ec2-instance-region http-client))))) - + (let [cached-region (atom nil)] + (reify RegionProvider + (fetch [_] + (or @cached-region + (reset! cached-region (valid-region (ec2/get-ec2-instance-region http-client)))))))) (defn default-region-provider - "Return a chain-region-provider comprising, in order: + "Returns a chain-region-provider with, in order: environment-region-provider system-property-region-provider @@ -108,3 +112,11 @@ (system-property-region-provider) (profile-region-provider) (instance-region-provider http-client)])) + +(defn fetch-async + "Returns a channel that will produce the result of calling fetch on + the provider. + + Alpha. Subject to change." + [provider] + (u/fetch-async fetch provider "region")) diff --git a/src/cognitect/aws/retry.clj b/src/cognitect/aws/retry.clj index dcf34e70..33ca919b 100644 --- a/src/cognitect/aws/retry.clj +++ b/src/cognitect/aws/retry.clj @@ -4,13 +4,15 @@ (ns cognitect.aws.retry (:require [clojure.core.async :as a])) +(set! *warn-on-reflection* true) + (defn ^:skip-wiki with-retry "For internal use. Do not call directly. - Calls req-fn, a function that wraps some operation and returns a - channel. When the response to req-fn is retriable? and backoff - returns an int, waits backoff ms and retries, otherwise puts - response on resp-chan." + Calls req-fn, a *non-blocking* function that wraps some operation + and returns a channel. When the response to req-fn is retriable? + and backoff returns an int, waits backoff ms and retries, otherwise + puts response on resp-chan." [req-fn resp-chan retriable? backoff] (a/go-loop [retries 0] (let [resp (a/> formats + (map #(try (util/parse-date % d) (catch java.text.ParseException _ nil))) + (filter identity) + first)) + (defn parse-date - [shape data] - (condp = (:timestampFormat shape) - "rfc822" (util/parse-date util/rfc822-date-format data) - "iso8601" (util/parse-date util/iso8601-date-format data) - (cond (int? data) - (java.util.Date. (* 1000 data)) + [{:keys [timestampFormat]} data] + (when data + (cond (= "rfc822" timestampFormat) + (parse-date* data util/rfc822-date-format) + (= "iso8601" timestampFormat) + (parse-date* data + util/iso8601-date-format + util/iso8601-msecs-date-format) + (int? data) + (java.util.Date. (* 1000 ^int data)) (double? data) (java.util.Date. (* 1000 (long data))) (re-matches #"^\d+$" data) - (java.util.Date. (* 1000 (read-string data))) + (java.util.Date. (* 1000 (long (read-string data)))) :else - (->> [util/iso8601-date-format - util/iso8601-msecs-date-format - util/rfc822-date-format] - (map #(try (util/parse-date % data) (catch java.text.ParseException _ nil))) - (filter identity) - first)))) + (parse-date* data + util/iso8601-date-format + util/iso8601-msecs-date-format + util/rfc822-date-format)))) ;; ---------------------------------------------------------------------------------------- ;; JSON Parser & Serializer @@ -396,10 +406,10 @@ (defmethod xml-parse* "string" [_ nodes] (or (data nodes) "")) (defmethod xml-parse* "character" [_ nodes] (or (data nodes) "")) (defmethod xml-parse* "boolean" [_ nodes] (= (data nodes) "true")) -(defmethod xml-parse* "double" [_ nodes] (Double. (data nodes))) -(defmethod xml-parse* "float" [_ nodes] (Double. (data nodes))) -(defmethod xml-parse* "long" [_ nodes] (Long. (data nodes))) -(defmethod xml-parse* "integer" [_ nodes] (Long. (data nodes))) +(defmethod xml-parse* "double" [_ nodes] (Double/parseDouble ^String (data nodes))) +(defmethod xml-parse* "float" [_ nodes] (Double/parseDouble ^String (data nodes))) +(defmethod xml-parse* "long" [_ nodes] (Long/parseLong ^String (data nodes))) +(defmethod xml-parse* "integer" [_ nodes] (Long/parseLong ^String (data nodes))) (defmethod xml-parse* "blob" [_ nodes] (util/base64-decode (data nodes))) (defmethod xml-parse* "timestamp" [shape nodes] diff --git a/src/cognitect/aws/signers.clj b/src/cognitect/aws/signers.clj index ff5ef6b3..89fd5c55 100644 --- a/src/cognitect/aws/signers.clj +++ b/src/cognitect/aws/signers.clj @@ -138,16 +138,14 @@ (string-to-sign request auth-info)))) (defn v4-sign-http-request - [service region credentials http-request & {:keys [content-sha256-header?]}] + [service endpoint credentials http-request & {:keys [content-sha256-header?]}] (let [{:keys [:aws/access-key-id :aws/secret-access-key :aws/session-token]} credentials auth-info {:access-key-id access-key-id :secret-access-key secret-access-key :service (or (service/signing-name service) (service/endpoint-prefix service)) - :region (if (and (-> service :metadata :globalEndpoint) - (not= "s3" (service/service-name service))) - "us-east-1" - (name region))} + :region (or (get-in endpoint [:credentialScope :region]) + (:region endpoint))} req (cond-> http-request session-token (assoc-in [:headers "x-amz-security-token"] session-token) content-sha256-header? (assoc-in [:headers "x-amz-content-sha256"] (hashed-body http-request)))] @@ -160,13 +158,13 @@ (signature auth-info req))))) (defmethod client/sign-http-request "v4" - [service region credentials http-request] - (v4-sign-http-request service region credentials http-request)) + [service endpoint credentials http-request] + (v4-sign-http-request service endpoint credentials http-request)) (defmethod client/sign-http-request "s3" - [service region credentials http-request] - (v4-sign-http-request service region credentials http-request :content-sha256-header? true)) + [service endpoint credentials http-request] + (v4-sign-http-request service endpoint credentials http-request :content-sha256-header? true)) (defmethod client/sign-http-request "s3v4" - [service region credentials http-request] - (v4-sign-http-request service region credentials http-request :content-sha256-header? true)) + [service endpoint credentials http-request] + (v4-sign-http-request service endpoint credentials http-request :content-sha256-header? true)) diff --git a/src/cognitect/aws/util.clj b/src/cognitect/aws/util.clj index fb87c827..4b4b6cca 100644 --- a/src/cognitect/aws/util.clj +++ b/src/cognitect/aws/util.clj @@ -7,14 +7,15 @@ [clojure.data.xml :as xml] [jsonista.core :as json] [byte-streams :as byte-streams] + [clojure.core.async :as a] [clojure.java.io :as io]) - (:import [java.text SimpleDateFormat] + (:import [java.util.concurrent Executors ExecutorService ThreadFactory] + [java.text SimpleDateFormat] [java.util Date TimeZone] [java.util UUID] [java.io InputStream] [java.nio.charset Charset] [java.security MessageDigest] - [org.apache.commons.codec.binary Hex] [javax.crypto Mac] [javax.crypto.spec SecretKeySpec] [java.nio ByteBuffer] @@ -64,9 +65,19 @@ (def ^ThreadLocal rfc822-date-format (date-format "EEE, dd MMM yyyy HH:mm:ss z")) -(defn hex-encode - [^bytes bytes] - (String. (Hex/encodeHex bytes true))) +(let [hex-chars (char-array [\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \a \b \c \d \e \f])] + (defn hex-encode + [^bytes bytes] + (let [bl (alength bytes) + ca (char-array (* 2 bl))] + (loop [i (int 0) + c (int 0)] + (if (< i bl) + (let [b (long (bit-and (long (aget bytes i)) 255))] + (aset ca c ^char (aget hex-chars (unsigned-bit-shift-right b 4))) + (aset ca (unchecked-inc-int c) (aget hex-chars (bit-and b 15))) + (recur (unchecked-inc-int i) (unchecked-add-int c 2))) + (String. ca)))))) (defn sha-256 "Returns the sha-256 digest (bytes) of data, which can be a @@ -229,19 +240,25 @@ (or data {}) (:members shape))) -(defonce ^:private dynalock (Object.)) - -(defn dynaload - [s] - (let [ns (namespace s)] - (assert ns) - (locking dynalock - (require (symbol ns))) - (let [v (resolve s)] - (if v - @v - (throw (RuntimeException. (str "Var " s " is not on the classpath"))))))) - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; used to fetch creds and region + +(defn fetch-async + "Internal use. Do not call directly." + [fetch provider item] + (a/thread + (try + ;; lock on the provider to avoid redundant concurrent requests + ;; before the provider has a chance to cache the results of the + ;; first fetch. + (or (locking provider + (fetch provider)) + {:cognitect.anomalies/category :cognitect.anomalies/fault + :cognitect.anomalies/message (format "Unable to fetch %s. See log for more details." item)}) + (catch Throwable t + {:cognitect.anomalies/category :cognitect.anomalies/fault + ::throwable t + :cognitect.anomalies/message (format "Unable to fetch %s." item)})))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Wrappers - here to support testing with-redefs since diff --git a/test/resources/cognitect/protocols/output/rest-xml.json b/test/resources/cognitect/protocols/output/rest-xml.json index 0c846334..1a777bae 100644 --- a/test/resources/cognitect/protocols/output/rest-xml.json +++ b/test/resources/cognitect/protocols/output/rest-xml.json @@ -32,5 +32,40 @@ } } ] + }, + { + "description": "Single string in payload", + "metadata": {"protocol": "rest-xml"}, + "shapes": { + "OutputShape": { + "type": "structure", + "members": { + "Data": { + "shape": "DataShape" + } + }, + "payload" : "Data" + }, + "DataShape": { + "type": "string" + } + }, + "cases": [ + { + "given": { + "output": { + "shape": "OutputShape" + }, + "name": "OperationName" + }, + "result": { + "Data": "{\"a\":1}" + }, + "response": { + "status_code": 200, + "body": "{\"a\":1}" + } + } + ] } ] diff --git a/test/src/cognitect/aws/api_test.clj b/test/src/cognitect/aws/api_test.clj new file mode 100644 index 00000000..ab250830 --- /dev/null +++ b/test/src/cognitect/aws/api_test.clj @@ -0,0 +1,33 @@ +(ns cognitect.aws.api-test + (:require [clojure.test :as t :refer [deftest testing is]] + [cognitect.aws.client :as client] + [cognitect.aws.http :as http] + [cognitect.aws.client.api :as aws] + [cognitect.aws.client.shared :as shared])) + +(deftest test-underlying-http-client + (testing "defaults to shared client" + (let [clients (repeatedly 5 #(aws/client {:api :s3 :region "us-east-1"}))] + (is (= #{(shared/http-client)} + (into #{(shared/http-client)} + (->> clients (map (fn [c] (-> c client/-get-info :http-client)))))))))) + +(deftest test-stop + (let [call-count (atom 0) + default (aws/client {:api :s3 :region "us-east-1"}) + supplied-shared (aws/client {:api :s3 :region "us-east-1" + :http-client (shared/http-client)}) + supplied-unshared (aws/client {:api :s3 :region "us-east-1" + :http-client (http/resolve-http-client nil)})] + (with-redefs [http/stop (fn [_] (swap! call-count inc))] + (testing "has no effect when aws-client uses the default shared http-client" + (aws/stop default) + (is (zero? @call-count))) + (testing "has no effect when user supplies the shared http-client" + (aws/stop supplied-shared) + (is (zero? @call-count))) + (testing "forwards to http/stop when user supplies a different http-client" + (aws/stop supplied-unshared) + (is (= 1 @call-count)))) + ;; now actually stop the aws-client with the non-shared http-client + (aws/stop supplied-unshared))) diff --git a/test/src/cognitect/aws/client_test.clj b/test/src/cognitect/aws/client_test.clj new file mode 100644 index 00000000..bf670698 --- /dev/null +++ b/test/src/cognitect/aws/client_test.clj @@ -0,0 +1,94 @@ +(ns cognitect.aws.client-test + (:require [clojure.test :as t :refer [deftest testing is]] + [cognitect.aws.client.api :as aws] + [cognitect.aws.client :as client] + [cognitect.aws.client.api.async :as api.async] + [cognitect.aws.http :as http] + [cognitect.aws.region :as region] + [cognitect.aws.credentials :as creds] + [clojure.core.async :as a])) + +(defn stub-http-client [result] + (reify http/HttpClient + (-submit [_ _ ch] + (a/go (a/>! ch result)) + ch) + (-stop [_]))) + +(defn stub-credentials-provider [creds] + (reify creds/CredentialsProvider + (fetch [_] creds))) + +(defn stub-region-provider [region] + (reify region/RegionProvider + (fetch [_] region))) + +(def params {:api :s3 + ;; use an anomaly to bypass parsing http-response + :http-client (stub-http-client {:cognitect.anomalies/category :cognitect.aws/test + :cognitect.anomalies/message "test"}) + :region-provider (stub-region-provider :us-east-1) + :credentials-provider (stub-credentials-provider + {:access-key-id "a" + :secret-access-key "b"})}) + +(deftest test-handle-http-response + (testing "returns http-response if it is an anomaly" + (is (= {:cognitect.anomalies/category :does-not-matter} + (#'client/handle-http-response {} {} {:cognitect.anomalies/category :does-not-matter}))))) + +(deftest test-meta + (let [res (aws/invoke (aws/client params) {:op :ListBuckets})] + (testing "request meta includes :http-request" + (is (= {:uri "/" + :server-name "s3.amazonaws.com" + :body nil} + (select-keys (:http-request (meta res)) [:uri :server-name :body])))) + (testing "request meta includes raw response" + (is (= {:cognitect.anomalies/category :cognitect.aws/test, + :cognitect.anomalies/message "test", + :body nil} + (:http-response (meta res))))))) + +(deftest test-providers + (testing "base case" + (let [aws-client (aws/client params)] + (is (= "test" + (:cognitect.anomalies/message + (aws/invoke aws-client {:op :ListBuckets})))))) + (testing "nil creds (regression test - should not hang)" + (let [aws-client (aws/client (assoc params + :credentials-provider + (stub-credentials-provider nil)))] + (is (re-find #"^Unable to fetch credentials" + (:cognitect.anomalies/message + (aws/invoke aws-client {:op :ListBuckets})))))) + (testing "empty creds (regression test - should not hang)" + (let [aws-client (aws/client (assoc params + :credentials-provider + (stub-credentials-provider {})))] + (is (= "test" + (:cognitect.anomalies/message + (aws/invoke aws-client {:op :ListBuckets})))))) + (testing "nil region (regression test - should not hang)" + (let [aws-client (aws/client (assoc params + :region-provider + (stub-region-provider nil)))] + (is (re-find #"^Unable to fetch region" + (:cognitect.anomalies/message + (aws/invoke aws-client {:op :ListBuckets})))))) + (testing "empty region (regression test - should not hang)" + (let [aws-client (aws/client (assoc params + :region-provider + (stub-region-provider "")))] + (is (re-find #"^No known endpoint." + (:cognitect.anomalies/message + (aws/invoke aws-client {:op :ListBuckets}))))))) + +(deftest validate-requests? + (let [aws-client (aws/client params)] + (is (not (api.async/validate-requests? aws-client))) + (api.async/validate-requests aws-client true) + (is (api.async/validate-requests? aws-client)) + (api.async/validate-requests aws-client false) + (is (not (api.async/validate-requests? aws-client))))) diff --git a/test/src/cognitect/aws/credentials_test.clj b/test/src/cognitect/aws/credentials_test.clj index 7b514901..b6806a12 100644 --- a/test/src/cognitect/aws/credentials_test.clj +++ b/test/src/cognitect/aws/credentials_test.clj @@ -2,16 +2,14 @@ ;; All rights reserved. (ns cognitect.aws.credentials-test - (:require [cognitect.aws.credentials :as credentials] + (:require [clojure.test :as t :refer [deftest testing use-fixtures is]] + [clojure.java.io :as io] + [cognitect.aws.credentials :as credentials] [cognitect.aws.util :as u] [cognitect.aws.test.utils :as tu] [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils] - [cognitect.aws.ec2-metadata-utils-test :as ec2-metadata-utils-test] - [clojure.spec.alpha :as s] - [clojure.spec.test.alpha :as stest] - [clojure.spec.gen.alpha :as gen] - [clojure.test :refer :all] - [clojure.java.io :as io])) + [cognitect.aws.ec2-metadata-utils-test :as ec2-metadata-utils-test]) + (:import (java.time Instant))) (use-fixtures :once ec2-metadata-utils-test/test-server) @@ -58,20 +56,36 @@ (is (nil? (credentials/fetch p))))))))) (deftest system-properites-credentials-provider-test + (testing "all vars present" + (with-redefs [u/getProperty (tu/stub-getProperty {"aws.accessKeyId" "foo" + "aws.secretKey" "bar" + "aws.sessionToken" "baz"})] + (is (= {:aws/access-key-id "foo" + :aws/secret-access-key "bar" + :aws/session-token "baz"} + (credentials/fetch + (credentials/system-property-credentials-provider)))))) (testing "required vars present" (with-redefs [u/getProperty (tu/stub-getProperty {"aws.accessKeyId" "foo" - "aws.secretKey" "bar"})] - (is (map? (credentials/fetch - (credentials/system-property-credentials-provider)))))) + "aws.secretKey" "bar"})] + (is (= {:aws/access-key-id "foo" + :aws/secret-access-key "bar" + :aws/session-token nil} + (credentials/fetch + (credentials/system-property-credentials-provider)))))) (testing "required vars blank" (doall (for [props [{} {"aws.accessKeyId" "foo"} {"aws.secretKey" "bar"} {"aws.accessKeyId" "" - "aws.secretKey" "bar"} + "aws.secretKey" "bar"} + {"aws.accessKeyId" " " + "aws.secretKey" "bar"} {"aws.accessKeyId" "foo" - "aws.secretKey" ""}]] + "aws.secretKey" ""} + {"aws.accessKeyId" "foo" + "aws.secretKey" " "}]] (with-redefs [u/getProperty (tu/stub-getProperty props)] (let [p (credentials/system-property-credentials-provider)] (is (nil? (credentials/fetch p))))))))) @@ -148,13 +162,13 @@ {:aws/access-key-id "id" :aws/secret-access-key "secret" ::credentials/ttl 1})) - creds (credentials/auto-refreshing-credentials p)] + creds (credentials/cached-credentials-with-auto-refresh p)] (credentials/fetch creds) - (Thread/sleep 5000) + (Thread/sleep 2500) (let [refreshed @cnt] (credentials/stop creds) - (Thread/sleep 2000) - (is (<= 3 refreshed) "The credentials have been refreshed.") + (Thread/sleep 1000) + (is (= 3 refreshed) "The credentials have been refreshed.") (is (= refreshed @cnt) "We stopped the auto-refreshing process.")))) (deftest basic-credentials-provider @@ -166,7 +180,7 @@ (defn minutes-from-now [m] - (-> (java.time.Instant/now) + (-> (Instant/now) (.plusSeconds (* m 60)) (.with java.time.temporal.ChronoField/NANO_OF_SECOND 0) str)) @@ -176,14 +190,17 @@ (is (= 3600 (credentials/calculate-ttl {})))) (testing "refreshes in exp - 5 minutes" (let [c {:Expiration (minutes-from-now 60)}] - (is (< (- (* 55 60) 5) + (is (< (- (* 55 60) (* 5 60)) (credentials/calculate-ttl c) - (+ (* 55 60) 5))))) + (+ (* 55 60) (* 5 60)))))) (testing "short expiration minimum is one minute" (let [c {:Expiration (minutes-from-now 3)}] + (is (= 60 (credentials/calculate-ttl c))))) + (testing "supports java.util.Date (return value from sts :AssumeRole)" + (let [c {:Expiration (java.util.Date/from (Instant/parse (minutes-from-now 3)))}] (is (= 60 (credentials/calculate-ttl c)))))) (comment - (run-tests) + (t/run-tests) ) diff --git a/test/src/cognitect/aws/ec2_metadata_utils_test.clj b/test/src/cognitect/aws/ec2_metadata_utils_test.clj index 3a504950..9d70025d 100644 --- a/test/src/cognitect/aws/ec2_metadata_utils_test.clj +++ b/test/src/cognitect/aws/ec2_metadata_utils_test.clj @@ -5,6 +5,7 @@ (:require [clojure.test :refer :all] [clojure.core.async :as a] [cognitect.aws.http :as http] + [cognitect.aws.client.shared :as shared] [cognitect.aws.test.ec2-metadata-utils-server :as ec2-metadata-utils-server] [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils] [byte-streams :as byte-streams])) @@ -15,15 +16,14 @@ (defn test-server [f] ;; NOTE: starting w/ 0 generates a random port - (let [server-stop-fn (ec2-metadata-utils-server/start 0) + (let [server-stop-fn (ec2-metadata-utils-server/start 0) test-server-port (-> server-stop-fn meta :local-port)] (try (System/setProperty ec2-metadata-utils/ec2-metadata-service-override-system-property (str "http://localhost:" test-server-port)) (binding [*test-server-port* test-server-port - *http-client* (http/resolve-http-client 'cognitect.aws.http.cognitect/create)] - (f) - (http/stop *http-client*)) + *http-client* (shared/http-client)] + (f)) (finally (server-stop-fn) (System/clearProperty ec2-metadata-utils/ec2-metadata-service-override-system-property))))) diff --git a/test/src/cognitect/aws/region_test.clj b/test/src/cognitect/aws/region_test.clj index 269c972e..b87f1223 100644 --- a/test/src/cognitect/aws/region_test.clj +++ b/test/src/cognitect/aws/region_test.clj @@ -4,6 +4,7 @@ (ns cognitect.aws.region-test (:require [clojure.test :refer :all] [clojure.java.io :as io] + [clojure.core.async :as a] [cognitect.aws.region :as region] [cognitect.aws.util :as u] [cognitect.aws.test.utils :as tu] @@ -40,8 +41,23 @@ (is (= "us-west-1" (region/fetch (region/profile-region-provider)))))))) (deftest instance-region-provider-test - (testing "The provider obtains the region from the instance metadata correctly." - (is (= "us-east-1" (region/fetch (region/instance-region-provider ec2-metadata-utils-test/*http-client*)))))) + (testing "provider caches the fetched value" + (let [orig-get-region-fn cognitect.aws.ec2-metadata-utils/get-ec2-instance-region + request-counter (atom 0) + fetch-counter (atom 0)] + (with-redefs [cognitect.aws.ec2-metadata-utils/get-ec2-instance-region + (fn [http] + (swap! fetch-counter inc) + (orig-get-region-fn http))] + (let [num-requests 10 + p (region/instance-region-provider ec2-metadata-utils-test/*http-client*) + chans (repeatedly num-requests + #(do + (swap! request-counter inc) + (region/fetch-async p)))] + (is (apply = "us-east-1" (map #(a/ parsed :content last :content)))) (is (re-matches #"\n\s+inner-value\s+" (-> parsed :content last :content first)))))) +(deftest hex-encode + (is (= (util/hex-encode (byte-array (range -128 128))) + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"))) + (comment (run-tests)