diff --git a/.github/workflows/lein-test.yaml b/.github/workflows/lein-test.yaml index 42bf56b..f36371f 100644 --- a/.github/workflows/lein-test.yaml +++ b/.github/workflows/lein-test.yaml @@ -26,7 +26,7 @@ jobs: distribution: temurin java-version: ${{ matrix.java }} - name: Install Clojure tools - uses: DeLaGuardo/setup-clojure@10.2 + uses: DeLaGuardo/setup-clojure@12.5 with: cli: latest # Clojure CLI based on tools.deps lein: latest # Leiningen diff --git a/project.clj b/project.clj index c7552a9..b542a52 100644 --- a/project.clj +++ b/project.clj @@ -46,8 +46,7 @@ :profiles {:defaults {:dependencies [[puppetlabs/http-client] [puppetlabs/trapperkeeper :classifier "test"] [com.puppetlabs/trapperkeeper-webserver-jetty10] - [puppetlabs/kitchensink :classifier "test"]] - :resource-paths ["dev-resources"]} + [puppetlabs/kitchensink :classifier "test"]]} :dev-dependencies {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]} :dev [:defaults :dev-dependencies] :fips-dependencies {:dependencies [[org.bouncycastle/bcpkix-fips] @@ -70,8 +69,7 @@ ;; per https://github.com/technomancy/leiningen/issues/1907 ;; the provided profile is necessary for lein jar / lein install - :provided {:dependencies [[org.bouncycastle/bcpkix-jdk18on]] - :resource-paths ["dev-resources"]} + :provided {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]} :testutils {:source-paths ^:replace ["test"] :java-source-paths ^:replace []}} diff --git a/test/puppetlabs/trapperkeeper/services/metrics/metrics_service_test.clj b/test/puppetlabs/trapperkeeper/services/metrics/metrics_service_test.clj index 6b46f24..798a096 100644 --- a/test/puppetlabs/trapperkeeper/services/metrics/metrics_service_test.clj +++ b/test/puppetlabs/trapperkeeper/services/metrics/metrics_service_test.clj @@ -15,10 +15,9 @@ [puppetlabs.trapperkeeper.services.webrouting.webrouting-service :as webrouting-service] [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10-service] [puppetlabs.trapperkeeper.testutils.bootstrap :refer [with-app-with-config]] - [puppetlabs.trapperkeeper.testutils.logging :refer [with-test-logging]] + [puppetlabs.trapperkeeper.testutils.logging :refer [with-test-logging] :as logging] [puppetlabs.trapperkeeper.app :as app] [puppetlabs.kitchensink.core :as ks] - [puppetlabs.trapperkeeper.testutils.logging :as logging] [puppetlabs.trapperkeeper.core :as trapperkeeper] [puppetlabs.trapperkeeper.services.metrics.metrics-testutils :as utils] [puppetlabs.trapperkeeper.services :as tk-services] @@ -97,127 +96,126 @@ (deftest test-metrics-service (testing "Can boot metrics service and access registry" - (with-app-with-config - app - services - (assoc-in metrics-service-config [:metrics :metrics-webservice :mbeans :enabled] true) - - (testing "metrics service functions" - (let [svc (app/get-service app :MetricsService)] - (testing "`get-metrics-registry` called without domain works" - (is (instance? MetricRegistry (metrics-protocol/get-metrics-registry svc)))) - - (testing "`get-metrics-registry` called with domain works" - (is (instance? MetricRegistry - (metrics-protocol/get-metrics-registry svc :pl.foo.reg)))) - - (testing "`get-server-id` works" - (is (= "localhost" (metrics-protocol/get-server-id svc)))))) - - (testing "returns latest status for all services" - (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans") - body (parse-response resp)] - (is (= 200 (:status resp))) - (doseq [[metric path] body - :let [resp (http-client/get (str "http://localhost:8180/metrics/v1" path))]] - (is (= 200 (:status resp))))) - - (let [resp (http-client/get "http://localhost:8180/metrics/v2/list/java.lang") - body (parse-response resp)] - (is (= 200 (:status resp))) - (doseq [mbean (keys (get body "value")) - :let [url (str "http://localhost:8180/metrics/v2/read/" - (jolokia-encode (str "java.lang:" mbean)) - ;; NOTE: Some memory pools intentionally don't - ;; implement MBean attributes. This results - ;; in an error being thrown when those - ;; attributes are read and is expected. - "?ignoreErrors=true") - resp (http-client/get url) - body (parse-response resp)]] - ;; NOTE: Jolokia returns 200 OK for most responses. The actual - ;; status code is in the JSON payload that makes up the body. - (is (= 200 (get body "status")))))) - - (testing "register should add a metric to the registry with a keyword domain" - (let [svc (app/get-service app :MetricsService) - register-and-get-metric (fn [domain metric] - (metrics/register - (metrics-protocol/get-metrics-registry svc domain) - (metrics/host-metric-name "localhost" metric) - (metrics/gauge 2)) - (http-client/get - (str "http://localhost:8180/metrics/v1/mbeans/" - (codec/url-encode - (str (name domain) ":name=puppetlabs.localhost." metric))))) - resp (register-and-get-metric :pl.test.reg "foo")] - (is (= 200 (:status resp))) - (is (= {"Value" 2} (parse-response resp))))) - - (testing "querying multiple metrics via POST should work" - (let [svc (app/get-service app :MetricsService) - registry (metrics-protocol/get-metrics-registry svc :pl.other.reg)] - (metrics/register registry - (metrics/host-metric-name "localhost" "foo") - (metrics/gauge 2)) - (metrics/register registry - (metrics/host-metric-name "localhost" "bar") - (metrics/gauge 500)) - - (let [resp (http-client/post - "http://localhost:8180/metrics/v1/mbeans" - {:body (json/generate-string - ["pl.other.reg:name=puppetlabs.localhost.foo" - "pl.other.reg:name=puppetlabs.localhost.bar"])}) - body (parse-response resp)] - (is (= 200 (:status resp))) - (is (= [{"Value" 2} {"Value" 500}] body))) + (with-test-logging + (with-app-with-config + app + services + (assoc-in metrics-service-config [:metrics :metrics-webservice :mbeans :enabled] true) - (let [resp (http-client/post - "http://localhost:8180/metrics/v1/mbeans" - {:body (json/generate-string - {:foo "pl.other.reg:name=puppetlabs.localhost.foo" - :bar "pl.other.reg:name=puppetlabs.localhost.bar"})}) - body (parse-response resp)] - (is (= 200 (:status resp))) - (is (= {"foo" {"Value" 2} - "bar" {"Value" 500}} body))) + (testing "metrics service functions" + (let [svc (app/get-service app :MetricsService)] + (testing "`get-metrics-registry` called without domain works" + (is (instance? MetricRegistry (metrics-protocol/get-metrics-registry svc)))) - (let [resp (http-client/post - "http://localhost:8180/metrics/v1/mbeans" - {:body (json/generate-string - "pl.other.reg:name=puppetlabs.localhost.foo")}) - body (parse-response resp)] + (testing "`get-metrics-registry` called with domain works" + (is (instance? MetricRegistry + (metrics-protocol/get-metrics-registry svc :pl.foo.reg)))) + + (testing "`get-server-id` works" + (is (= "localhost" (metrics-protocol/get-server-id svc)))))) + + (testing "returns latest status for all services" + (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans") + body (parse-response resp)] + (is (= 200 (:status resp))) + (doseq [[metric path] body + :let [resp (http-client/get (str "http://localhost:8180/metrics/v1" path))]] + (is (= 200 (:status resp))))) + + (let [resp (http-client/get "http://localhost:8180/metrics/v2/list/java.lang") + body (parse-response resp)] + (is (= 200 (:status resp))) + (doseq [mbean (keys (get body "value")) + :let [url (str "http://localhost:8180/metrics/v2/read/" + (jolokia-encode (str "java.lang:" mbean)) + ;; NOTE: Some memory pools intentionally don't + ;; implement MBean attributes. This results + ;; in an error being thrown when those + ;; attributes are read and is expected. + "?ignoreErrors=true") + resp (http-client/get url) + body (parse-response resp)]] + ;; NOTE: Jolokia returns 200 OK for most responses. The actual + ;; status code is in the JSON payload that makes up the body. + (is (= 200 (get body "status")))))) + + (testing "register should add a metric to the registry with a keyword domain" + (let [svc (app/get-service app :MetricsService) + register-and-get-metric (fn [domain metric] + (metrics/register + (metrics-protocol/get-metrics-registry svc domain) + (metrics/host-metric-name "localhost" metric) + (metrics/gauge 2)) + (http-client/get + (str "http://localhost:8180/metrics/v1/mbeans/" + (codec/url-encode + (str (name domain) ":name=puppetlabs.localhost." metric))))) + resp (register-and-get-metric :pl.test.reg "foo")] (is (= 200 (:status resp))) - (is (= {"Value" 2} body))) - - (let [resp (http-client/post - "http://localhost:8180/metrics/v1/mbeans" - {:body "{\"malformed json"}) - body (slurp (:body resp))] - (is (= 400 (:status resp))) - (is (re-find #"Unexpected end-of-input" body))) - - (let [resp (http-client/post - "http://localhost:8180/metrics/v2" - {:body (json/generate-string - [{:type "read" :mbean "pl.other.reg:name=puppetlabs.localhost.foo"} - {:type "read" :mbean "pl.other.reg:name=puppetlabs.localhost.bar"}])}) - body (parse-response resp true)] - (is (= [200 200] (map :status body))) - (is (= [{:Value 2} {:Value 500}] (map :value body)))))) - - (testing "metrics/v2 should deny write requests" - (with-test-logging + (is (= {"Value" 2} (parse-response resp))))) + + (testing "querying multiple metrics via POST should work" + (let [svc (app/get-service app :MetricsService) + registry (metrics-protocol/get-metrics-registry svc :pl.other.reg)] + (metrics/register registry + (metrics/host-metric-name "localhost" "foo") + (metrics/gauge 2)) + (metrics/register registry + (metrics/host-metric-name "localhost" "bar") + (metrics/gauge 500)) + + (let [resp (http-client/post + "http://localhost:8180/metrics/v1/mbeans" + {:body (json/generate-string + ["pl.other.reg:name=puppetlabs.localhost.foo" + "pl.other.reg:name=puppetlabs.localhost.bar"])}) + body (parse-response resp)] + (is (= 200 (:status resp))) + (is (= [{"Value" 2} {"Value" 500}] body))) + + (let [resp (http-client/post + "http://localhost:8180/metrics/v1/mbeans" + {:body (json/generate-string + {:foo "pl.other.reg:name=puppetlabs.localhost.foo" + :bar "pl.other.reg:name=puppetlabs.localhost.bar"})}) + body (parse-response resp)] + (is (= 200 (:status resp))) + (is (= {"foo" {"Value" 2} + "bar" {"Value" 500}} body))) + + (let [resp (http-client/post + "http://localhost:8180/metrics/v1/mbeans" + {:body (json/generate-string + "pl.other.reg:name=puppetlabs.localhost.foo")}) + body (parse-response resp)] + (is (= 200 (:status resp))) + (is (= {"Value" 2} body))) + + (let [resp (http-client/post + "http://localhost:8180/metrics/v1/mbeans" + {:body "{\"malformed json"}) + body (slurp (:body resp))] + (is (= 400 (:status resp))) + (is (re-find #"Unexpected end-of-input" body))) + + (let [resp (http-client/post + "http://localhost:8180/metrics/v2" + {:body (json/generate-string + [{:type "read" :mbean "pl.other.reg:name=puppetlabs.localhost.foo"} + {:type "read" :mbean "pl.other.reg:name=puppetlabs.localhost.bar"}])}) + body (parse-response resp true)] + (is (= [200 200] (map :status body))) + (is (= [{:Value 2} {:Value 500}] (map :value body)))))) + + (testing "metrics/v2 should deny write requests" (let [resp (http-client/get (str "http://localhost:8180/metrics/v2/write/" (jolokia-encode "java.lang:type=Memory") "/Verbose/true")) body (parse-response resp)] - (is (= 403 (get body "status")))))) + (is (= 403 (get body "status"))))) - (testing "metrics/v2 should deny exec requests" - (with-test-logging + (testing "metrics/v2 should deny exec requests" (let [resp (http-client/get (str "http://localhost:8180/metrics/v2/exec/" (jolokia-encode "java.util.logging:type=Logging") @@ -227,17 +225,19 @@ (deftest metrics-service-with-tk-auth (testing "tk-auth works when included in bootstrap" - (with-app-with-config - app - (conj services authorization-service/authorization-service) - (merge metrics-service-config auth-config ssl-webserver-config) - (let [resp (http-client/get "https://localhost:8180/metrics/v2/list" ssl-opts)] - (is (= 200 (:status resp)))) - (let [resp (http-client/get "https://localhost:8180/metrics/v2" ssl-opts)] - (is (= 403 (:status resp))))))) + (with-test-logging + (with-app-with-config + app + (conj services authorization-service/authorization-service) + (merge metrics-service-config auth-config ssl-webserver-config) + (let [resp (http-client/get "https://localhost:8180/metrics/v2/list" ssl-opts)] + (is (= 200 (:status resp)))) + (let [resp (http-client/get "https://localhost:8180/metrics/v2" ssl-opts)] + (is (= 403 (:status resp)))))))) (deftest metrics-v1-endpoint-disabled-by-default (testing "metrics/v1 is disabled by default, returns 404" + (with-test-logging (with-app-with-config app [jetty10-service/jetty10-service @@ -246,7 +246,7 @@ metrics-webservice] metrics-service-config (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans")] - (is (= 404 (:status resp))))))) + (is (= 404 (:status resp)))))))) (deftest metrics-endpoint-with-jolokia-disabled-test (testing "metrics/v2 returns 404 when Jolokia is not enabled" @@ -263,50 +263,52 @@ (deftest metrics-endpoint-with-permissive-jolokia-policy (testing "metrics/v2 allows exec requests when configured with a permissive policy" - (let [config (assoc-in metrics-service-config - [:metrics :metrics-webservice :jolokia :servlet-init-params :policyLocation] - (str "file://" test-resources-dir "/jolokia-access-permissive.xml"))] - (with-app-with-config - app - [jetty10-service/jetty10-service - webrouting-service/webrouting-service - metrics-service - metrics-webservice] - config - (let [resp (http-client/get - (str "http://localhost:8180/metrics/v2/exec/" - (jolokia-encode "java.util.logging:type=Logging") - "/getLoggerLevel/root")) - body (parse-response resp)] - (is (= 200 (get body "status")))))))) + (with-test-logging + (let [config (assoc-in metrics-service-config + [:metrics :metrics-webservice :jolokia :servlet-init-params :policyLocation] + (str "file://" test-resources-dir "/jolokia-access-permissive.xml"))] + (with-app-with-config + app + [jetty10-service/jetty10-service + webrouting-service/webrouting-service + metrics-service + metrics-webservice] + config + (let [resp (http-client/get + (str "http://localhost:8180/metrics/v2/exec/" + (jolokia-encode "java.util.logging:type=Logging") + "/getLoggerLevel/root")) + body (parse-response resp)] + (is (= 200 (get body "status"))))))))) (deftest metrics-endpoint-with-jmx-disabled-test (testing "returns data for jvm even when jmx is not enabled" - (let [config (-> metrics-service-config - (assoc-in [:metrics :metrics-webservice :mbeans :enabled] true) - (assoc :registries - {:pl.no.jmx {:reporters - {:jmx - {:enabled false}}}}))] - (with-app-with-config - app - [jetty10-service/jetty10-service - webrouting-service/webrouting-service - metrics-service - metrics-webservice] - config - (testing "returns latest status for all services" - (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans") - body (parse-response resp)] - (is (= 200 (:status resp))) - (is (not (empty? body))))) - (testing "returns Memoory mbean information" - (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans/java.lang%3Atype%3DMemory") - body (parse-response resp) - heap-memory (get body "HeapMemoryUsage")] - (is (= 200 (:status resp))) - (is (= #{"committed" "init" "max" "used"} (ks/keyset heap-memory))) - (is (every? #(< 0 %) (vals heap-memory))))))))) + (with-test-logging + (let [config (-> metrics-service-config + (assoc-in [:metrics :metrics-webservice :mbeans :enabled] true) + (assoc :registries + {:pl.no.jmx {:reporters + {:jmx + {:enabled false}}}}))] + (with-app-with-config + app + [jetty10-service/jetty10-service + webrouting-service/webrouting-service + metrics-service + metrics-webservice] + config + (testing "returns latest status for all services" + (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans") + body (parse-response resp)] + (is (= 200 (:status resp))) + (is (not (empty? body))))) + (testing "returns Memoory mbean information" + (let [resp (http-client/get "http://localhost:8180/metrics/v1/mbeans/java.lang%3Atype%3DMemory") + body (parse-response resp) + heap-memory (get body "HeapMemoryUsage")] + (is (= 200 (:status resp))) + (is (= #{"committed" "init" "max" "used"} (ks/keyset heap-memory))) + (is (every? #(< 0 %) (vals heap-memory)))))))))) (deftest get-metrics-registry-service-function-test (with-app-with-config