handlers.clj 16.4 KB
Newer Older
1 2 3 4 5 6 7
(ns puppetlabs.puppetdb.http.handlers
  (:require [puppetlabs.puppetdb.http :as http]
            [bidi.schema :as bidi-schema]
            [puppetlabs.puppetdb.http.query :as http-q]
            [puppetlabs.puppetdb.query.paging :as paging]
            [puppetlabs.puppetdb.query-eng :refer [produce-streaming-body
                                                   stream-query-result]]
8
            [puppetlabs.puppetdb.middleware :refer [validate-query-params
9 10
                                                    parent-check
                                                    handler-schema]]
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
            [puppetlabs.comidi :as cmdi]
            [schema.core :as s]
            [puppetlabs.puppetdb.schema :as pls]
            [puppetlabs.puppetdb.catalogs :refer [catalog-query-schema]]
            [puppetlabs.kitchensink.core :as kitchensink]
            [puppetlabs.puppetdb.scf.storage-utils :as sutils]
            [puppetlabs.puppetdb.utils :refer [assoc-when]]
            [clojure.walk :refer [keywordize-keys]]))

(def params-schema {(s/optional-key :optional) [s/Str]
                    (s/optional-key :required) [s/Str]})

;; General route/handler construction functions

(pls/defn-validated extract-query :- bidi-schema/RoutePair
  ([routes :- bidi-schema/RoutePair]
   (extract-query {:optional paging/query-params} routes))
  ([param-spec :- params-schema
    routes :- bidi-schema/RoutePair]
   (cmdi/wrap-routes routes #(http-q/extract-query % param-spec))))

(pls/defn-validated append-handler :- bidi-schema/RoutePair
  [route :- bidi-schema/RoutePair
   handler-to-append :- handler-schema]
  (cmdi/wrap-routes route #(comp % handler-to-append)))

37
(pls/defn-validated create-query-handler :- handler-schema
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
  "Creates a new query handler for the given `entity` and `version`."
  [version :- s/Keyword
   entity :- s/Str
   & handler-fns :- [handler-schema]]
  (apply comp
         (http-q/query-handler version)
         (http-q/restrict-query-to-entity entity)
         handler-fns))

(pls/defn-validated wrap-with-parent-check :- bidi-schema/RoutePair
  "Wraps all handlers found in `route` with the parent-check middleware"
  [route :- bidi-schema/RoutePair
   version :- s/Keyword
   entity :- s/Keyword
   route-param-key :- s/Keyword]
  (cmdi/wrap-routes route #(parent-check % version entity route-param-key)))

55
(pls/defn-validated experimental-root-routes :- bidi-schema/RoutePair
56
  [version :- s/Keyword]
57 58
  (cmdi/ANY "" []
            (-> (http-q/query-handler version)
59
                (http-q/extract-query-pql {:optional (conj paging/query-params "ast_only")
60 61
                                           :required ["query"]})
                (http/experimental-warning "The root endpoint is experimental"))))
62 63 64 65 66 67 68 69

(defn report-data-responder
  "Respond with either metrics or logs for a given report hash.
   `entity` should be either :metrics or :logs."
  [version entity]
  (fn [{:keys [globals route-params]}]
    (let [query ["from" entity ["=" "hash" (:hash route-params)]]]
      (produce-streaming-body version {:query query}
70
                              (http-q/narrow-globals globals)))))
71 72

(defn route-param [param-name]
73
  [#"[\w%\.~:-]*" param-name])
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

;; Handlers checking for a single entity

(defn status-response
  "Executes `query` and if a result is found, calls `found-fn` with
  that result, returns 404 otherwise."
  [version query globals found-fn not-found-response]
  (if-let [query-result (first (stream-query-result version query {} globals))]
    (http/json-response (found-fn query-result))
    not-found-response))

(defn catalog-status
  "Produces a response body for a request to retrieve the catalog for the node in route-params"
  [version]
  (fn [{:keys [globals route-params]}]
    (let [node (:node route-params)]
      (status-response version
                       ["from" "catalogs" ["=" "certname" node]]
92
                       (http-q/narrow-globals globals)
93 94 95 96 97 98 99 100 101 102 103
                       #(s/validate catalog-query-schema
                                    (kitchensink/mapvals sutils/parse-db-json [:edges :resources] %))
                       (http/status-not-found-response "catalog" node)))))

(defn factset-status
  "Produces a response body for a request to retrieve the factset for the node in route-params"
  [version]
  (fn [{:keys [globals route-params]}]
    (let [node (:node route-params)]
      (status-response version
                       ["from" "factsets" ["=" "certname" node]]
104
                       (http-q/narrow-globals globals)
105 106 107 108 109 110 111 112 113 114
                       identity
                       (http/status-not-found-response "factset" node)))))

(defn node-status
  "Produce a response body for a single environment."
  [version]
  (fn [{:keys [globals route-params]}]
    (let [node (:node route-params)]
      (status-response version
                       ["from" "nodes" ["=" "certname" node]]
115
                       (http-q/narrow-globals globals)
116 117 118 119 120 121 122 123 124 125
                       identity
                       (http/status-not-found-response "node" node)))))

(defn environment-status
  "Produce a response body for a single environment."
  [version]
  (fn [{:keys [globals route-params]}]
    (let [environment (:environment route-params)]
      (status-response version
                       ["from" "environments" ["=" "name" environment]]
126
                       (http-q/narrow-globals globals)
127 128 129
                       identity
                       (http/status-not-found-response "environment" environment)))))

130 131 132 133 134 135 136 137 138 139 140
(defn producer-status
  "Produce a response body for a single producer."
  [version]
  (fn [{:keys [globals route-params]}]
    (let [producer (:producer route-params)]
      (status-response version
                       ["from" "producers" ["=" "name" producer]]
                       (http-q/narrow-globals globals)
                       identity
                       (http/status-not-found-response "producer" producer)))))

141 142 143 144 145 146 147 148 149 150 151 152 153 154
;; Routes

(pls/defn-validated events-routes :- bidi-schema/RoutePair
  "Ring app for querying events"
  [version :- s/Keyword]
  (extract-query
   {:optional (concat
               ["query"
                "distinct_resources"
                "distinct_start_time"
                "distinct_end_time"]
               paging/query-params)}
   (cmdi/routes
    (cmdi/ANY "" []
155
              (create-query-handler version "events")))))
156 157 158 159 160 161

(pls/defn-validated reports-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (cmdi/routes
   (extract-query
    (cmdi/ANY "" []
162
              (create-query-handler version "reports")))
163

164 165
   (cmdi/context ["/" :hash "/events"]
                 (-> (events-routes version)
166
                     (append-handler (comp http-q/restrict-query-to-report))
167
                     (wrap-with-parent-check version :report :hash)))
168

169 170 171
   (cmdi/ANY ["/" :hash "/metrics"] []
             (-> (report-data-responder version "report_metrics")
                 (parent-check version :report :hash)
172
                 (validate-query-params {:optional ["pretty"]})))
173

174 175 176
   (cmdi/ANY ["/" :hash "/logs"] []
             (-> (report-data-responder version "report_logs")
                 (parent-check version :report :hash)
177
                 (validate-query-params {:optional ["pretty"]})))))
178 179 180 181 182 183

(pls/defn-validated resources-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   (cmdi/routes
    (cmdi/ANY "" []
184
              (create-query-handler version "resources"  http-q/restrict-query-to-active-nodes))
185

186 187
    (cmdi/context ["/" (route-param :type)]
                  (cmdi/ANY "" []
188
                            (create-query-handler version "resources"
189
                                            http-q/restrict-resource-query-to-type
190
                                            http-q/restrict-query-to-active-nodes))
191
                  (cmdi/ANY ["/" (route-param :title)] []
192
                            (create-query-handler version "resources"
193 194
                                            http-q/restrict-resource-query-to-title
                                            http-q/restrict-resource-query-to-type
195
                                            http-q/restrict-query-to-active-nodes))))))
196

197 198 199 200
(pls/defn-validated edge-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   (cmdi/ANY "" []
201
             (create-query-handler version "edges" http-q/restrict-query-to-active-nodes))))
202

203 204 205 206 207 208
(pls/defn-validated catalog-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   (cmdi/routes

    (cmdi/ANY "" []
209
              (create-query-handler version "catalogs"))
210 211 212

    (cmdi/context ["/" (route-param :node)]
                  (cmdi/ANY "" []
213
                            (catalog-status version))
214

215 216 217
                  (cmdi/context "/edges"
                                (-> (edge-routes version)
                                    (wrap-with-parent-check version :catalog :node)))
218

219 220
                  (cmdi/context "/resources"
                                (-> (resources-routes version)
221
                                    (append-handler http-q/restrict-query-to-node)
222
                                    (wrap-with-parent-check version :catalog :node)))))))
223 224 225

(pls/defn-validated facts-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
226 227 228
  (extract-query
   (cmdi/routes
    (cmdi/ANY "" []
229
              (create-query-handler version "facts" http-q/restrict-query-to-active-nodes))
230 231

    (cmdi/context ["/" (route-param :fact)]
232

233
                  (cmdi/ANY "" []
234
                            (create-query-handler version "facts"
235
                                            http-q/restrict-fact-query-to-name
236
                                            http-q/restrict-query-to-active-nodes))
237 238

                  (cmdi/ANY ["/" (route-param :value)] []
239
                            (create-query-handler version "facts"
240 241
                                            http-q/restrict-fact-query-to-name
                                            http-q/restrict-fact-query-to-value
242
                                            http-q/restrict-query-to-active-nodes))))))
243

244 245 246 247 248 249 250 251
(pls/defn-validated inventory-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
    (cmdi/routes
      (cmdi/ANY "" []
                (create-query-handler version "inventory"
                                      http-q/restrict-query-to-active-nodes)))))

252 253
(pls/defn-validated factset-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
254 255 256
  (extract-query
   (cmdi/routes
    (cmdi/ANY "" []
257
              (create-query-handler version "factsets" http-q/restrict-query-to-active-nodes))
258 259 260

    (cmdi/context ["/" :node]
                  (cmdi/ANY "" []
261
                            (factset-status version))
262 263

                  (cmdi/ANY "/facts" []
264
                            (-> (create-query-handler version "factsets" http-q/restrict-query-to-node)
265
                                (parent-check version :factset :node)))))))
266 267 268

(pls/defn-validated fact-names-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
269
  (extract-query
270 271 272 273 274 275 276
   (cmdi/ANY "" []
             (comp
              (fn [{:keys [params globals puppetdb-query]}]
                (let [puppetdb-query (assoc-when puppetdb-query :order_by [[:name :ascending]])]
                  (produce-streaming-body
                   version
                   (http-q/validate-distinct-options! (merge (keywordize-keys params) puppetdb-query))
277
                   (http-q/narrow-globals globals))))
278
              (http-q/restrict-query-to-entity "fact_names")))))
279 280 281

(pls/defn-validated node-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
282 283 284
  (extract-query
   (cmdi/routes
    (cmdi/ANY "" []
285
              (create-query-handler version "nodes" http-q/restrict-query-to-active-nodes))
286 287 288
    (cmdi/context ["/" (route-param :node)]
                  (cmdi/ANY "" []
                            (-> (node-status version)
289 290
                                (validate-query-params {:optional ["pretty"]})))

291 292
                  (cmdi/context "/facts"
                                (-> (facts-routes version)
293
                                    (append-handler http-q/restrict-query-to-node)
294 295 296
                                    (wrap-with-parent-check version :node :node)))
                  (cmdi/context "/resources"
                                (-> (resources-routes version)
297
                                    (append-handler http-q/restrict-query-to-node)
298
                                    (wrap-with-parent-check version :node :node)))))))
299 300 301

(pls/defn-validated environments-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
302 303 304
  (cmdi/routes
   (extract-query
    (cmdi/ANY "" []
305
              (create-query-handler version "environments")))
306 307
   (cmdi/context ["/" (route-param :environment)]
                 (cmdi/ANY "" []
308 309
                   (validate-query-params (environment-status version)
                                          {:optional ["pretty"]}))
310

311 312 313 314 315 316
                 (wrap-with-parent-check
                  (cmdi/routes
                   (extract-query
                    (cmdi/context "/facts"
                                  (-> (facts-routes version)
                                      (append-handler http-q/restrict-query-to-environment))))
317

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
                   (extract-query
                    (cmdi/context "/resources"
                                  (-> (resources-routes version)
                                      (append-handler http-q/restrict-query-to-environment))))

                   (extract-query
                    (cmdi/context "/reports"
                                  (-> (reports-routes version)
                                      (append-handler http-q/restrict-query-to-environment))))

                   (extract-query
                    {:optional (concat
                                ["query"
                                 "distinct_resources"
                                 "distinct_start_time"
                                 "distinct_end_time"]
                                paging/query-params)}

                    (cmdi/context "/events"
                                  (-> (events-routes version)
                                      (append-handler http-q/restrict-query-to-environment)))))
                  version :environment :environment))))
340

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
(pls/defn-validated producers-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (cmdi/routes
   (extract-query
    (cmdi/ANY "" []
              (create-query-handler version "producers")))
   (cmdi/context ["/" (route-param :producer)]
                 (cmdi/ANY "" []
                   (validate-query-params (producer-status version)
                                          {:optional ["pretty"]}))
                 (wrap-with-parent-check
                  (cmdi/routes
                   (extract-query
                    (cmdi/context "/factsets"
                                  (-> (factset-routes version)
                                      (append-handler http-q/restrict-query-to-producer))))

                  (extract-query
                    (cmdi/context "/catalogs"
                                  (-> (catalog-routes version)
                                      (append-handler http-q/restrict-query-to-producer))))

                   (extract-query
                    (cmdi/context "/reports"
                                  (-> (reports-routes version)
                                      (append-handler http-q/restrict-query-to-producer)))))
                  version :producer :producer))))

369 370 371 372
(pls/defn-validated fact-contents-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   (cmdi/ANY "" []
373
             (create-query-handler version "fact_contents" http-q/restrict-query-to-active-nodes))))
374 375 376 377 378

(pls/defn-validated fact-path-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   (cmdi/ANY "" []
379
             (create-query-handler version "fact_paths"))))
380 381 382 383 384 385 386 387 388 389

(pls/defn-validated event-counts-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   {:required ["summarize_by"]
    :optional (concat ["counts_filter" "count_by"
                       "distinct_resources" "distinct_start_time"
                       "distinct_end_time"]
                      paging/query-params)}
   (cmdi/ANY "" []
390
             (create-query-handler version "event_counts"))))
391 392 393 394 395 396 397 398 399

(pls/defn-validated agg-event-counts-routes :- bidi-schema/RoutePair
  [version :- s/Keyword]
  (extract-query
   {:required ["summarize_by"]
    :optional ["query" "counts_filter" "count_by"
               "distinct_resources" "distinct_start_time"
               "distinct_end_time"]}
   (cmdi/ANY "" []
400 401
             (create-query-handler version
                                   "aggregate_event_counts"))))