pax_global_header00006660000000000000000000000064131345034010014504gustar00rootroot0000000000000052 comment=f29324967920879fce198ec81f2e278cdfcdf245 dujour-version-check-0.2.2/000077500000000000000000000000001313450340100155535ustar00rootroot00000000000000dujour-version-check-0.2.2/.gitignore000066400000000000000000000001241313450340100175400ustar00rootroot00000000000000/target /classes /checkouts pom.xml pom.xml.asc *.jar *.class /.lein-* /.nrepl-port dujour-version-check-0.2.2/.travis.yml000066400000000000000000000007721313450340100176720ustar00rootroot00000000000000language: clojure lein: lein2 jdk: - oraclejdk7 - openjdk7 script: ./ext/travisci/test.sh notifications: email: false hipchat: rooms: secure: ASvt1XwEYbgkKuYZjZHytwg/6Y53Tg4T7QhohiDB4Xb1dmJueqPFpV2ko/VjHCa18JjLiUq0nWcDpRjsqaGGvJ5FSxTyyWDKtZsg1sUf4F+7aZ5vq0Dzg8Uzvdu7m9X1Uszvs9zf6wJ+Jobq4xck1xpPYxFT/+ei2Q2STrJ9xwQ= template: - "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}" - "Change view: %{compare_url}" - "Build details: %{build_url}"dujour-version-check-0.2.2/CHANGELOG.md000066400000000000000000000006311313450340100173640ustar00rootroot00000000000000## 0.1.3 * Allow arbitrary parameters to be included in the `request-values` map, rather than just `:product-name` * Update the http-client dependency to the latest version (0.4.4) ## 0.1.2 * No changes. Bumping version to 0.1.2 for the a public clojars release. ## 0.1.1 * Added a new public function, `get-version-string`, which returns the version of a particular product as a string dujour-version-check-0.2.2/CONTRIBUTING.md000066400000000000000000000010241313450340100200010ustar00rootroot00000000000000# How to contribute Third-party patches are essential for keeping Puppet Labs open-source projects great. We want to keep it as easy as possible to contribute changes that allow you to get the most out of our projects. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. For more info, see our canonical guide to contributing: [https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md) dujour-version-check-0.2.2/LICENSE000066400000000000000000000260751313450340100165720ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. dujour-version-check-0.2.2/MAINTAINERS000066400000000000000000000013611313450340100172510ustar00rootroot00000000000000{ "version": 1, "file_format": "This MAINTAINERS file format is described at https://github.com/puppetlabs/maintainers", "issues": "https://tickets.puppetlabs.com/browse/PE", "internal_list": "https://groups.google.com/a/puppet.com/forum/?hl=en#!forum/team-product-analytics", "people": [ { "github": "briancain", "email": "brian.cain@puppet.com", "name": "Brian Cain" }, { "github": "smcclellan", "email": "scott.mcclellan@puppet.com", "name": "Scott McClellan" }, { "github": "pcarlisle", "email": "patrick@puppet.com", "name": "Patrick Carlisle" }, { "github": "vine77", "email": "nathan.ward@puppet.com", "name": "Nathan Ward" } ] } dujour-version-check-0.2.2/README.md000066400000000000000000000053331313450340100170360ustar00rootroot00000000000000# version-check-service [![Build Status](https://travis-ci.org/puppetlabs/dujour-version-check.png?branch=master)](https://travis-ci.org/puppetlabs/dujour-version-check) [![Clojars Project](https://img.shields.io/clojars/v/puppetlabs/dujour-version-check.svg)](https://clojars.org/puppetlabs/dujour-version-check) This library allows you to perform version checks with dujour. To use this in your project, add the following to your `project.clj` file: ``` [puppetlabs/dujour-version-check "0.1.2"] ``` _Note:_ The latest version number of this library can be found above Then, call the `check-for-updates!` function. This function takes two arguments, `request-values` and `update-server-url`. `update-server-url` should be a string containing the URL of the update server. `request-values` is a map that currently only supports a single key, `:product-name`. The value contained at this key can either be a string containing the artifact-id or a map with the following schema: ```clj {:group-id schema/Str :artifact-id schema/Str} ``` If only the artifact id is provided, the group id will default to `"puppetlabs.packages"`. The request map can also accept `:certname` and `:cacert` strings, which can be used to uniquely identify a user. These values will be SHA-512 hashed and sent as `site-id` and `host-id`, respectively, before being sent to the server. This library provides one other public API function, `get-version-string`. This function takes one argument, `product-name`, which should be the artifact id as a string. It optionally takes one more argument, `group-id`, which should be the group-id of the desired artifact as a string. ## License Copyright © 2014 Puppet Labs Distributed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) Support ------- Please log tickets and issues at our [JIRA tracker](http://tickets.puppetlabs.com). A [mailing list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is available for asking questions and getting help from others. In addition there is an active #puppet channel on Freenode. We use semantic version numbers for our releases, and recommend that users stay as up-to-date as possible by upgrading to patch releases and minor releases as they become available. Bugfixes and ongoing development will occur in minor releases for the current major version. Security fixes will be backported to a previous major version on a best-effort basis, until the previous major version is no longer maintained. Long-term support, including security patches and bug fixes, is available for commercial customers. Please see the following page for more details: [Puppet Enterprise Support Lifecycle](http://puppetlabs.com/misc/puppet-enterprise-lifecycle) dujour-version-check-0.2.2/dev-resources/000077500000000000000000000000001313450340100203415ustar00rootroot00000000000000dujour-version-check-0.2.2/dev-resources/logback-test.xml000066400000000000000000000004531313450340100234440ustar00rootroot00000000000000 %d %-5p [%c{2}] %m%n dujour-version-check-0.2.2/ext/000077500000000000000000000000001313450340100163535ustar00rootroot00000000000000dujour-version-check-0.2.2/ext/travisci/000077500000000000000000000000001313450340100201775ustar00rootroot00000000000000dujour-version-check-0.2.2/ext/travisci/test.sh000077500000000000000000000000341313450340100215120ustar00rootroot00000000000000#!/bin/bash lein2 test :alldujour-version-check-0.2.2/project.clj000066400000000000000000000024351313450340100177170ustar00rootroot00000000000000(def ks-version "1.0.0") (def tk-version "1.0.0") (def tk-jetty-version "1.0.0") (defproject puppetlabs/dujour-version-check "0.2.2" :description "Dujour Version Check library" :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/tools.logging "0.3.1"] [prismatic/schema "0.2.2"] [puppetlabs/http-client "0.4.4"] [ring/ring-codec "1.0.0"] [cheshire "5.3.1"] [trptcolin/versioneer "0.1.0"] [slingshot "0.10.3"]] :deploy-repositories [["releases" {:url "https://clojars.org/repo" :username :env/clojars_jenkins_username :password :env/clojars_jenkins_password :sign-releases false}]] :profiles {:dev {:dependencies [[puppetlabs/trapperkeeper ~tk-version :classifier "test" :scope "test"] [puppetlabs/kitchensink ~ks-version :classifier "test" :scope "test"] [puppetlabs/trapperkeeper-webserver-jetty9 ~tk-jetty-version] [puppetlabs/trapperkeeper-webserver-jetty9 ~tk-jetty-version :classifier "test"] [ring-mock "0.1.5"]]}} ) dujour-version-check-0.2.2/src/000077500000000000000000000000001313450340100163425ustar00rootroot00000000000000dujour-version-check-0.2.2/src/puppetlabs/000077500000000000000000000000001313450340100205215ustar00rootroot00000000000000dujour-version-check-0.2.2/src/puppetlabs/dujour/000077500000000000000000000000001313450340100220315ustar00rootroot00000000000000dujour-version-check-0.2.2/src/puppetlabs/dujour/version_check.clj000066400000000000000000000212011313450340100253410ustar00rootroot00000000000000(ns puppetlabs.dujour.version-check (:require [cheshire.core :as json] [clojure.set :as set] [clojure.tools.logging :as log] [puppetlabs.http.client.sync :as client] [puppetlabs.kitchensink.core :as ks] [schema.core :as schema] [slingshot.slingshot :refer [throw+]] [trptcolin.versioneer.core :as version]) (:import com.fasterxml.jackson.core.JsonParseException java.io.IOException org.apache.http.HttpException)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Constants (def default-group-id "puppetlabs.packages") (def default-update-server-url "http://updates.puppetlabs.com") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Schemas (def ProductCoords {:group-id schema/Str :artifact-id schema/Str}) (def ProductName (schema/conditional string? schema/Str map? ProductCoords)) (def RequestValues {(schema/optional-key :certname) schema/Str (schema/optional-key :cacert) schema/Str :product-name ProductName schema/Any schema/Any}) (def UpdateInfo {:version schema/Str :newer schema/Bool :link schema/Str :product schema/Str (schema/optional-key :message) schema/Str (schema/optional-key :whitelist) {schema/Keyword {schema/Keyword schema/Str}}}) (def RequestResult {:status schema/Int :body schema/Any :resp schema/Any}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Private (defn schema-match? [schema obj] (nil? (schema/check schema obj))) (schema/defn get-coords :- ProductCoords [product-name :- ProductName] (condp schema-match? product-name schema/Str {:group-id default-group-id :artifact-id product-name} ProductCoords product-name)) (schema/defn version* :- schema/Str "Get the version number of this installation." ([group-id artifact-id] (version* group-id artifact-id nil)) ([group-id artifact-id default] (version/get-version group-id artifact-id default))) (def version "Get the version number of this installation." (memoize version*)) (defn- throw-connection-error [cause] (throw+ {:kind ::connection-error :msg (.getMessage cause) :cause cause})) (schema/defn ^:always-validate update-info-post :- RequestResult "Make a POST request to the puppetlabs server to determine the latest available version." [request-values :- RequestValues update-server-url :- schema/Str] (let [product-name (:product-name request-values) {:keys [group-id artifact-id]} (get-coords product-name) current-version (str (or (:version request-values) (version group-id artifact-id ""))) version-data {:version current-version} request-body (-> request-values (dissoc :product-name) (set/rename-keys {:agent-os :agent_os :puppet-agent-versions :puppet_agent_versions}) (assoc "product" artifact-id) (assoc "group" group-id) (merge version-data) json/generate-string) _ (log/tracef "Making update request to %s with data: %s" update-server-url request-body) {:keys [status body] :as resp} (try (client/post update-server-url {:headers {"Accept" "application/json"} :body request-body :as :text}) (catch HttpException e (throw-connection-error e)) (catch IOException e (throw-connection-error e)))] (log/tracef "Received response from %s, status: %s, body: %s" update-server-url status body) {:status status :body body :resp resp})) (defn- throw-unexpected-response [body] (throw+ {:kind ::unexpected-response :msg "Server returned HTTP code 200 but the body was not understood." :details {:body body}})) (defn- parse-body [body] (try (let [update-response (json/parse-string body true)] (if (schema/check (merge UpdateInfo {schema/Any schema/Any}) update-response) (throw-unexpected-response body) (select-keys update-response [:version :newer :link :product :message :whitelist]))) (catch JsonParseException _ (throw-unexpected-response body)))) (schema/defn ^:always-validate update-info :- UpdateInfo "Make a request to the puppetlabs server to determine the latest available version. Attempts a POST request before falling back to a GET request. Returns the JSON object received from the server, which is expected to be a map containing keys `:version`, `:newer`, and `:link`. Returns `nil` if the request does not succeed for some reason." [request-values :- RequestValues update-server-url :- schema/Str] (let [{:keys [status body resp]} (update-info-post request-values update-server-url)] (cond (= status 200) (parse-body body) :else (throw+ {:kind ::http-error-code :msg (format "Server returned HTTP status code %s" status) :details {:status status :body body}})))) (defn validate-config! [request-values update-server-url] ;; if this ends up surfacing error messages that aren't very user-friendly, ;; we can improve the validation logic. (schema/validate RequestValues request-values) (schema/validate (schema/maybe schema/Str) update-server-url)) (defn- version-check "This will fetch the latest version number and log if the system is out of date." [request-values update-server-url] (log/debugf "Checking for newer versions of %s" (:product-name request-values)) (let [update-server-url (or update-server-url default-update-server-url) {:keys [version newer link] :as response} (update-info request-values update-server-url) link-str (if link (format " Visit %s for details." link) "") update-msg (format "Newer version %s is available!%s" version link-str)] (when newer (log/info update-msg)) response)) (defn get-hash "Returns a SHA-512 encoded value of the given string." [data] (let [md (. java.security.MessageDigest getInstance "sha-512")] (.update md (.getBytes data)) (let [bytes (.digest md)] (reduce #(str %1 (format "%02x" %2)) "" bytes)))) (defn- update-with-ids [parameters] (let [keys-present-to-hash (vec (keys (select-keys parameters [:cacert :certname])))] (-> (ks/mapvals get-hash keys-present-to-hash parameters) (set/rename-keys {:certname :host-id :cacert :site-id})))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Public (defn ^:deprecated check-for-updates! "Check whether a newer version of the software is available and submit telemetry data. This is deprecated in favor of `check-for-update` and `send-telemetry` which separate these two operations (since users may want to opt out of telemetry while still being notified of new versions)." ([request-values update-server-url] (check-for-updates! request-values update-server-url nil)) ([request-values update-server-url callback-fn] (validate-config! request-values update-server-url) (future (let [arguments (update-with-ids request-values) server-response (version-check arguments update-server-url)] (if-not (nil? callback-fn) (callback-fn server-response) server-response))))) (schema/defn check-for-update "Check whether a newer version of the software is available. It does not submit any extra data beyond the product name and version. If a newer version is available it will log a message to that effect and return a map describing the newer version (see `UpdateInfo`)." [request-values :- RequestValues update-server-url :- (schema/maybe schema/Str)] (version-check (select-keys request-values [:product-name :version]) update-server-url)) (schema/defn send-telemetry "Submit telemetry data. This will submit a map of data to the telemetry service at the given url." [request-values :- RequestValues update-server-url :- schema/Str] (update-info (update-with-ids request-values) update-server-url)) (defn get-version-string ([product-name] (get-version-string product-name default-group-id)) ([product-name group-id] (version group-id product-name))) dujour-version-check-0.2.2/test/000077500000000000000000000000001313450340100165325ustar00rootroot00000000000000dujour-version-check-0.2.2/test/puppetlabs/000077500000000000000000000000001313450340100207115ustar00rootroot00000000000000dujour-version-check-0.2.2/test/puppetlabs/dujour/000077500000000000000000000000001313450340100222215ustar00rootroot00000000000000dujour-version-check-0.2.2/test/puppetlabs/dujour/version_check_test.clj000066400000000000000000000317511313450340100266030ustar00rootroot00000000000000(ns puppetlabs.dujour.version-check-test (:require [cheshire.core :as json] [clojure.test :refer :all] [puppetlabs.dujour.version-check :refer :all] [puppetlabs.kitchensink.core :as ks] [puppetlabs.trapperkeeper.testutils.logging :refer [with-test-logging]] [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] [ring.util.codec :as codec] [schema.test :as schema-test] [slingshot.test])) (use-fixtures :once schema-test/validate-schemas) (def version-check #'puppetlabs.dujour.version-check/version-check) (deftest test-get-coords (testing "group-id should use the default if not specified" (is (= {:group-id "puppetlabs.packages" :artifact-id "foo"} (get-coords "foo")))) (testing "should use group-id if specified" (is (= {:group-id "foo.foo" :artifact-id "foo"} (get-coords {:group-id "foo.foo" :artifact-id "foo"}))))) (defn parse-params [params encoding] (let [params (codec/form-decode params (str encoding))] (if (map? params) params {}))) (defn return-all-as-message-app [req] (let [query-string (:query-string req) get-params (if query-string (parse-params query-string "UTF-8")) body (:body req) post-params (if body (json/parse-string (slurp body))) params (if (nil? post-params) get-params post-params)] {:status 200 :body (json/generate-string {:newer true :link "http://foo.com" :message (json/generate-string params) :product "foo" :version "9000.0.0" :whitelist {"namespace.name" {:datatype "string", :description "something"}}})})) (defn server-error-app [_] {:status 500 :body "aaaaaaaaaaaaaaaaaaaaaaaaaa"}) (defn malformed-response-app [_] {:status 123 :body ""}) (defn empty-response-app [_] {:status 200 :body "{}"}) (defn malformed-body-app [_] {:status 200 :body "wow what's going on here"}) (defn extra-fields-app [_] {:status 200 :body (json/encode {:newer true :link "http://zombo.com" :message "woooo" :product "zombocom" :version "1000000" :whitelist {"namespace.name" {:datatype "string", :description "something"}} :you "can" :do "anything" :at "zombocom"})}) (deftest test-check-for-update (testing "logs the correct version information during a valid version-check" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [return-val (check-for-update {:certname "some-certname" :cacert "some-cacert" :product-name "foo"} (format "http://localhost:%s" port))] (is (= (:version return-val) "9000.0.0")) (is (:newer return-val)) (is (logged? #"Newer version 9000.0.0 is available!" :info)))))) (testing "filters out extra parameters" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [return-val (check-for-update {:certname "some-certname" :cacert "some-cacert" :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port))] (is (= (:version return-val) "9000.0.0")) (is (= ((json/parse-string (:message return-val)) "database-version") nil)) (is (= (:whitelist return-val) {:namespace.name {:datatype "string", :description "something"}})) (is (:newer return-val)) (is (logged? #"Newer version 9000.0.0 is available!" :info)))))) (testing "allows but does not return extra response fields" (with-test-logging (jetty9/with-test-webserver extra-fields-app port (is (= [:version :newer :link :product :message :whitelist] (keys (check-for-update {:certname "some-certname" :cacert "some-cacert" :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port))))))))) (deftest test-send-telemetry (testing "does not send the actual certname or cacert" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [certname "some-certname" cacert "some-cacert" return-val (send-telemetry {:certname certname :cacert cacert :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port)) message (json/parse-string (:message return-val))] (is (= (message "host-id") (get-hash certname))) (is (= (message "site-id") (get-hash cacert))) (is (nil? (message "certname"))) (is (nil? (message "cacert"))))))) (testing "sends agent_os instead of agent-os" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [agent-os {"debian" 15 "centos" 5} return-val (send-telemetry {:agent-os agent-os :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port)) message (json/parse-string (:message return-val))] (is (= (message "agent_os") agent-os)) (is (nil? (message "agent-os"))))))) (testing "sends puppet_agent_versions instead of puppet-agent-versions" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [puppet-agent-versions {"1.6.7" 15 "1.4.5" 5} return-val (send-telemetry {:puppet-agent-versions puppet-agent-versions :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port)) message (json/parse-string (:message return-val))] (is (= (message "puppet_agent_versions") puppet-agent-versions)) (is (nil? (message "puppet-agent-versions"))))))) (testing "doesn't clobber agent_os" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [agent_os {"debian" 15 "centos" 5} return-val (send-telemetry {:agent_os agent_os :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port)) message (json/parse-string (:message return-val))] (is (= (message "agent_os") agent_os)) (is (nil? (message "agent-os"))))))) (testing "only submits agent_os if both agent-os and agent_os are present" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [agent_os {"debian" 15 "centos" 5} agent-os {"debian" 20 "centos" 10} return-val (send-telemetry {:agent_os agent_os :agent-os agent-os :product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port)) message (json/parse-string (:message return-val))] ;; The original `agent_os` should be clobbered in this case. (is (= (message "agent_os") agent-os)) (is (nil? (message "agent-os"))))))) (testing "sends the version number" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [return-val (send-telemetry {:product-name "foo" :version "9.4"} (format "http://localhost:%s" port))] (is (= ((json/parse-string (:message return-val)) "version") "9.4")) (is (= ((json/parse-string (:message return-val)) "product") "foo")))))) (testing "allows omitting certname and cacert for backwards compatibility" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [return-val (send-telemetry {:product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port))] (is (= (:version return-val) "9000.0.0"))))))) (deftest error-handling-update (testing "throws a slingshot exception when there is a connection error" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/connection-error] (check-for-update {:product-name "foo" :database-version "9.4"} "http://localhost:1")))))) (testing "throws a slingshot exception when an error is returned by the server" (with-test-logging (jetty9/with-test-webserver server-error-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/http-error-code] (check-for-update {:product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port))))))) (testing "throws a slingshot exception when the server returns a bad response (catches apache http exceptions)" (with-test-logging (jetty9/with-test-webserver malformed-response-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/connection-error] (check-for-update {:product-name "foo"} (format "http://localhost:%s" port))))))) (testing "throws a slingshot exception when the server returns valid but unexpected json" (with-test-logging (jetty9/with-test-webserver empty-response-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/unexpected-response] (check-for-update {:product-name "foo"} (format "http://localhost:%s" port))))))) (testing "throws a slingshot exception when the server returns a malformed body" (with-test-logging (jetty9/with-test-webserver malformed-body-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/unexpected-response] (check-for-update {:product-name "foo"} (format "http://localhost:%s" port)))))))) (deftest error-handling-telemetry (testing "throws a slingshot exception when there is a connection error" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/connection-error] (send-telemetry {:product-name "foo" :database-version "9.4"} "http://localhost:1")))))) (testing "throws a slingshot exception when an error is returned by the server" (with-test-logging (jetty9/with-test-webserver server-error-app port (is (thrown+? [:kind :puppetlabs.dujour.version-check/http-error-code] (send-telemetry {:product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port)))))))) (deftest test-check-for-updates (testing "returns a future that can be dereferenced" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [return-val (promise) callback-fn (fn [resp] (deliver return-val resp) "return string") future (check-for-updates! {:product-name "foo" :database-version "9.4"} (format "http://localhost:%s" port) callback-fn) result @future] (is (= "return string" result)) (is (= (:version @return-val) "9000.0.0"))))))) (deftest test-get-version-string (testing "get-version-string returns the correct version string" (with-test-logging (jetty9/with-test-webserver return-all-as-message-app port (let [version-string (get-version-string "trapperkeeper-webserver-jetty9" "puppetlabs")] (is (not (.isEmpty version-string))) (is (re-matches #"^\d+.\d+.\d+" version-string))))))) (deftest test-get-hash (testing "runs deterministically" (let [str (ks/uuid)] (is (= (get-hash str) (get-hash str))))))