├── util ├── bin │ └── pdb └── pdb │ ├── requirements.txt │ ├── samples │ ├── README.md │ └── pdb ├── ext ├── .gitignore ├── test-conf │ ├── pgver-default │ ├── puppet-ref-default │ └── puppetserver-ref-default ├── build_defaults.yaml ├── bin │ ├── sha256sum │ ├── run-rspec-tests │ ├── clj-1.11 │ ├── jdk-from-spec │ ├── symlink-relative-to │ ├── spec-includes │ ├── prefixed-ref-from-spec │ ├── flavor-from-spec │ ├── host-info │ ├── check-spec-env │ ├── contributors-in-git-log │ ├── run-external-tests │ ├── pdbbox-env │ ├── require-pgbox │ └── prep-debianish ├── project_data.yaml └── jenkins │ └── lein-test.sh ├── resources ├── ext │ ├── docs │ ├── config │ │ ├── bootstrap.cfg │ │ ├── conf.d │ │ │ ├── repl.ini │ │ │ ├── database.ini │ │ │ ├── config.ini │ │ │ ├── jetty.ini │ │ │ └── auth.conf │ │ └── request-logging.xml │ ├── cli │ │ ├── anonymize │ │ └── upgrade │ └── ezbake.conf ├── puppetlabs │ └── puppetdb │ │ ├── benchmark │ │ └── config.ini │ │ ├── generate │ │ └── realistic │ │ │ ├── README.md │ │ │ └── metadata.txt │ │ └── bootstrap.cfg └── logback.xml ├── Makefile ├── test-resources ├── puppetserver │ ├── ssl │ │ ├── ca │ │ │ ├── serial │ │ │ ├── inventory.txt │ │ │ ├── ca_pub.pem │ │ │ ├── ca_crl.pem │ │ │ └── signed │ │ │ │ └── localhost.pem │ │ ├── public_keys │ │ │ └── localhost.pem │ │ ├── crl.pem │ │ └── certs │ │ │ └── localhost.pem │ ├── rich_data_environment.conf │ ├── rich_data_puppet.conf │ ├── puppet.conf │ ├── request-logging-dev.xml │ ├── logback-dev.xml │ ├── bootstrap-7.x.cfg │ └── bootstrap.cfg ├── ca.srl ├── binary-template.erb ├── create-test-db.sh ├── extension.cnf ├── logback-test.xml ├── README.md ├── integration-bootstrap.cfg ├── puppetlabs │ └── puppetdb │ │ ├── ssl │ │ ├── public_keys │ │ │ └── localhost.pem │ │ ├── private_keys │ │ │ └── keyonly.pem │ │ └── certs │ │ │ ├── ca.pem │ │ │ └── localhost.pem │ │ └── cli │ │ └── export │ │ └── tiny-catalog.json ├── integration-puppetdb.conf ├── localhost.csr ├── localhost.pem └── ca.pem ├── documentation ├── release_notes.markdown ├── images │ ├── pdb_erd.png │ ├── perf-dash-large.png │ └── perf-dash-small.png ├── README.md ├── api │ ├── wire_format │ │ ├── facts_format_v4.markdown │ │ ├── facts_format_v5.markdown │ │ └── deactivate_node_format_v3.markdown │ ├── meta │ │ └── v1 │ │ │ ├── server-time.markdown │ │ │ └── version.markdown │ ├── query │ │ └── v4 │ │ │ └── fact-names.markdown │ └── admin │ │ └── v1 │ │ └── archive.markdown └── using.markdown ├── .gitattributes ├── puppet ├── spec │ ├── spec.opts │ ├── README.markdown │ ├── unit │ │ ├── indirector │ │ │ ├── facts │ │ │ │ └── puppetdb_apply_spec.rb │ │ │ └── node │ │ │ │ └── puppetdb_spec.rb │ │ ├── face │ │ │ └── node │ │ │ │ ├── deactivate_spec.rb │ │ │ │ └── status_spec.rb │ │ └── util │ │ │ └── puppetdb_spec.rb │ └── spec_helper.rb └── lib │ └── puppet │ ├── functions │ └── puppetdb_query.rb │ ├── util │ └── puppetdb │ │ ├── command_names.rb │ │ └── atom.rb │ ├── indirector │ ├── node │ │ └── puppetdb.rb │ └── facts │ │ └── puppetdb_apply.rb │ └── face │ └── node │ └── deactivate.rb ├── .dockerignore ├── .dir-locals.el ├── contrib ├── gem │ ├── Gemfile │ ├── README.md │ └── openvoxdb-terminus.gemspec └── README.md ├── dev-resources ├── time-shift-export │ └── input-archive │ │ ├── facts │ │ └── facts.json │ │ ├── catalogs │ │ └── catalog.json │ │ ├── export-metadata.json │ │ └── reports │ │ └── report.json └── suppression.xml ├── dev-docs ├── README.md └── run-pdb-on-macos.md ├── acceptance ├── setup │ ├── pre_suite │ │ ├── 15_prep_locales.rb │ │ ├── 00_setup_test_env.rb │ │ ├── 05_clear_firewalls.rb │ │ ├── 30_generate_ssl_certs.rb │ │ ├── 80_add_dev_repo.rb │ │ ├── 50_install_modules.rb │ │ ├── 70_install_released_puppetdb.rb │ │ ├── 20_install_puppet.rb │ │ ├── 75_clean_out_puppet5_repos.rb │ │ └── 90_install_devel_puppetdb.rb │ ├── post_suite │ │ ├── 10_collect_artifacts.rb │ │ └── 99_teardown.rb │ ├── openvox │ │ ├── configure_type_defaults.rb │ │ └── configure_openvoxdb.rb │ └── early │ │ └── 00_remove_previous_config.rb ├── options │ ├── common.rb │ ├── openvox.rb │ └── postgres.rb ├── config │ ├── vbox-debian7-64mda.cfg │ ├── ec2-west-el7-64mda-el7-64a.cfg │ └── vbox-debian7-64mda-2xdb.cfg └── tests │ ├── commands │ ├── list-sub-commands.rb │ ├── args-handling.rb │ └── delete_reports.rb │ ├── security │ └── puppetdb-ssl-setup │ │ ├── nonprod-environment.rb │ │ ├── jetty-changes.rb │ │ └── no-puppet-certs.rb │ └── smoke.rb ├── test └── puppetlabs │ └── puppetdb │ ├── test_protocols.clj │ ├── testutils │ ├── log.clj │ ├── dashboard.clj │ ├── parse_yaml.clj │ ├── facts.clj │ ├── mem.clj │ ├── nio.clj │ └── tar.clj │ ├── utils │ └── metrics_test.clj │ ├── integration │ ├── masterless.clj │ └── file_with_binary_template.clj │ ├── http │ ├── query_test.clj │ └── sync_version_checking.clj │ ├── cli │ ├── util_test.clj │ ├── pdb_dataset_test.clj │ └── time_shift_export_test.clj │ ├── test_import.clj │ ├── generative │ └── overrideable_generators_test.clj │ ├── meta_test.clj │ ├── query │ └── regression_test.clj │ ├── core_test.clj │ └── dashboard_test.clj ├── renovate.json ├── HISTORY.md ├── src └── puppetlabs │ ├── puppetdb │ ├── query │ │ ├── common.clj │ │ ├── edges.clj │ │ ├── catalog_inputs.clj │ │ ├── facts.clj │ │ ├── resources.clj │ │ └── fact_contents.clj │ ├── lint.clj │ ├── meta │ │ └── version.clj │ ├── constants.clj │ ├── package_util.clj │ ├── nio.clj │ ├── cli │ │ ├── tk_util.clj │ │ ├── version.clj │ │ └── util.clj │ ├── mq.clj │ ├── honeysql.clj │ ├── metrics │ │ └── core.clj │ ├── meta.clj │ ├── jdbc │ │ └── internal.clj │ ├── command │ │ └── constants.clj │ ├── factsets.clj │ ├── nodes.clj │ └── core.clj │ └── puppetdb.clj ├── locust ├── load-test │ └── example.yaml └── run-load-test ├── .github ├── dependabot.yml └── workflows │ ├── release.yml │ └── build.yml ├── locales └── eo.po ├── NOTICE.txt ├── Dockerfile ├── .mailmap ├── README.md ├── config.sample.ini ├── config └── image_templates │ └── ec2.yaml ├── .gitignore ├── tasks ├── upload.rake └── tag.rake ├── ci └── bin │ └── prep-and-run-in └── .clj-kondo └── config.edn /util/bin/pdb: -------------------------------------------------------------------------------- 1 | ../pdb/pdb -------------------------------------------------------------------------------- /ext/.gitignore: -------------------------------------------------------------------------------- 1 | files/* 2 | -------------------------------------------------------------------------------- /ext/test-conf/pgver-default: -------------------------------------------------------------------------------- 1 | 11 2 | -------------------------------------------------------------------------------- /resources/ext/docs: -------------------------------------------------------------------------------- 1 | ../../documentation -------------------------------------------------------------------------------- /ext/test-conf/puppet-ref-default: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include dev-resources/Makefile.i18n 2 | -------------------------------------------------------------------------------- /ext/test-conf/puppetserver-ref-default: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/ca/serial: -------------------------------------------------------------------------------- 1 | 002 2 | -------------------------------------------------------------------------------- /documentation/release_notes.markdown: -------------------------------------------------------------------------------- 1 | release_notes_8.markdown -------------------------------------------------------------------------------- /util/pdb/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | ijson 3 | grequests 4 | -------------------------------------------------------------------------------- /test-resources/ca.srl: -------------------------------------------------------------------------------- 1 | 17E3F8768B3DF060A9505C2E5D73E1A75108931A 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docker/** text eol=lf 2 | resources/** text eol=lf 3 | -------------------------------------------------------------------------------- /resources/ext/config/bootstrap.cfg: -------------------------------------------------------------------------------- 1 | ../../puppetlabs/puppetdb/bootstrap.cfg -------------------------------------------------------------------------------- /util/pdb/samples: -------------------------------------------------------------------------------- 1 | ../../resources/puppetlabs/puppetdb/benchmark/samples -------------------------------------------------------------------------------- /test-resources/puppetserver/rich_data_environment.conf: -------------------------------------------------------------------------------- 1 | rich_data = true 2 | -------------------------------------------------------------------------------- /test-resources/puppetserver/rich_data_puppet.conf: -------------------------------------------------------------------------------- 1 | [main] 2 | rich_data = true 3 | -------------------------------------------------------------------------------- /puppet/spec/spec.opts: -------------------------------------------------------------------------------- 1 | --format 2 | s 3 | --colour 4 | --loadby 5 | mtime 6 | --backtrace 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !.git 3 | !project.clj 4 | !src 5 | !resources 6 | !documentation 7 | !docker 8 | !test 9 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((clojure-mode 2 | (clojure-docstring-fill-column . 79) 3 | (eval . (put-clojure-indent 'try! 0)))) 4 | -------------------------------------------------------------------------------- /ext/build_defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | project: 'puppetdb' 3 | repo_name: 'puppet8' 4 | nonfinal_repo_name: 'puppet8-nightly' 5 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/ca/inventory.txt: -------------------------------------------------------------------------------- 1 | 0x0001 2022-02-08T22:49:12UTC 2037-02-05T22:49:14UTC /CN=localhost 2 | -------------------------------------------------------------------------------- /documentation/images/pdb_erd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenVoxProject/openvoxdb/HEAD/documentation/images/pdb_erd.png -------------------------------------------------------------------------------- /test-resources/binary-template.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenVoxProject/openvoxdb/HEAD/test-resources/binary-template.erb -------------------------------------------------------------------------------- /contrib/gem/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in puppetdb-terminus.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /documentation/images/perf-dash-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenVoxProject/openvoxdb/HEAD/documentation/images/perf-dash-large.png -------------------------------------------------------------------------------- /documentation/images/perf-dash-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenVoxProject/openvoxdb/HEAD/documentation/images/perf-dash-small.png -------------------------------------------------------------------------------- /resources/puppetlabs/puppetdb/benchmark/config.ini: -------------------------------------------------------------------------------- 1 | [global] 2 | logging-config = resources/logback.xml 3 | 4 | [jetty] 5 | host = 0.0.0.0 6 | port = 8080 7 | -------------------------------------------------------------------------------- /dev-resources/time-shift-export/input-archive/facts/facts.json: -------------------------------------------------------------------------------- 1 | { 2 | "certname" : "host-8", 3 | "producer_timestamp" : "2021-05-13T11:11:00.000Z" 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/time-shift-export/input-archive/catalogs/catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "certname" : "host-8", 3 | "producer_timestamp" : "2021-05-13T11:10:00.000Z" 4 | } 5 | -------------------------------------------------------------------------------- /resources/ext/cli/anonymize: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ${JAVA_BIN} ${JAVA_ARGS} -cp ${INSTALL_DIR}/puppetdb.jar clojure.main -m puppetlabs.puppetdb.core anonymize "$@" 4 | -------------------------------------------------------------------------------- /dev-docs/README.md: -------------------------------------------------------------------------------- 1 | # Developer Documentation 2 | 3 | ## Table of Contents 4 | 5 | 1. Releases 6 | * [Reconciling Git commits and Jira Tickets](./release/reconciliation.md) 7 | -------------------------------------------------------------------------------- /test-resources/create-test-db.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | createuser -DRSP puppetdb 3 | createuser -dRP --superuser pdb_test_admin 4 | createuser -DRSP pdb_test 5 | createdb -E UTF8 -O puppetdb puppetdb_test 6 | -------------------------------------------------------------------------------- /test-resources/extension.cnf: -------------------------------------------------------------------------------- 1 | [CA_extensions] 2 | basicConstraints = critical,CA:TRUE 3 | nsComment = "Puppet Ruby/OpenSSL Internal Certificate" 4 | keyUsage = critical,keyCertSign,cRLSign 5 | subjectKeyIdentifier = hash 6 | -------------------------------------------------------------------------------- /resources/ext/config/conf.d/repl.ini: -------------------------------------------------------------------------------- 1 | [nrepl] 2 | # Set to true to enable the remote REPL 3 | enabled = false 4 | 5 | # What port the REPL should listen on 6 | port = 8082 7 | 8 | # IP address to listen on 9 | host = 127.0.0.1 10 | -------------------------------------------------------------------------------- /test-resources/puppetserver/puppet.conf: -------------------------------------------------------------------------------- 1 | [main] 2 | certname = localhost 3 | 4 | [agent] 5 | server = localhost 6 | 7 | [master] 8 | storeconfigs = true 9 | storeconfigs_backend = puppetdb 10 | reports = puppetdb 11 | autosign = true 12 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/15_prep_locales.rb: -------------------------------------------------------------------------------- 1 | test_name 'Ensure en_US.UTF-8 locale is present on Debian for openvoxdb migrations' do 2 | confine :to, :platform => /^debian/ 3 | on(master, puppet_resource('package', 'locales-all', 'ensure=present')) 4 | end 5 | -------------------------------------------------------------------------------- /puppet/spec/README.markdown: -------------------------------------------------------------------------------- 1 | NOTE 2 | ==== 3 | 4 | This project's specs depend on puppet core, and thus they require the 5 | puppetlabs_spec_helper project. For more information please see the 6 | README in that project, which can be found here: 7 | 8 | https://github.com/puppetlabs/puppetlabs_spec_helper -------------------------------------------------------------------------------- /resources/puppetlabs/puppetdb/generate/realistic/README.md: -------------------------------------------------------------------------------- 1 | # Generated data 2 | 3 | This data is a best guess at what a realistic size/shape of data might be in 4 | PuppetDB to allow us to do more accurate performance testing. It was generated 5 | using the configuration values found in `metadata.txt`. 6 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/test_protocols.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.test-protocols 2 | (:require [clojure.test :refer :all])) 3 | 4 | ;; A protocol to be reified alongside IFn; after the function has been 5 | ;; called once, called? should return true. 6 | (defprotocol IMockFn 7 | (called? [this])) 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "addLabels": ["renovate", "dependencies"], 4 | "assigneesFromCodeOwners": true, 5 | "automerge": true, 6 | "automergeType": "pr", 7 | "dependencyDashboard": true, 8 | "enabledManagers": ["leiningen"] 9 | } 10 | -------------------------------------------------------------------------------- /acceptance/options/common.rb: -------------------------------------------------------------------------------- 1 | def common_options_hash() 2 | dir = File.expand_path(File.join(File.dirname(__FILE__), '..')) 3 | 4 | { 5 | :helper => [File.join(dir, 'helper.rb')], 6 | :pre_suite => [File.join(dir, 'setup', 'early'), 7 | File.join(dir, 'setup', 'pre_suite')] 8 | } 9 | end 10 | -------------------------------------------------------------------------------- /dev-resources/time-shift-export/input-archive/export-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp" : "2021-04-15T13:23:40.218Z", 3 | "command_versions" : { 4 | "replace_catalog" : 9, 5 | "store_report" : 8, 6 | "replace_facts" : 5, 7 | "configure_expiration" : 1, 8 | "replace_catalog_inputs" : 1 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /acceptance/setup/post_suite/10_collect_artifacts.rb: -------------------------------------------------------------------------------- 1 | 2 | step "Create artifacts directory" do 3 | unless File.directory?('artifacts') 4 | Dir.mkdir("artifacts") 5 | end 6 | end 7 | step "Collect puppetdb log file" do 8 | 9 | scp_from(database, "/var/log/puppetlabs/puppetdb/puppetdb.log", "./artifacts") 10 | 11 | end 12 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/00_setup_test_env.rb: -------------------------------------------------------------------------------- 1 | os_families = {} 2 | 3 | step "Determine host OS's" do 4 | os_families = hosts.inject({}) do |result, host| 5 | result[host.name] = get_os_family(host) 6 | result 7 | end 8 | end 9 | 10 | PuppetDBExtensions.initialize_test_config(options, 11 | os_families) 12 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## 8.9.1 2 | 3 | * Add obsoletes/replaces/conflicts metadata with puppetdb and puppetdb-termini on the openvoxdb and openvoxdb-termini packages. 4 | 5 | ## 8.9.0 6 | 7 | * Initial openvoxdb release. Based on puppetdb 8.8.1. Supported on all platforms that puppetdb currently supports, but for all architectures rather than just x86_64. 8 | -------------------------------------------------------------------------------- /puppet/lib/puppet/functions/puppetdb_query.rb: -------------------------------------------------------------------------------- 1 | require 'puppet/util/puppetdb' 2 | Puppet::Functions.create_function(:puppetdb_query) do 3 | dispatch :puppetdb_query do 4 | required_param 'Variant[String[1], Array[Data, 1]]', :query 5 | end 6 | 7 | def puppetdb_query(query) 8 | Puppet::Util::Puppetdb.query_puppetdb(query) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /resources/ext/config/conf.d/database.ini: -------------------------------------------------------------------------------- 1 | [database] 2 | 3 | # The database address, i.e. //HOST:PORT/DATABASE_NAME 4 | # subname = //localhost:5432/puppetdb 5 | 6 | # Connect as a specific user 7 | # username = foobar 8 | 9 | # Use a specific password 10 | # password = foobar 11 | 12 | # How often (in minutes) to compact the database 13 | # gc-interval = 60 14 | -------------------------------------------------------------------------------- /ext/bin/sha256sum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Outputs sha256 checksum of STDIN 4 | # Works on Linux and MacOS 5 | 6 | set -x 7 | echo "$@" 1>&2 8 | 9 | if test "$#" != 0; then 10 | echo "Usage: sha256sum < PATH" 1>&2 11 | exit 2 12 | fi 13 | 14 | case "$OSTYPE" in 15 | darwin*) exec shasum -a 256 "$@" ;; 16 | *) exec sha256sum "$@" ;; 17 | esac 18 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/log.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.log 2 | (:import 3 | [ch.qos.logback.classic Level])) 4 | 5 | ;;The below functions are useful with 6 | ;;puppetlabs.trapperkeeper.testutils.logging/with-log-suppressed-unless-notable 7 | 8 | (defn notable-pdb-event? [event] 9 | (.isGreaterOrEqual (.getLevel event) Level/ERROR)) 10 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/query/common.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.common) 2 | 3 | ;; A macro so that if nothing else clojure.test ERROR message 4 | ;; file/line numbers will be right (only reports first stack entry). 5 | (defmacro bad-query-ex [msg] 6 | `(ex-info ~msg {:kind :puppetlabs.puppetdb.query/invalid 7 | :puppetlabs.puppetdb/known-error? true})) 8 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/05_clear_firewalls.rb: -------------------------------------------------------------------------------- 1 | unless (test_config[:skip_presuite_provisioning]) 2 | step "Flushing iptables chains" do 3 | hosts.each do |host| 4 | on host, 'apt-get install -y iptables' if (is_bullseye || is_bookworm) 5 | on host, "iptables -F INPUT -t filter" 6 | on host, "iptables -F FORWARD -t filter" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /puppet/lib/puppet/util/puppetdb/command_names.rb: -------------------------------------------------------------------------------- 1 | module Puppet::Util::Puppetdb 2 | module CommandNames 3 | CommandReplaceCatalog = "replace catalog" 4 | CommandReplaceFacts = "replace facts" 5 | CommandDeactivateNode = "deactivate node" 6 | CommandStoreReport = "store report" 7 | CommandReplaceCatalogInputs = "replace catalog inputs" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/lint.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.lint) 2 | 3 | (defmacro ignore-value 4 | "Suppress eastwood's unused-ret-vals warning: 5 | https://github.com/jonase/eastwood#unused-ret-vals. See 6 | ./eastwood.clj for the suppression that makes this work." 7 | [x] 8 | ;; Use the dummy let to avoid https://github.com/jonase/eastwood/issues/355 9 | `(let [x# ~x] 10 | nil)) 11 | -------------------------------------------------------------------------------- /acceptance/options/openvox.rb: -------------------------------------------------------------------------------- 1 | # Set the option vars provided by acceptance/options/postgres.rb 2 | # without tying in the hardcoded pre-suite and test paths set 3 | # in acceptance/options/common.rb 4 | { 5 | :is_puppetserver => 'true', 6 | :'use-service' => 'true', 7 | :'puppetserver-confdir' => '/etc/puppetlabs/puppetserver/conf.d', 8 | :puppetservice => 'puppetserver', 9 | } 10 | -------------------------------------------------------------------------------- /test-resources/puppetserver/request-logging-dev.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./target/logs/puppetserver-access.log 4 | 5 | %h %l %u %user %date "%r" %s %b %h %a %localPort %D 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /acceptance/config/vbox-debian7-64mda.cfg: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-7-amd64.vm: 3 | roles: 4 | - master 5 | - agent 6 | - dashboard 7 | - database 8 | platform: debian-7-amd64 9 | hypervisor: vagrant 10 | box: debian-70rc1-x64-vbox4210-nocm 11 | box_url: http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box 12 | 13 | CONFIG: 14 | nfs_server: none 15 | consoleport: 443 16 | -------------------------------------------------------------------------------- /acceptance/setup/openvox/configure_type_defaults.rb: -------------------------------------------------------------------------------- 1 | # Ensures that the /root/.ssh/environment file is 2 | # set up with a path that includes /opt/puppetlabs/bin. 3 | # Normally this would be called as part of beaker-puppet 4 | # install steps, but we are installing openvox packages outside 5 | # of beaker-puppet, since it doesn't handle openvox. 6 | test_name('configure root ssh environment path') do 7 | configure_type_defaults_on(agents) 8 | end 9 | -------------------------------------------------------------------------------- /acceptance/tests/commands/list-sub-commands.rb: -------------------------------------------------------------------------------- 1 | test_name "list sub-commands" do 2 | bin_loc = puppetdb_bin_dir(database) 3 | 4 | step "running puppetdb on its own should list all sub-commands" do 5 | result = on database, "#{bin_loc}/puppetdb" 6 | ["delete-reports", "ssl-setup", "foreground"].each do |k| 7 | assert_match(/#{k}/, result.stdout, "puppetdb command should list #{k} as a sub-command") 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /puppet/lib/puppet/util/puppetdb/atom.rb: -------------------------------------------------------------------------------- 1 | require 'thread' 2 | 3 | module Puppet::Util::Puppetdb 4 | class Atom 5 | def initialize(value) 6 | @value = value 7 | @mutex = Mutex.new 8 | end 9 | 10 | def deref() 11 | @mutex.synchronize { 12 | @value 13 | } 14 | end 15 | 16 | def reset(value) 17 | @mutex.synchronize { 18 | @value = value 19 | } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/30_generate_ssl_certs.rb: -------------------------------------------------------------------------------- 1 | unless (test_config[:skip_presuite_provisioning]) 2 | step "Run an agent to create the SSL certs" do 3 | on( master, "chown -R puppet:puppet /opt/puppetlabs/puppet/cache") 4 | databases.each do |database| 5 | with_puppet_running_on(master, {'master' => {'autosign' => 'true', 'trace' => 'true'}}) do 6 | run_agent_on(database, "--test") 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /contrib/gem/README.md: -------------------------------------------------------------------------------- 1 | # PuppetDB Terminus Gemspec 2 | 3 | The files in this directory are intended to provide a starting point for 4 | building a gem package of the puppetdb terminus code. This will be useful 5 | for cases where puppet is being installed as a gem and you would also like to 6 | include puppetdb. 7 | 8 | Note that support for installing the puppetdb termini via rubygems relies on 9 | changes to the puppet autoloader that were first introduced in the 3.0 series. -------------------------------------------------------------------------------- /acceptance/setup/early/00_remove_previous_config.rb: -------------------------------------------------------------------------------- 1 | 2 | unless (options[:vmrun]) or (ENV['PUPPETDB_SKIP_PRESUITE_PROVISIONING']) 3 | step "Clean up configuration files on master" do 4 | on master, "rm -rf /etc/puppet/routes.yaml" 5 | end 6 | 7 | step "Clean up configuration files on puppetdb server" do 8 | on databases, "rm -rf /etc/puppetdb/ssl" 9 | end 10 | 11 | step "Remove old modules from master" do 12 | on master, "rm -rf /etc/puppet/modules/*" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test-resources/puppetserver/logback-dev.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d %-5p [%t] [%c{2}] %m%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d %-5p [%thread] [%c{2}] %m%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/meta/version.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.meta.version 2 | "Versioning Utility Library 3 | 4 | This namespace contains some utility functions relating to checking version 5 | numbers of various fun things." 6 | (:require [trptcolin.versioneer.core :as version])) 7 | 8 | ;; ### PuppetDB current version 9 | 10 | (defn version 11 | "Get the version number of this PuppetDB installation." 12 | [] 13 | {:post [(string? %)]} 14 | (version/get-version "puppetlabs" "puppetdb")) 15 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/80_add_dev_repo.rb: -------------------------------------------------------------------------------- 1 | repo_config_dir = 'tmp/repo_configs' 2 | 3 | if (test_config[:install_type] == :package) \ 4 | && test_config[:package_build_version] \ 5 | && !(test_config[:skip_presuite_provisioning]) 6 | then 7 | # do not install the dev_repo if a package_build_version has not been specified. 8 | databases.each do |database| 9 | install_puppetlabs_dev_repo database, 'puppetdb', test_config[:package_build_version], 10 | repo_config_dir, options 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /util/pdb/README.md: -------------------------------------------------------------------------------- 1 | ## Next generation developer utilities for PDB 2 | 3 | A python CLI utility for 4 | 5 | * load simulation (currently in a hacky state, only supporting facts) 6 | * querying (no streaming, possibly broken) 7 | 8 | ### Setup 9 | 10 | * Install virtualenv and python 3 11 | apt-get install virtualenv 12 | 13 | * Set up virtualenv 14 | virtualenv -p python3 venv 15 | source venv/bin/activate 16 | pip install -r requirements.txt 17 | 18 | * Run the tool 19 | ./pdb simulate 100000 30 20 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/constants.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.constants) 2 | 3 | (def filename-forbidden-characters 4 | [\/ \u0000 \\ \:]) 5 | 6 | (def sha1-hex-length 7 | "The length (in ASCII/UTF-8 bytes) of a SHA1 sum when encoded in hexadecimal." 8 | 40) 9 | 10 | ;; Use to signal when a change isn't backwards compatible with pdbext ha-sync. 11 | ;; Bumping this version number will cause pdbext sync requests to fail with 409s 12 | ;; until both pdbs are upgraded and on the same sync version. 13 | (def pdb-sync-ver 2) 14 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/50_install_modules.rb: -------------------------------------------------------------------------------- 1 | require 'beaker/dsl/install_utils' 2 | 3 | extend Beaker::DSL::InstallUtils 4 | 5 | unless (test_config[:skip_presuite_provisioning]) 6 | step "Install the puppetdb module and dependencies" do 7 | install_puppetdb_module(databases, puppet_repo_version(test_config[:platform_version], 8 | test_config[:install_mode], 9 | test_config[:nightly])) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/dashboard.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.dashboard) 2 | 3 | (defn dashboard-base-url->str 4 | "Similar to puppetlabs.puppetdb.utils/base-url->str but doesn't 5 | include a version as the dashboard page does not include a version" 6 | [{:keys [protocol host port prefix] :as _base-url}] 7 | (-> (java.net.URL. protocol host port prefix) 8 | .toURI .toASCIIString)) 9 | 10 | (defn dashboard-page? [{:keys [body] :as _req}] 11 | (.contains body "OpenvoxDB: Dashboard")) 12 | -------------------------------------------------------------------------------- /ext/project_data.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | project: 'puppetdb' 3 | author: 'Puppet Labs' 4 | email: 'info@puppetlabs.com' 5 | homepage: 'https://github.com/puppetlabs/puppetdb' 6 | summary: 'Puppet Centralized Storage Daemon' 7 | description: 'Puppet Centralized Storage' 8 | version_file: 'version' 9 | # files and gem_files are space separated lists 10 | files: 'ext *.md puppetb.jar version documentation Rakefile' 11 | tar_excludes: '.gitignore' 12 | gem_files: 13 | gem_require_path: 14 | gem_test_files: 15 | gem_executables: 16 | gem_default_executables: 17 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/package_util.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.package-util 2 | (:require [schema.core :as s])) 3 | 4 | ;;; small utilities for manipulating package data; in a separate file to untangle 5 | ;;; a dependency loop. 6 | 7 | (def package-tuple 8 | [(s/one s/Str "package_name") 9 | (s/one s/Str "version") 10 | (s/one s/Str "provider")]) 11 | 12 | (def hashed-package-tuple 13 | (conj package-tuple 14 | (s/one s/Str "package_hash"))) 15 | 16 | (defn package-tuple-hash [hashed-package-tuple] 17 | (nth hashed-package-tuple 3)) 18 | -------------------------------------------------------------------------------- /resources/ext/config/conf.d/config.ini: -------------------------------------------------------------------------------- 1 | # See README.md for more thorough explanations of each section and 2 | # option. 3 | 4 | [global] 5 | # Store mq/db data in a custom directory 6 | vardir = /opt/puppetlabs/server/data/puppetdb 7 | 8 | # Use an external logback config file 9 | logging-config = /etc/puppetlabs/puppetdb/logback.xml 10 | 11 | [command-processing] 12 | # How many command-processing threads to use, defaults to (CPUs / 2) 13 | # threads = 4 14 | 15 | # How many threads can write to disk at once, defaults to min(CPUs / 2, 4) 16 | # concurrent-writes = 4 17 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/nio.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.nio 2 | (:import 3 | [java.nio.file CopyOption Path OpenOption Paths StandardCopyOption])) 4 | 5 | (def copt-atomic StandardCopyOption/ATOMIC_MOVE) 6 | (def copt-replace StandardCopyOption/REPLACE_EXISTING) 7 | 8 | (defn copts ^"[Ljava.nio.file.CopyOption;" [opts] 9 | (into-array CopyOption opts)) 10 | 11 | (defn oopts ^"[Ljava.nio.file.OpenOption;" [opts] 12 | (into-array OpenOption opts)) 13 | 14 | (defn get-path ^Path [^String s & more-strings] 15 | (Paths/get s (into-array String more-strings))) 16 | -------------------------------------------------------------------------------- /test-resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d %-5p [%thread] [%c{2}] %m%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /util/pdb/pdb: -------------------------------------------------------------------------------- 1 | """": # -*-python-*- 2 | command -v python3 > /dev/null && exec python3 "$0" "$@" 3 | command -v python2 > /dev/null && exec python2 "$0" "$@" 4 | echo "error: unable to find python3 or python2" 1>&2; exit 2 5 | """ 6 | 7 | import sys 8 | from puppetdb import PuppetDB 9 | 10 | if __name__ == '__main__': 11 | pdb = PuppetDB() 12 | subcommand = sys.argv[1] 13 | if subcommand == 'simulate': 14 | nhosts, interval = sys.argv[2:] 15 | pdb.simulate(int(nhosts), int(interval)) 16 | if subcommand == 'query': 17 | pdb.query(sys.argv[2]) 18 | -------------------------------------------------------------------------------- /acceptance/options/postgres.rb: -------------------------------------------------------------------------------- 1 | # We are eval'd in the scope of the acceptance framework's option-parsing 2 | # code, so we can't use __FILE__ to find our location. We have access to 3 | # a variable 'options_file_path', though. 4 | require File.expand_path(File.join(File.dirname(options_file_path), 'common.rb')) 5 | 6 | common_options_hash.tap do |my_hash| 7 | my_hash[:is_puppetserver] = 'true' 8 | my_hash[:'use-service'] = 'true' 9 | my_hash[:'puppetserver-confdir'] = '/etc/puppetlabs/puppetserver/conf.d' 10 | my_hash[:puppetservice] = 'puppetserver' 11 | end 12 | -------------------------------------------------------------------------------- /locust/load-test/example.yaml: -------------------------------------------------------------------------------- 1 | # Example 2 | # name: my custom call name, will be shown in the report 3 | # path: path without the host. The host is set in cli as an option 4 | # query: query should be hash for POST and string for GET 5 | # method: GET/POST 6 | # headers: any custom headers that will be sent with the request 7 | --- 8 | - path: "/pdb/query/v4/facts" 9 | limit: 100 10 | offset: 1 11 | method: GET 12 | headers: '' 13 | - path: "/pdb/query/v4/facts" 14 | query: ["~", "certname", "host"] 15 | method: POST 16 | headers: {content-type: application/json} 17 | alias: example 2 18 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/utils/metrics_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.utils.metrics-test 2 | (:require [puppetlabs.puppetdb.utils.metrics :as mutils] 3 | [clojure.test :refer :all] 4 | [metrics.timers :refer [timer]])) 5 | 6 | (deftest test-multitime-macro 7 | (testing "should update all supplied timers" 8 | (let [timers (mapv timer ["t1" "t2" "t3"])] 9 | (doseq [t timers] 10 | (is (zero? (mutils/number-recorded t)))) 11 | (is (= 6 (mutils/multitime! timers (+ 1 2 3)))) 12 | (doseq [t timers] 13 | (is (= 1 (mutils/number-recorded t))))))) 14 | -------------------------------------------------------------------------------- /puppet/lib/puppet/indirector/node/puppetdb.rb: -------------------------------------------------------------------------------- 1 | require 'puppet/node' 2 | require 'puppet/indirector/rest' 3 | require 'puppet/util/puppetdb' 4 | 5 | class Puppet::Node::Puppetdb < Puppet::Indirector::REST 6 | include Puppet::Util::Puppetdb 7 | 8 | def find(request) 9 | end 10 | 11 | def save(request) 12 | end 13 | 14 | def destroy(request) 15 | current_time = Time.now 16 | 17 | submit_command(request.key, CommandDeactivateNode, 3, current_time.clone.utc) do 18 | {:certname => request.key, 19 | :producer_timestamp => Puppet::Util::Puppetdb.to_wire_time(current_time)} 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | # raise PRs for gem updates 5 | - package-ecosystem: bundler 6 | directory: "/" 7 | schedule: 8 | interval: daily 9 | time: "13:00" 10 | open-pull-requests-limit: 10 11 | 12 | # Maintain dependencies for GitHub Actions 13 | - package-ecosystem: github-actions 14 | directory: "/" 15 | schedule: 16 | interval: daily 17 | time: "13:00" 18 | open-pull-requests-limit: 10 19 | 20 | - package-ecosystem: "docker" 21 | directory: "/" 22 | schedule: 23 | interval: "daily" 24 | time: "13:00" 25 | open-pull-requests-limit: 10 26 | -------------------------------------------------------------------------------- /acceptance/config/ec2-west-el7-64mda-el7-64a.cfg: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | el7-64-1: 3 | roles: 4 | - master 5 | - database 6 | - dashboard 7 | - agent 8 | vmname: el-7-x86_64-west 9 | platform: el-7-x86_64 10 | amisize: c3.large 11 | hypervisor: ec2 12 | snapshot: foss 13 | el7-64-2: 14 | roles: 15 | - agent 16 | vmname: el-7-x86_64-west 17 | platform: el-7-x86_64 18 | amisize: c3.large 19 | hypervisor: ec2 20 | snapshot: foss 21 | CONFIG: 22 | nfs_server: none 23 | consoleport: 443 24 | vpc_id: vpc-3cb28658 25 | subnet_ids: 26 | - subnet-8990e3ff 27 | - subnet-11d78c75 28 | - subnet-afaa18f7 29 | -------------------------------------------------------------------------------- /acceptance/tests/commands/args-handling.rb: -------------------------------------------------------------------------------- 1 | test_name "argument handling" do 2 | bin_loc = puppetdb_bin_dir(database) 3 | 4 | ["ssl-setup"].each do |k| 5 | step "running puppetdb #{k} -h should not error out" do 6 | on database, "#{bin_loc}/puppetdb #{k} -h" 7 | end 8 | 9 | # We don't test ssl-setup here, because its actions without any arguments 10 | # are tested elsewhere. 11 | next if k == 'ssl-setup' 12 | step "running puppetdb-#{k} with no arguments should not error out" do 13 | on database, "#{bin_loc}/puppetdb #{k}", :acceptable_exit_codes => [1] 14 | assert_match(/Missing required argument/, stdout) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /acceptance/config/vbox-debian7-64mda-2xdb.cfg: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-7-amd64-1: 3 | roles: 4 | - master 5 | - agent 6 | - dashboard 7 | - database 8 | platform: debian-7-amd64 9 | hypervisor: vagrant 10 | box: debian-70rc1-x64-vbox4210-nocm 11 | box_url: http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box 12 | debian-7-amd64-2: 13 | roles: 14 | - database 15 | platform: debian-7-amd64 16 | hypervisor: vagrant 17 | box: debian-70rc1-x64-vbox4210-nocm 18 | box_url: http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box 19 | CONFIG: 20 | nfs_server: none 21 | consoleport: 443 22 | -------------------------------------------------------------------------------- /test-resources/README.md: -------------------------------------------------------------------------------- 1 | # Cetificate setup 2 | 3 | some of the tests work with certificates. 4 | They can expire after some years. 5 | To regenerate them: 6 | 7 | ``` 8 | openssl genrsa -out ca_key.pem 4096 9 | openssl req -x509 -key ca_key.pem -out ca_crt.pem -days 3650 -subj "/CN=Puppet CA: localhost" -config extension.cnf -extensions CA_extensions 10 | openssl genrsa -out localhost.key 4096 11 | openssl req -new -key localhost.key -out localhost.csr -subj "/CN=localhost" 12 | openssl x509 -req -in localhost.csr -CA ca.pem -CAkey ca_key.pem -CAcreateserial -out localhost.pem -days 825 -sha256 -extfile <(printf "[v3_req]\nsubjectAltName=DNS:localhost,DNS:puppet") -extensions v3_req 13 | ``` 14 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | 4 | on: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | jobs: 10 | release: 11 | name: Release 12 | runs-on: ubuntu-24.04 13 | # Optional but recommended to use a specific environment 14 | environment: release 15 | # Prevent releases from forked repositories 16 | if: github.repository_owner == 'OpenVoxProject' 17 | 18 | permissions: 19 | contents: write 20 | 21 | steps: 22 | - name: Create Release Page 23 | shell: bash 24 | env: 25 | GH_TOKEN: ${{ github.token }} 26 | run: gh release create --repo ${{ github.repository }} ${{ github.ref_name }} --generate-notes 27 | -------------------------------------------------------------------------------- /dev-resources/suppression.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | ^pkg:maven/org\.clojure/core\.cache@.*$ 11 | CVE-2020-36448 12 | 13 | 14 | -------------------------------------------------------------------------------- /locales/eo.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR Puppet 3 | # This file is distributed under the same license as the puppetlabs.puppetdb package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: puppetlabs.puppetdb\n" 9 | "Report-Msgid-Bugs-To: docs@puppet.com\n" 10 | "POT-Creation-Date: \n" 11 | "PO-Revision-Date: 2017-03-22 14:56-0600\n" 12 | "Language-Team: \n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "X-Generator: Poedit 1.8.12\n" 17 | "Last-Translator: \n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "Language: eo\n" 20 | 21 | -------------------------------------------------------------------------------- /ext/bin/run-rspec-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run Ruby rspec tests on Ruby code in puppet/ directory 4 | # Requires single argument, a Puppet version number like 6.27.0 5 | 6 | set -uxeo pipefail 7 | 8 | # Validate argument 9 | test "$#" -eq 1 10 | puppet_ref="$1" 11 | 12 | # Log Ruby version 13 | ruby -v 14 | # Save Puppet version as local test config value 15 | ext/bin/test-config --set puppet-ref "$puppet_ref" 16 | # The puppet directory contains the PuppetDB related Ruby files packaged with 17 | # Puppet as well as the rspec test files that test them 18 | cd puppet 19 | # Install Ruby dependencies from top-level Gemfile via bundle 20 | bundle install --retry=10 21 | # Start rspec tests 22 | bundle exec rspec spec 23 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | # PuppetDB User-Contributed Content 2 | 3 | This directory contains code and other content that has been contributed by 4 | PuppetDB users. It's a place where we can include items that we know will be 5 | useful to other users, but that we can't yet officially maintain or support. 6 | We hope that you will find it valuable! 7 | 8 | Please keep in mind that the contents of this directory may not be exhaustively 9 | tested and could conceivably become out-of-sync with the main production code base. 10 | If you find something that is out of date or has any other issues, or if you have 11 | other content that you think may be useful to the community, we welcome additional 12 | submissions via github pull requests! 13 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | PuppetDB - Centralized Puppet Storage 2 | Copyright 2011-2016 Puppet Inc 3 | 4 | This product includes software developed at Puppet Inc (http://puppet.com/). 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this software except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | -------------------------------------------------------------------------------- /puppet/spec/unit/indirector/facts/puppetdb_apply_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'puppet/indirector/facts/puppetdb_apply' 3 | 4 | describe Puppet::Node::Facts::PuppetdbApply do 5 | describe "#save" do 6 | let(:facts) { Puppet::Node::Facts.new('foo') } 7 | 8 | def save 9 | subject.save(Puppet::Node::Facts.indirection.request(:save, facts.name, facts)) 10 | end 11 | 12 | it "should only submit commands once" do 13 | subject.expects(:submit_command).once 14 | save 15 | save 16 | save 17 | end 18 | 19 | end 20 | 21 | describe "#find" do 22 | it "always returns nil to force the cache to be skipped" do 23 | subject.find('foo').should be_nil 24 | end 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /test-resources/integration-bootstrap.cfg: -------------------------------------------------------------------------------- 1 | puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service 2 | puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service 3 | puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-webservice 4 | puppetlabs.trapperkeeper.services.status.status-service/status-service 5 | puppetlabs.trapperkeeper.services.scheduler.scheduler-service/scheduler-service 6 | puppetlabs.puppetdb.cli.services/puppetdb-service 7 | puppetlabs.puppetdb.command/command-service 8 | puppetlabs.puppetdb.pdb-routing/maint-mode-service 9 | puppetlabs.puppetdb.pdb-routing/pdb-routing-service 10 | puppetlabs.puppetdb.config/config-service 11 | puppetlabs.puppetdb.dashboard/dashboard-redirect-service 12 | -------------------------------------------------------------------------------- /acceptance/tests/security/puppetdb-ssl-setup/nonprod-environment.rb: -------------------------------------------------------------------------------- 1 | test_name "puppetdb ssl-setup on nonproduction environment" do 2 | confdir = on(database, "puppet config print confdir --section master").stdout.chomp 3 | bin_loc = "#{puppetdb_bin_dir(database)}" 4 | step "back up existing puppet.conf" do 5 | on database, "cp #{confdir}/puppet.conf #{confdir}/puppet.conf.bak" 6 | end 7 | step "ensure proper exit code on ssl-setup" do 8 | on database, "puppet config set environment foo --section main" 9 | result = on database, "#{bin_loc}/puppetdb ssl-setup", :acceptable_exit_codes => [0] 10 | end 11 | step "restore original puppet.conf" do 12 | on database, "mv #{confdir}/puppet.conf.bak #{confdir}/puppet.conf" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb) 2 | 3 | ;;; Notable namespace keys 4 | 5 | ;; :puppetlabs.puppetdb.known-error? - indicates that the error is 6 | ;; expected, e.g. a query or command syntax error, JSON parse error, 7 | ;; configuration error, etc. In general, the stack trace should be 8 | ;; considered uninteresting (i.e. only the message is expected to be 9 | ;; interesting), but if there's a cause (.getCause) or suppressed 10 | ;; exceptions (.getSuppressed) then they may also be interesting (in 11 | ;; their entirety). A cause should only be added intentionally (when 12 | ;; constructing the ex-info), but suppressed exceptions may be added 13 | ;; indirectly, while unwinding, via for example java's 14 | ;; try-with-resources or murphy. 15 | -------------------------------------------------------------------------------- /ext/bin/clj-1.11: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ueo pipefail 4 | 5 | nopf() { set +o pipefail; } 6 | 7 | clojure_ver=1.11.2 8 | spec_ver=0.3.218 # spec.alpha 9 | specs_ver=0.2.62 # core.specs.alpha 10 | 11 | script_home="$(cd "$(dirname "$0")" && pwd)" 12 | 13 | find_jar_or_die() 14 | { 15 | local name="$1" ver="$2" 16 | jar="$((nopf; find ~/.m2 -name "$name-$ver.jar") | head -1)" 17 | if ! test "$jar"; then 18 | echo "error: can't find $name $ver in ~/.m2" 1>&2 19 | exit 2 20 | fi 21 | echo "$jar" 22 | } 23 | 24 | clj_jar="$(find_jar_or_die clojure "$clojure_ver")" 25 | spec_jar="$(find_jar_or_die spec.alpha "$spec_ver")" 26 | specs_jar="$(find_jar_or_die core.specs.alpha "$specs_ver")" 27 | 28 | exec java -cp "$clj_jar:$spec_jar:$specs_jar" clojure.main "$@" 29 | -------------------------------------------------------------------------------- /ext/bin/jdk-from-spec: -------------------------------------------------------------------------------- 1 | """": # -*-python-*- 2 | command -v python3 > /dev/null && exec python3 "$0" "$@" 3 | command -v python2 > /dev/null && exec python2 "$0" "$@" 4 | echo "error: unable to find python3 or python2" 1>&2; exit 2 5 | """ 6 | 7 | # Gets the JDK package name from the spec string 8 | # jdk-from-spec core/openjdk8/pg-9.6 => openjdk8 9 | 10 | from __future__ import print_function 11 | from sys import exit, stderr 12 | import os, sys 13 | 14 | def usage(stream): 15 | print('Usage: jdk-from-spec core/openjdk8/pg-9.6', file=stream) 16 | 17 | def misuse(): 18 | usage(stderr) 19 | exit(2) 20 | 21 | len(sys.argv) == 2 or misuse() 22 | specs = sys.argv[1] 23 | specs = specs.split('/') 24 | specs = [x for x in specs if 'jdk' in x] 25 | len(specs) == 1 or misuse() 26 | print(specs[0]) 27 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/cli/tk_util.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.cli.tk-util 2 | "This namespace is separate from cli.util because we don't want to 3 | require any more than we have to there." 4 | (:require 5 | [clojure.tools.logging :as log] 6 | [puppetlabs.i18n.core :refer [trs]] 7 | [puppetlabs.puppetdb.cli.util :as cliu])) 8 | 9 | (defn run-tk-cli-cmd [f] 10 | (let [jdk (cliu/java-version)] 11 | (if-let [{:keys [warn error]} (cliu/jdk-unsupported-msg jdk)] 12 | (if error 13 | (do 14 | (binding [*out* *err*] (println (trs "error:") error)) 15 | (log/error error) 16 | cliu/err-exit-status) 17 | (do 18 | (binding [*out* *err*] (println (trs "warn:") warn)) 19 | (log/warn warn) 20 | (f))) 21 | (f)))) 22 | -------------------------------------------------------------------------------- /documentation/README.md: -------------------------------------------------------------------------------- 1 | # PuppetDB documentation 2 | 3 | If you were redirected while trying to reach the PuppetDB docs at puppet.com/docs/puppetdb, the version you were trying to reach is no longer maintained. 4 | 5 | For the most recent PuppetDB docs, see [puppet.com/docs/puppetdb/latest](https://puppet.com/docs/puppetdb/latest). 6 | For a list of the current maintained versions of open source Puppet and its components, see the most recent [Puppet version](https://puppet.com/docs/puppet/latest/about_agent.html) information. 7 | For a list of supported Puppet Enterprise versions, see the [Puppet Enterprise lifecycle](https://puppet.com/misc/puppet-enterprise-lifecycle). 8 | 9 | If you need docs for an older version of PuppetDB, you may be able to find it by switching to the relevant branch of this repository. 10 | 11 | -------------------------------------------------------------------------------- /resources/puppetlabs/puppetdb/generate/realistic/metadata.txt: -------------------------------------------------------------------------------- 1 | {:generated-from 2 | {:resource-size 500, 3 | :num-additional-logs 10, 4 | :num-reports 20, 5 | :silent false, 6 | :max-fact-depth 7, 7 | :additional-edge-percent 15, 8 | :random-distribution false, 9 | :exclude-unchanged-resources true, 10 | :high-change-reports-percent 5.0, 11 | :verbose false, 12 | :percent-add-report-logs 20, 13 | :high-change-resources-percent 60, 14 | :num-hosts 5, 15 | :blob-count 2, 16 | :total-fact-size 15, 17 | :low-change-resources-percent 5, 18 | :num-facts 400, 19 | :help false, 20 | :blob-size 40, 21 | :num-resources 800, 22 | :num-classes 100, 23 | :num-packages 1000, 24 | :title-size 40, 25 | :low-change-reports-percent 15.0}, 26 | :stored-at "/tmp/pdb-generate-14340763794995129976"} 27 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/query/edges.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.edges 2 | "Fact query generation" 3 | (:require [puppetlabs.puppetdb.schema :as pls] 4 | [puppetlabs.puppetdb.query :as query] 5 | [schema.core :as s])) 6 | 7 | ;; SCHEMA 8 | 9 | (def edge-schema 10 | (query/wrap-with-supported-fns 11 | {(s/optional-key :certname) String 12 | (s/optional-key :relationship) String 13 | (s/optional-key :source_title) String 14 | (s/optional-key :source_type) String 15 | (s/optional-key :target_title) String 16 | (s/optional-key :target_type) String})) 17 | 18 | ;; MUNGE 19 | 20 | (pls/defn-validated munge-result-rows 21 | "Reassemble rows from the database into the final expected format." 22 | [_ _] 23 | (fn [rows] 24 | (map #(s/validate edge-schema %) rows))) 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM almalinux:9 2 | 3 | WORKDIR / 4 | 5 | RUN dnf install -y --enablerepo=crb vim wget git rpm-build java-11-openjdk java-11-openjdk-devel libyaml-devel zlib zlib-devel gcc-c++ patch readline readline-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison sqlite-devel 6 | RUN wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein 7 | RUN chmod a+x lein 8 | RUN mv lein /usr/local/bin 9 | RUN wget -q https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer -O- | bash 10 | RUN /bin/bash --login -c 'rbenv install 3.2.6' 11 | RUN /bin/bash --login -c 'rbenv global 3.2.6' 12 | RUN git config --global user.email "openvox@voxpupuli.org" 13 | RUN git config --global user.name "Vox Pupuli" 14 | RUN git config --global --add safe.directory /code 15 | 16 | CMD ["tail -f /dev/null"] 17 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/70_install_released_puppetdb.rb: -------------------------------------------------------------------------------- 1 | # We skip this step entirely unless we are running in :upgrade mode. 2 | version = test_config[:package_build_version].to_s 3 | latest_released = get_latest_released(version) 4 | 5 | if ([:upgrade_oldest, :upgrade_latest].include? test_config[:install_mode]) \ 6 | && !(test_config[:skip_presuite_provisioning]) 7 | 8 | install_target = test_config[:install_mode] == :upgrade_latest ? latest_released : oldest_supported 9 | step "Install most recent released PuppetDB on the PuppetDB server for upgrade test" do 10 | databases.each do |database| 11 | enable_https_apt_sources(database) 12 | install_puppetdb(database, install_target) 13 | start_puppetdb(database) 14 | install_puppetdb_termini(master, databases, install_target) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /ext/bin/symlink-relative-to: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Creates symbolic link to a relative path 4 | # Relative path is forced, even if absolute is given 5 | # Simple on Linux, more complicated on MacOS bc `ln` is missing -r flag 6 | 7 | set -uexo pipefail 8 | 9 | script_name=$(basename "$0") 10 | 11 | target="$1" 12 | link_name="$2" 13 | 14 | case "$OSTYPE" in 15 | darwin*) 16 | # Require coreutils on MacOS 17 | if test -z "$(type -t realpath)"; then 18 | echo "$script_name: realpath missing; please install coreutils" 1>&2 19 | exit 2 20 | fi 21 | # Use realpath to create relative link path 22 | target="$(realpath --relative-to "$(dirname "$link_name")" "$target")" 23 | ln -sf "$target" "$link_name" 24 | ;; 25 | *) 26 | ln -srf "$target" "$link_name" 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /dev-docs/run-pdb-on-macos.md: -------------------------------------------------------------------------------- 1 | ## Installing dependencies: 2 | **Jdk 11:** 3 | 4 | brew install openjdk@11 5 | 6 | After installing openjdk run: 7 | 8 | sudo ln -sfn /usr/local/opt/openjdk@11/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-11.jdk 9 | **Leiningen:** 10 | 11 | brew install leiningen 12 | 13 | **pgbox script** 14 | 15 | Download the [script](https://gitlab.com/pgbox-org/pgbox/-/blob/master/pgbox) locally and add it to your PATH 16 | 17 | In the project directory install project dependencies using Leiningen: 18 | 19 | lein install 20 | 21 | ## Running pdb 22 | Initialize a pdbbox environment: 23 | 24 | $ ext/bin/pdbbox-init \ 25 | --sandbox ./test-sandbox \ 26 | --pgbin /usr/local/bin \ 27 | --pgport 5432 \ 28 | --bind-addr 127.0.0.1 \ 29 | Run the project 30 | 31 | lein run services -c test-sandbox/conf.d 32 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/ca/ca_pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwVM0mdVROZaeKC9BSKkk 3 | GiqKnO/HpPHjdj4ZVcGHXd9ypmfzOSskPQ8mTCwa4R0rMgzhHB7KXN3QX0Cn4jMl 4 | /zWxujmYHy+a69anou4OPRJSATAUvxLb3acxKTEeDpH4WrK/e5vImFYsLRMgDoYx 5 | iTPD0OjeD/DcVY+tXSbSO4TP9cYQPA7bETjCFRSi2Ww92/umk/eXFYyzQkRt9/u4 6 | YObMZTVPcQUpeTruKyzucA3gaejdOb/dLpuKAFBn9LuJW1VfwMMbhPcdHJ7jYWl3 7 | 0wIyyefZrc2au005MBvi0KIaJ7xbuNm7hZVT+CveGDeRRqSGLZ44k/6obv/hU3uY 8 | F776E6McRhCoZLUpze2iv9BI6VVAu8viErlxTnyzX2xFci0Uf/UklzPm5cdzqYEN 9 | d/YJExnwsy0eY5OE9Ya644REgXAjAPp2WnUjvk/fxBUHjiIqNtD3PE1PtFUbdNLh 10 | 1EQ9GLSBDrzRsPLM9VXMPJBsVrMPfB2yGkAOPFkqOE+XSIa/UyO00pVtVQBfJX8c 11 | NmquczHQe+KJ5OPmCP2MTJY2zSH/NzwWs1TSQaK0mkdtlaNAKzYoCPnDWNeHHONw 12 | 9bRO9qusc06Q1B4JLCvbyYAHRbe+elsTUE87Hy2vT0xkooWh4gEn3MFFN8mpdys8 13 | J1h0vxakklDBq0zUF9dJc8cCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /puppet/spec/unit/face/node/deactivate_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'spec_helper' 4 | require 'puppet/face' 5 | require 'puppet/indirector/node/puppetdb' 6 | 7 | describe "node face: deactivate" do 8 | let(:subject) { Puppet::Face[:node, :current] } 9 | 10 | it "should fail if no node is given" do 11 | expect { subject.deactivate }.to raise_error ArgumentError, /provide at least one node/ 12 | end 13 | 14 | it "should deactivate each node using the puppetdb terminus" do 15 | nodes = ['a', 'b', 'c'] 16 | nodes.each do |node| 17 | Puppet::Node::Puppetdb.any_instance.expects(:destroy).with do |request| 18 | request.key == node 19 | end.returns('uuid' => "uuid_#{node}") 20 | end 21 | 22 | subject.deactivate(*nodes).should == { 23 | 'a' => 'uuid_a', 24 | 'b' => 'uuid_b', 25 | 'c' => 'uuid_c', 26 | } 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/public_keys/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtBaxrIGzY+4DwwvfPG29 3 | 66oxzjf2vdROQgReUesY/XMu6K4oqdEx7Qvt7S8hOnkfRvOEekPNSH9KPbPB0K96 4 | qTz/AKTViFSEzdNEcKPDAPjLUQs1B8kY4yme2y6tyJ2UVl9Myb8xO/73o7WRfd0m 5 | ItkFqN4DQ2Y+jr1Pmd1aQZOUy1QqQeSgbpfETAu2DVgQUd6iW/wcAvO6+0MqJL79 6 | 7Fu7C06w0nTe24/IFU2LEY3xuAv/RWAp7aJRoHRzizoTIaodeHUhJB/GSSJAqXKf 7 | SKGh3b6RSidIVucPt2ov9/lW950iBOqzKYrxRLUTyd4llW+C4TbEt6MNKDdhgbFT 8 | pGc6uPKPiVWpqR8yUiXjE4DbeKTgf0sRbDNlnYVPynls0fGLaK7iJII3k6ip04Lt 9 | 1zf6qp7uWx9/rV1v7Se1yTg8FjPp31KQ8XqLVKLg50CepGFLaCez1zheNvWIjhZn 10 | XoHfNI1Yz3af6cVXhzpCW5JGridbCZPbAsN7ZcvJwfB7ao4VIkInESN8By12urHQ 11 | GGXk4Vig3G4xVPQUrhx2mGMMp07leLJ9CADBSJRynpiZYTl8bQbPIIaPCXY55WX7 12 | h9UO5nnMvKp0vUFaaFd3BMH2RBhNAE90yqnkcf2DZaAF1rxT5U5cDhAz0m3pE0I0 13 | laHhUsijgMSKt5MBF8wd2WMCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Claire Cadman claire cadman 2 | Claire Cadman clairecadman 3 | Heston Hoffman hestonhoffman 4 | 5 | Zak Kent Zak-Kent 6 | Zak Kent 7 | Jean Bond jbondpdx 8 | Ethan J. Brown Iristyle 9 | Cas Donoghue donoghuc 10 | Michelle Fredette MFredette 11 | Jonathan Newman jonathannewman 12 | Eric Newton <95654936+rileynewton@users.noreply.github.com> 13 | -------------------------------------------------------------------------------- /test-resources/puppetlabs/puppetdb/ssl/public_keys/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0IRgG8Qh1NcU4zDPXzBq 3 | Gzq2DboL4tPZc7BCylv8CPlZPYQHCcbaV4MLN84ArKEcMvqNGqthTfaOqioHObOR 4 | 5WwfOo4w0WizK9wEtTzPItlT4g/WuMkLITzHH0ex2E8omP3d/+BI1Hk1CVFxl+pq 5 | GE7CH+dh1jekWI2CNShHCFXZDTWf6S7xYnCQtVPVNERb1OJJaTtwdYX9mW084wzG 6 | 8n2rSReHyxVgrga23emmFNRbSqBdDY2c1ZNb7u9yJGOcfF6hjerpkbu9nwd7BgpH 7 | hbwPoUDfbEKxYQtKxaUAAG3WUGMoZqz9+JhJ4OpJ8jGScWf8Igz0AinY8tLfCHl7 8 | Sj3yp7L0A3ZZHIbtCzSKOHArH1uwzFszQtHmNe5eMGeMZDr1/AKPCVSw5Kk09YuJ 9 | sz4abG+GCEmACUfl0Pj4ao3vtYudxBNZhdcac7Dl59WK9LhuBTe3yXyYcsfqq4ui 10 | VHUot8K/4e6qS9GmiwCqovAof52GVurVj6LiyI/F9hXk1nT3Zn1HQXgE8SYuEqeC 11 | Qr795BfEtjbN9pHo2zzfJEl8xdHISUQJf/5EFPUvIhFmD4xuxGDsOwpYjSmXPGyj 12 | /gKBKZhJ8+S7meR6Ssw3fhiFS1zGx0dKZjXn+naiW8qlMmPd7l9rYpWzBy/vsWwL 13 | XuAh8xeq83e0hM/6rUsskc0CAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/mq.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.mq 2 | (:require 3 | [clojure.java.jmx :as jmx] 4 | [metrics.timers :refer [timer]] 5 | [puppetlabs.puppetdb.metrics.core :as metrics])) 6 | 7 | (def mq-metrics-registry (get-in metrics/metrics-registries [:mq :registry])) 8 | 9 | (def metrics (atom {:message-persistence-time (timer mq-metrics-registry 10 | (metrics/keyword->metric-name 11 | [:global] :message-persistence-time))})) 12 | 13 | (defn queue-size 14 | "Returns the number of pending messages in the queue. 15 | Throws {:kind ::queue-not-found} when the queue doesn't exist." 16 | [] 17 | (try 18 | (jmx/read "puppetlabs.puppetdb.mq:name=global.depth" :Count) 19 | (catch javax.management.InstanceNotFoundException _ 20 | (throw (ex-info "" {:kind ::queue-not-found}))))) 21 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/honeysql.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.honeysql 2 | "Some generic HoneySQL extensions, candidates for re-usability and 3 | potential upstream submission.") 4 | 5 | ;; SCHEMA 6 | 7 | ;; COMMMON DIRECT SQL FUNCTIONS 8 | 9 | (defn coalesce 10 | "coalesce(arg, ...) sql function" 11 | [& args] 12 | (into [:coalesce] args)) 13 | 14 | (defn scast 15 | "cast(source AS target) sql function" 16 | [source 17 | target] 18 | [:cast source target]) 19 | 20 | (defn json-agg 21 | "json_agg(expr) sql function" 22 | [expr] 23 | [:json_agg expr]) 24 | 25 | (defn row-to-json 26 | "row_to_json(record) sql function" 27 | [record] 28 | [[:row_to_json record]]) 29 | 30 | ;; Misc honeysql functions 31 | 32 | (defn extract-sql 33 | "Returns a keyworidzed version of the SQL string if `k` is an sql raw, otherwise returns `s`" 34 | [s] 35 | (if (keyword? s) 36 | s 37 | (keyword (second s)))) 38 | -------------------------------------------------------------------------------- /puppet/lib/puppet/indirector/facts/puppetdb_apply.rb: -------------------------------------------------------------------------------- 1 | require 'puppet/indirector/facts/puppetdb' 2 | 3 | # This class provides an alternative implementation of the Facts::Puppetdb 4 | # terminus that better suits execution via `puppet apply`. 5 | # 6 | # This terminus is designed to be used as a cache terminus, to ensure that facts 7 | # are stored in PuppetDB. It does not act as a real cache itself however, it 8 | # tells Puppet to fallback to the `terminus` instead. 9 | class Puppet::Node::Facts::PuppetdbApply < Puppet::Node::Facts::Puppetdb 10 | attr_writer :dbstored 11 | 12 | # Here we override the normal save, only saving the first time, as a `save` 13 | # can be called multiple times in a puppet run. 14 | def save(args) 15 | unless @dbstored 16 | @dbstored = true 17 | super(args) 18 | end 19 | end 20 | 21 | # By returning nil, we force puppet to use the real terminus. 22 | def find(args) 23 | nil 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PuppetDB [![Build Status](https://app.travis-ci.com/puppetlabs/puppetdb.svg?branch=main)](https://app.travis-ci.com/puppetlabs/puppetdb) 2 | 3 | [docs]: https://docs.puppet.com/puppetdb/latest 4 | [contributing]: documentation/CONTRIBUTING.md 5 | [users]: https://groups.google.com/forum/#!forum/puppet-users 6 | 7 | PuppetDB is the fast, scalable, and reliable data warehouse for Puppet. It caches data generated by Puppet, and gives you advanced features at awesome speed with a powerful API. 8 | 9 | For documentation on this product, consult the [latest documentation][docs]. 10 | 11 | ## Contributing 12 | 13 | If you would like to contribute to PuppetDB, please take a look at our [contributing doc][contributing]. 14 | 15 | ## Maintenance 16 | 17 | Community Help: [Puppet Users Mailing List][users], [Puppet Community Portal](https://puppet.com/community) 18 | 19 | Tickets: [https://tickets.puppet.com/browse/PDB](https://tickets.puppet.com/browse/PDB) 20 | -------------------------------------------------------------------------------- /acceptance/setup/post_suite/99_teardown.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | test_config = PuppetDBExtensions.config 4 | 5 | def uninstall_package(host, os_families, pkg_name) 6 | os = os_families[host.name] 7 | case os 8 | when :debian 9 | on(host, "apt-get -f -y purge #{pkg_name} ") 10 | when :redhat 11 | on(host, "yum -y remove #{pkg_name}") 12 | else 13 | raise ArgumentError, "Unsupported OS family: '#{os}'" 14 | end 15 | end 16 | 17 | 18 | step "Stop puppetdb" do 19 | stop_puppetdb(database) 20 | end 21 | 22 | if (test_config[:purge_after_run]) 23 | if (test_config[:install_type] == :package) 24 | step "Uninstall packages" do 25 | uninstall_package(database, test_config[:os_families], "puppetdb") 26 | uninstall_package(master, test_config[:os_families], "puppetdb-termini") 27 | hosts.each do |host| 28 | uninstall_package(host, test_config[:os_families], "puppet") 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/parse_yaml.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.parse-yaml 2 | (:import (java.util List Map Set) 3 | (org.yaml.snakeyaml Yaml))) 4 | 5 | (defprotocol JavaMap->ClojureMap 6 | (java->clj [o])) 7 | 8 | (extend-protocol JavaMap->ClojureMap 9 | Map 10 | (java->clj [o] (let [entries (.entrySet o)] 11 | (reduce (fn [m [^String k v]] 12 | (assoc m (keyword k) (java->clj v))) 13 | {} entries))) 14 | 15 | List 16 | (java->clj [o] (vec (map java->clj o))) 17 | 18 | Set 19 | (java->clj [o] (set (map java->clj o))) 20 | 21 | Object 22 | (java->clj [o] o) 23 | 24 | nil 25 | (java->clj [_] nil)) 26 | 27 | (defn parse-yaml 28 | [yaml-string] 29 | ;; default in snakeyaml 2.0 is to not allow 30 | ;; global tags, which is the source of exploits. 31 | (let [yaml (new Yaml) 32 | data (.load yaml ^String yaml-string)] 33 | (java->clj data))) -------------------------------------------------------------------------------- /ext/bin/spec-includes: -------------------------------------------------------------------------------- 1 | """": # -*-python-*- 2 | command -v python3 > /dev/null && exec python3 "$0" "$@" 3 | command -v python2 > /dev/null && exec python2 "$0" "$@" 4 | echo "error: unable to find python3 or python2" 1>&2; exit 2 5 | """ 6 | 7 | # Prints search string if its a component of test spec 8 | # First argument is test spec, second is search string 9 | # Ex: spec-includes core/openjdk8/pg-9.6/rich rich => rich 10 | # Ex: spec-includes core/openjdk8/pg-9.6/rich openjdk8 => openjdk8 11 | # Ex: spec-includes core/openjdk8/pg-9.6/rich foo => 12 | 13 | from __future__ import print_function 14 | from sys import exit, stderr 15 | import os, sys 16 | 17 | def usage(stream): 18 | print('Usage: spec-includes .../pup-5.3.x/srv-5.1.x/something something', 19 | file=stream) 20 | 21 | def misuse(): 22 | usage(stderr) 23 | exit(2) 24 | 25 | len(sys.argv) == 3 or misuse() 26 | specs, what = sys.argv[1:] 27 | specs = specs.split('/') 28 | if what in specs: 29 | print(what) 30 | -------------------------------------------------------------------------------- /ext/bin/prefixed-ref-from-spec: -------------------------------------------------------------------------------- 1 | """": # -*-python-*- 2 | command -v python3 > /dev/null && exec python3 "$0" "$@" 3 | command -v python2 > /dev/null && exec python2 "$0" "$@" 4 | echo "error: unable to find python3 or python2" 1>&2; exit 2 5 | """ 6 | 7 | # Parses a spec for a given prefix and returns the ref (version number) 8 | # Basically prints out everything after the prefix up to the next / or end of spec 9 | # prefixed-ref-from-spec core/openjdk8/pg-9.6 pg- => 9.6 10 | 11 | from __future__ import print_function 12 | from sys import exit, stderr 13 | import os, sys 14 | 15 | def usage(stream): 16 | print('Usage: prefixed-ref-from-spec .../pup-5.5.x/srv-5.3.x/pg-9.6 srv-', 17 | file=stream) 18 | 19 | def misuse(): 20 | usage(stderr) 21 | exit(2) 22 | 23 | len(sys.argv) == 3 or misuse() 24 | specs, prefix = sys.argv[1:] 25 | specs = specs.split('/') 26 | specs = [x for x in specs if x.startswith(prefix)] 27 | len(specs) == 1 or misuse() 28 | print(specs[0][len(prefix):]) 29 | -------------------------------------------------------------------------------- /resources/ext/config/request-logging.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | /var/log/puppetlabs/puppetdb/puppetdb-access.log 4 | true 5 | 6 | /var/log/puppetlabs/puppetdb/puppetdb-access-%d{yyyy-MM-dd}.%i.log.gz 7 | 8 | 200MB 9 | 90 10 | 1GB 11 | 12 | 13 | %h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}" %D %header{X-Uncompressed-Length} 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ext/bin/flavor-from-spec: -------------------------------------------------------------------------------- 1 | """": # -*-python-*- 2 | command -v python3 > /dev/null && exec python3 "$0" "$@" 3 | command -v python2 > /dev/null && exec python2 "$0" "$@" 4 | echo "error: unable to find python3 or python2" 1>&2; exit 2 5 | """ 6 | 7 | # For a given spec, finds the "flavor" 8 | # core/openjdk8/pg-9.6 => core 9 | # core+ext/openjdk11/pg-13 => core+ext 10 | 11 | from __future__ import print_function 12 | from sys import exit, stderr 13 | import os, sys 14 | 15 | valid_flavors = ('core', 'ext', 'core+ext', 'int', 'lint', 'rspec') 16 | 17 | def usage(stream): 18 | print('Usage: flavor-from-spec core/openjdk8/pg-9.6', file=stream) 19 | 20 | def misuse(): 21 | usage(stderr) 22 | exit(2) 23 | 24 | len(sys.argv) == 2 or misuse() 25 | orig_specs = sys.argv[1] 26 | specs = orig_specs.split('/') 27 | specs = [x for x in specs if x in valid_flavors] 28 | if len(specs) != 1: 29 | print('Invalid test flavor in spec:', repr(orig_specs), file=stderr) 30 | exit(2) 31 | print(specs[0]) 32 | -------------------------------------------------------------------------------- /ext/bin/host-info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Prints out information about the host machine for testing logs 4 | 5 | set -uexo pipefail 6 | 7 | # Print out operating system information 8 | uname -a 9 | # Print out machine-specific hostname->IP address mappings 10 | cat /etc/hosts 11 | 12 | # Print out hostname memory information 13 | case "$OSTYPE" in 14 | darwin*|netbsd) 15 | hostname 16 | vm_stat -c 1 17 | sysctl -n machdep.cpu.brand_string 18 | system_profiler SPHardwareDataType 19 | ;; 20 | *) 21 | hostname --fqdn 22 | free -m 23 | free -h 24 | ;; 25 | esac 26 | 27 | # Print out detailed CPU information 28 | if test -e /proc/cpuinfo; then 29 | cat /proc/cpuinfo 30 | fi 31 | 32 | # Print out versions of critical testing software 33 | bash --version 34 | java -version || true 35 | lein version || true 36 | ruby --version || true 37 | bundle --version || true 38 | bundler --version || true 39 | pgbox --version || true 40 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/facts.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.facts 2 | (:require 3 | [puppetlabs.puppetdb.utils :as utils])) 4 | 5 | (def base-facts 6 | "A minimal set of facts useful for testing" 7 | {"hardwaremodel" "x86_64" 8 | "memorysize" "16.00 GB" 9 | "memorytotal" "16.00 GB" 10 | "puppetversion" "3.4.2" 11 | "ipaddress_lo0" "127.0.0.1" 12 | "id" "foo" 13 | "operatingsystem" "Debian"}) 14 | 15 | (def base-package-inventory 16 | [["ntp" "4.2.8" "apt"] 17 | ["puppet-agent" "6.6.0" "apt"] 18 | ["nokogiri" "4.5.6" "gem"]]) 19 | 20 | (defn create-host-facts 21 | "Create a map for `node` suitable for spitting to a tarball 22 | used by import/export/anonymize" 23 | [node additional-facts] 24 | {"facts" 25 | {node 26 | {"certname" node 27 | "environment" "DEV" 28 | "values" (merge base-facts additional-facts)}}}) 29 | 30 | (defn munge-facts 31 | [facts] 32 | (->> facts 33 | utils/vector-maybe 34 | (map #(dissoc % :producer_timestamp :hash)))) 35 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/integration/masterless.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.integration.masterless 2 | (:require 3 | [clojure.test :refer :all] 4 | [puppetlabs.puppetdb.integration.fixtures :as int])) 5 | 6 | (deftest ^:integration masterless-fact-storage 7 | (with-open [pg (int/setup-postgres) 8 | pdb (int/run-puppetdb pg {})] 9 | (testing "Run puppet apply to populate the db" 10 | (let [{:keys [out]} (int/run-puppet-apply pdb "notice($foo)" 11 | {:env {"FACTER_foo" "testfoo"}})] 12 | (is (re-find #"testfoo" out)))) 13 | 14 | (testing "Run again to ensure we aren't using puppetdb for the answer" 15 | (let [{:keys [out]} (int/run-puppet-apply pdb "notice($foo)" 16 | {:env {"FACTER_foo" "second test"}})] 17 | (is (re-find #"second test" out)))) 18 | 19 | (testing "Check that the fact was put in puppetdb" 20 | (is (= [{:value "second test"}] (int/pql-query pdb "facts [value] { name='foo' }")))))) 21 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/query/catalog_inputs.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.catalog-inputs 2 | (:require [puppetlabs.puppetdb.query :as query] 3 | [puppetlabs.puppetdb.schema :as pls] 4 | [puppetlabs.puppetdb.utils :as utils] 5 | [schema.core :as s])) 6 | 7 | (def row-schema 8 | (query/wrap-with-supported-fns 9 | {(s/optional-key :certname) s/Str 10 | (s/optional-key :producer_timestamp) pls/Timestamp 11 | (s/optional-key :catalog_uuid) s/Str 12 | (s/optional-key :inputs) [[s/Str]]})) 13 | 14 | (def converted-row-schema 15 | (query/wrap-with-supported-fns 16 | {(s/optional-key :certname) s/Str 17 | (s/optional-key :producer_timestamp) pls/Timestamp 18 | (s/optional-key :catalog_uuid) s/Str 19 | (s/optional-key :inputs) [[s/Str]]})) 20 | 21 | (pls/defn-validated convert-array-to-vec :- converted-row-schema 22 | [row] 23 | (utils/update-when row [:inputs] (partial map vec))) 24 | 25 | (defn munge-catalog-inputs 26 | [_ _] 27 | (fn [rows] 28 | (map convert-array-to-vec rows))) 29 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/http/query_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.http.query-test 2 | (:require [puppetlabs.puppetdb.http.query :as sut] 3 | [clojure.test :as t])) 4 | 5 | (t/deftest add-criteria-test 6 | (t/are [query criteria result] 7 | (= result (sut/add-criteria criteria query)) 8 | ["=" "bar" 7] ["=" "foo" 42] 9 | ["and" ["=" "bar" 7] ["=" "foo" 42]] 10 | 11 | ["extract" ["foo" "bar"]] nil 12 | ["extract" ["foo" "bar"]] 13 | 14 | ["extract" ["foo" "bar"]] ["=" "foo" 42] 15 | ["extract" ["foo" "bar"] ["=" "foo" 42]] 16 | 17 | ["extract" ["foo" "bar"] nil] ["=" "foo" 42] 18 | ["extract" ["foo" "bar"] ["=" "foo" 42]] 19 | 20 | ["extract" ["foo" "bar"] ["=" "bar" 7]] ["=" "foo" 42] 21 | ["extract" ["foo" "bar"] ["and" ["=" "bar" 7] ["=" "foo" 42]]] 22 | 23 | ["from" "thing" ["extract" ["foo" "bar"] ["=" "bar" 7]]] ["=" "foo" 42] 24 | ["from" "thing" ["extract" ["foo" "bar"] ["and" ["=" "bar" 7] ["=" "foo" 42]]]] 25 | 26 | ["from" "thing"] ["=" "foo" 42] 27 | ["from" "thing" ["=" "foo" 42]])) 28 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/mem.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.mem 2 | (:require [clojure.java.shell :as shell]) 3 | (:import [java.lang.management ManagementFactory] 4 | [java.nio.file Files NoSuchFileException])) 5 | 6 | (def getpid 7 | "Returns the integer PID of the current process." 8 | (let [pid (atom nil)] 9 | (fn [] 10 | (if-let [p @pid] 11 | p 12 | (reset! pid (->> (ManagementFactory/getRuntimeMXBean) 13 | .getName 14 | (re-find #"[0-9]+") 15 | Integer/parseInt)))))) 16 | 17 | (defn dump-heap 18 | "Dumps the current heap to filename via jmap." 19 | [filename] 20 | (try 21 | ;; Because jmap quietly does nothing if the file exists. 22 | (-> filename java.io.File. .toPath Files/delete) 23 | (catch NoSuchFileException _ 24 | true)) 25 | (assert (zero? 26 | (:exit (shell/sh "jmap" 27 | (str "-dump:live,format=b,file=" filename) 28 | (str (getpid))))))) 29 | -------------------------------------------------------------------------------- /contrib/gem/openvoxdb-terminus.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | 5 | Gem::Specification.new do |gem| 6 | gem.name = "openvoxdb-terminus" 7 | gem.version = "3.0.0" 8 | gem.authors = ["Puppet Labs", "OpenVoxProject"] 9 | gem.email = ["openvox@voxpupuli.org"] 10 | gem.description = "Puppet terminus files to connect to OpenVoxDB" 11 | gem.summary = "Connect OpenVox to OpenVoxDB by setting up a terminus for OpenVoxDB" 12 | gem.homepage = "https://github.com/OpenVoxProject/openvoxdb" 13 | gem.license = "Apache-2.0" 14 | 15 | gem.files = Dir['LICENSE.txt', 'NOTICE.txt', 'README.md', 'puppet/lib/**/*', 'puppet/spec/**/*'] 16 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 17 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 18 | gem.require_paths = ["lib"] 19 | 20 | gem.add_runtime_dependency 'json' 21 | gem.add_runtime_dependency 'openvox', '> 8.19', '< 9' 22 | end 23 | -------------------------------------------------------------------------------- /acceptance/tests/commands/delete_reports.rb: -------------------------------------------------------------------------------- 1 | test_name "test puppetdb delete" do 2 | step "clear puppetdb database" do 3 | clear_and_restart_puppetdb(database) 4 | end 5 | 6 | with_puppet_running_on master, { 7 | 'master' => { 8 | 'autosign' => 'true' 9 | }} do 10 | 11 | step "Run agents once to activate nodes" do 12 | run_agent_on agents, "--test --server #{master}" 13 | end 14 | end 15 | 16 | step "Verify the data was stored in PuppetDB" do 17 | check_record_count("nodes", agents.length) 18 | check_record_count("factsets", agents.length) 19 | check_record_count("catalogs", agents.length) 20 | check_record_count("reports", agents.length) 21 | end 22 | 23 | step "Run puppetdb delete-reports" do 24 | bin_loc = puppetdb_bin_dir(database) 25 | on(database, "#{bin_loc}/puppetdb delete-reports") 26 | end 27 | 28 | step "Verify puppetdb can still be queried" do 29 | check_record_count("nodes", agents.length) 30 | end 31 | 32 | step "Verify puppetdb has no reports" do 33 | check_record_count("reports", 0) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /resources/ext/config/conf.d/jetty.ini: -------------------------------------------------------------------------------- 1 | [jetty] 2 | # IP address or hostname to listen for clear-text HTTP. To avoid resolution 3 | # issues, IP addresses are recommended over hostnames. 4 | # Default is `localhost`. 5 | # host = 6 | 7 | # Port to listen on for clear-text HTTP. 8 | port = 8080 9 | 10 | # The following are SSL specific settings. They can be configured 11 | # automatically with the tool `puppetdb ssl-setup`, which is normally 12 | # ran during package installation. 13 | 14 | # IP address to listen on for HTTPS connections. Hostnames can also be used 15 | # but are not recommended to avoid DNS resolution issues. To listen on all 16 | # interfaces, use `0.0.0.0`. 17 | # ssl-host = 18 | 19 | # The port to listen on for HTTPS connections 20 | # ssl-port = 21 | 22 | # Private key path 23 | # ssl-key = 24 | 25 | # Public certificate path 26 | # ssl-cert = 27 | 28 | # Certificate authority path 29 | # ssl-ca-cert = 30 | 31 | # Access logging configuration path. To turn off access logging 32 | # comment out the line with `access-log-config=...` 33 | access-log-config = /etc/puppetlabs/puppetdb/request-logging.xml 34 | -------------------------------------------------------------------------------- /documentation/api/wire_format/facts_format_v4.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Facts wire format, version 4" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/wire_format/facts_format_v4.html" 5 | --- 6 | # Facts wire format - v4 7 | 8 | Facts are represented as JSON. Unless otherwise noted, `null` is not 9 | allowed anywhere in the set of facts. 10 | 11 | {"certname": , 12 | "environment": , 13 | "producer_timestamp": , 14 | "values": { 15 | : , 16 | ... 17 | } 18 | } 19 | 20 | The `"certname"` key is the certname the facts are associated with. 21 | 22 | The `"environment"` key is the environment associated to the node when the facts were collected. 23 | 24 | The `"values"` key points to a _JSON Object_ that represents the set 25 | of facts. Each key is the fact name, and the value is the fact value. 26 | 27 | The `"producer_timestamp"` key points to a timestamp reflecting 28 | the time of fact set submission from the Puppet Server to PuppetDB. 29 | 30 | Fact names and values **must** be strings. 31 | 32 | ## Encoding 33 | 34 | The entire fact set is expected to be valid JSON, which mandates UTF-8 35 | encoding. 36 | -------------------------------------------------------------------------------- /ext/jenkins/lein-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ueo pipefail 4 | set -x 5 | 6 | ulimit -u 4096 7 | 8 | export PDB_TEST_DUMP_LOG_ON_FAILURE=true 9 | export GEM_SOURCE="https://artifactory.delivery.puppetlabs.net/artifactory/api/gems/rubygems/" 10 | 11 | pghost=fixture-pg94.delivery.puppetlabs.net 12 | pgport=5432 13 | 14 | export HTTP_CLIENT="wget --no-check-certificate -O" 15 | 16 | rm -f testreports.xml *.war *.jar 17 | 18 | export PDB_TEST_DB_HOST="$pghost" 19 | export PDB_TEST_DB_PORT="$pgport" 20 | 21 | # For now, just conflate the normal test and admin users. 22 | export PDB_TEST_DB_USER=pdb_test 23 | export PDB_TEST_DB_ADMIN=pdb_test_admin 24 | export PDB_TEST_DB_USER_PASSWORD=daroniwu54ot 25 | export PDB_TEST_DB_ADMIN_PASSWORD=optuwaeg6ujzo 26 | 27 | PDB_TEST_ID="$(ruby -e "require 'securerandom'; print SecureRandom.uuid")" 28 | PDB_TEST_ID="$(echo -n "$PDB_TEST_ID" | perl -pe 's/[^a-zA-Z0-9_]/_/gmo')" 29 | declare -rx PDB_TEST_ID 30 | 31 | lein --version 32 | lein clean 33 | lein test 34 | 35 | export NO_ACCEPTANCE=true 36 | ext/bin/test-config --reset puppet-ref 37 | PDB_NO_PUPPETSERVER_INSTALL=true ext/bin/test-config --reset puppetserver-ref 38 | exec lein test :integration 39 | -------------------------------------------------------------------------------- /documentation/using.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using PuppetDB" 3 | layout: default 4 | canonical: "/puppetdb/latest/using.html" 5 | --- 6 | 7 | # Using PuppetDB 8 | 9 | [exported]: https://puppet.com/docs/puppet/latest/lang_exported.html 10 | 11 | 12 | Currently, PuppetDB's primary use is enabling advanced Puppet features. As use becomes more widespread, we expect additional applications to be built on PuppetDB. 13 | 14 | If you wish to build applications on PuppetDB, see the navigation sidebar for links to the API specifications. 15 | 16 | ## Checking node status 17 | 18 | The PuppetDB plugins [installed on your Puppet Server(s)](./connect_puppet_server.markdown) include a `status` action for the `node` face. On your Puppet Server, run: 19 | 20 | $ sudo puppet node status 21 | 22 | where `` is the name of the node you wish to investigate. This will tell you whether the node is active, when its last catalog was submitted, and when its last facts were submitted. 23 | 24 | ## Using exported resources 25 | 26 | PuppetDB lets you use exported resources, which allows your nodes to publish information for use by other nodes. 27 | 28 | [Learn more about using exported resources here.][exported] 29 | 30 | -------------------------------------------------------------------------------- /ext/bin/check-spec-env: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Sanity check for a PuppetDB test spec 4 | # Ensures that correct JDK is installed and test flavor is valid 5 | 6 | set -ueo pipefail 7 | 8 | usage() 9 | { 10 | echo "Usage: $(basename $0) SPEC" 11 | } 12 | 13 | misuse() 14 | { 15 | usage 1>&2 16 | exit 2 17 | } 18 | 19 | # Ensure version of JDK from spec is available 20 | verify-jdk() { 21 | local spec="$1" 22 | local actual_jdk expected_jdk 23 | expected_jdk="$(ext/bin/jdk-from-spec "$spec")" 24 | actual_jdk="$(ext/bin/jdk-info --print spec)" 25 | if test "$expected_jdk" != "$actual_jdk"; then 26 | printf "JDK in path %q is not what PDB_TEST specifies: %q\n" \ 27 | "$actual_jdk" "$expected_jdk" 1>&2 28 | exit 2 29 | fi 30 | } 31 | 32 | test $# -ne 1 && misuse 33 | 34 | # Ensure test kind/flavor from test spec is valid 35 | spec="$1" 36 | test_kind=$(ext/bin/flavor-from-spec "$spec") 37 | case "$test_kind" in 38 | core|ext|core+ext|int|lint) 39 | verify-jdk "$spec" 40 | ;; 41 | rspec) 42 | ;; 43 | *) 44 | echo "Unexpected test category: $test_kind" 1>&2 45 | exit 2 46 | ;; 47 | esac 48 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/cli/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.cli.util-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [puppetlabs.puppetdb.cli.util :refer [jdk-support-status]])) 5 | 6 | (deftest jdk-support-status-behavior 7 | (is (= :unsupported (jdk-support-status "1.5"))) 8 | (is (= :unsupported (jdk-support-status "1.5.0"))) 9 | (is (= :unsupported (jdk-support-status "1.6"))) 10 | (is (= :unsupported (jdk-support-status "1.6.0"))) 11 | (is (= :unknown (jdk-support-status "1.60"))) 12 | (is (= :unknown (jdk-support-status "1.60.1"))) 13 | (is (= :unknown (jdk-support-status "huh?"))) 14 | (is (= :unsupported (jdk-support-status "1.7"))) 15 | (is (= :unsupported (jdk-support-status "1.7.0"))) 16 | (is (= :deprecated (jdk-support-status "1.8"))) 17 | (is (= :deprecated (jdk-support-status "1.8.0"))) 18 | (is (= :deprecated (jdk-support-status "1.9"))) 19 | (is (= :deprecated (jdk-support-status "1.9.0"))) 20 | (is (= :deprecated (jdk-support-status "10"))) 21 | (is (= :deprecated (jdk-support-status "10.0"))) 22 | (is (= :tested (jdk-support-status "11.0"))) 23 | (is (= :tested (jdk-support-status "11.0.7"))) 24 | (is (= :official (jdk-support-status "17.0.4")))) 25 | -------------------------------------------------------------------------------- /config.sample.ini: -------------------------------------------------------------------------------- 1 | # See README.md for more thorough explanations of each section and 2 | # option. 3 | 4 | [global] 5 | # Store mq/db data in a custom directory 6 | vardir = /var/lib/puppetdb 7 | 8 | # Use an external logback config file 9 | # logging-config = /path/to/logback.xml 10 | 11 | [puppetdb] 12 | # List of certificate names from which to allow incoming HTTPS requests: 13 | # certificate-allowlist = /path/to/certname/allowlist 14 | 15 | [database] 16 | 17 | # Subname pattern: //host:port/databaseName 18 | #subname = //localhost:5432/puppetdb 19 | 20 | # Connect as a specific user 21 | # username = foobar 22 | 23 | # Use a specific password 24 | # password = foobar 25 | 26 | # How often (in minutes) to compact the database 27 | # gc-interval = 60 28 | 29 | [command-processing] 30 | # How many command-processing threads to use, defaults to (CPUs / 2) 31 | # threads = 4 32 | 33 | [jetty] 34 | # What host to listen on, defaults to binding to 'localhost' 35 | # host = foo.my.net 36 | 37 | # What port to listen on 38 | port = 8080 39 | 40 | [nrepl] 41 | # Set to true to enable the remote REPL 42 | enabled = false 43 | 44 | # What port the REPL should listen on 45 | port = 8082 46 | 47 | # IP address to listen on 48 | host = 127.0.0.1 49 | -------------------------------------------------------------------------------- /documentation/api/meta/v1/server-time.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Server time endpoint" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/meta/v1/server-time.html" 5 | --- 6 | 7 | # Server time endpoint 8 | 9 | [curl]: ../../query/curl.markdown#using-curl-from-localhost-non-sslhttp 10 | 11 | The `/server-time` endpoint can be used to retrieve the server time from the PuppetDB server. 12 | 13 | ## `/pdb/meta/v1/server-time` 14 | 15 | This metadata endpoint will return the current time of the clock on the PuppetDB 16 | server. This can be useful as input to other time-based queries against PuppetDB, 17 | to eliminate the possibility of time differences between the clocks on client 18 | machines. 19 | 20 | This endpoint does not use any URL parameters or query strings. 21 | 22 | ### Response format 23 | 24 | The response will be in `application/json`, and will return a JSON map with a 25 | single key: `server_time`, whose value is an ISO-8601 representation of the 26 | current time on the PuppetDB server. 27 | 28 | {"server_time": "2013-09-20T20:54:27.472Z"} 29 | 30 | ### Example 31 | 32 | [Using `curl` from localhost][curl]: 33 | 34 | curl -X GET http://localhost:8080/pdb/meta/v1/server-time 35 | 36 | {"server_time": "2013-09-20T20:54:27.472Z"} 37 | 38 | -------------------------------------------------------------------------------- /resources/ext/cli/upgrade: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Note: the variables here may look generic, but are currently 6 | # exported by the puppet packaging scripts. For example, even though 7 | # they may look like it, JAVA_BIN and JAVA_ARGS are not upstream java 8 | # variables (like JAVA_OPTS), but are set and exported by puppet. And 9 | # the USER variable here is not the bash variable of the same name, 10 | # but is shadowing/clobbering it. Most/all of these should be renamed 11 | # to something less generic like PDB_CONFIG_FILE, or better yet (for 12 | # that particular one), perhaps puppetdb should just support the 13 | # variable itself. 14 | 15 | cli_defaults=${INSTALL_DIR}/cli/cli-defaults.sh 16 | CLASSPATH=${INSTALL_DIR}/puppetdb.jar 17 | 18 | if [ -e "$cli_defaults" ]; then 19 | . "$cli_defaults" 20 | if [ $? -ne 0 ]; then 21 | echo "Unable to initialize cli defaults, failing start." 1>&2 22 | exit 1 23 | fi 24 | fi 25 | 26 | cmd=($(printf "%q %s -Dlogappender=STDOUT -cp %q clojure.main -m puppetlabs.puppetdb.core upgrade -c %q" \ 27 | "$JAVA_BIN" "$JAVA_ARGS" "$CLASSPATH" "$CONFIG")) 28 | 29 | if test "$(id -un)" = "$USER"; then 30 | exec "${cmd[@]}" "$@" 31 | else 32 | exec su "$USER" -s /bin/sh -c "${cmd[*]} ${*}" 33 | fi 34 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/test_import.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.test-import 2 | (:require [clojure.test :refer :all] 3 | [puppetlabs.puppetdb.import :refer [command-matcher]] 4 | [puppetlabs.puppetdb.scf.hash :as hash] 5 | [puppetlabs.puppetdb.time :refer [now]])) 6 | 7 | (defn cmd-path-with-hash [entity producer-ts-hash certname] 8 | (format "puppetdb-bak/%s/%s-%s.json" entity certname producer-ts-hash)) 9 | 10 | (def producer-ts-hash (hash/generic-identity-hash (now))) 11 | 12 | (deftest extract-command-with-hash 13 | (doseq [entity ["catalogs" "reports"]] 14 | (are [certname] (= (-> entity 15 | (cmd-path-with-hash producer-ts-hash certname) 16 | command-matcher) 17 | [entity certname]) 18 | "foo.com" 19 | "foo" 20 | "some-really-long-name-with-dashes" 21 | "some-name-with-double-json.json"))) 22 | 23 | (defn facts-path [certname] 24 | (format "puppetdb-bak/facts/%s.json" certname)) 25 | 26 | (deftest test-extract-facts-certname 27 | (are [certname] (= (command-matcher (facts-path certname)) 28 | ["facts" certname]) 29 | "foo.com" 30 | "foo" 31 | "some-really-long-name-with-dashes" 32 | "some-name-with-double-json.json")) 33 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/generative/overrideable_generators_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.generative.overrideable-generators-test 2 | (:require [clojure.test :refer :all] 3 | [clojure.test.check.clojure-test :as tc] 4 | [clojure.test.check.properties :as prop] 5 | [puppetlabs.puppetdb.generative.overridable-generators :as ogen])) 6 | 7 | (ogen/defgen :test/a ogen/boolean) 8 | (ogen/defgen :test/b ogen/uuid) 9 | (ogen/defgen :test/map (ogen/keys :test/a :test/b)) 10 | 11 | (tc/defspec keys-generator 100 12 | (prop/for-all [m (ogen/convert :test/map)] 13 | (is (= [:a :b] (keys m))))) 14 | 15 | (tc/defspec overrides 100 16 | (prop/for-all [m (->> :test/map 17 | (ogen/override {:test/a (ogen/return true)}) 18 | ogen/convert)] 19 | (is (= [:a :b] (clojure.core/keys m))) 20 | (is (= true (:a m))))) 21 | 22 | (tc/defspec override-presedence-is-outside-in 100 23 | (prop/for-all [m (->> :test/map 24 | (ogen/override {:test/a (ogen/return true)}) 25 | (ogen/override {:test/a (ogen/return false)}) 26 | ogen/convert)] 27 | (is (= [:a :b] (clojure.core/keys m))) 28 | (is (= false (:a m))))) 29 | -------------------------------------------------------------------------------- /acceptance/setup/openvox/configure_openvoxdb.rb: -------------------------------------------------------------------------------- 1 | # This mirrors the structure of 2 | # setup/pre_suite/90_install_devel_puppetdb.rb, but just 3 | # configures rather than trying to install openvox packages. 4 | test_name('configure openvoxdb using the puppetdb module') do 5 | databases.each do 6 | setup_openvoxdb_certs(database) 7 | configure_postgresql_repos_on_el(database) 8 | configure_openvoxdb(database) 9 | 10 | # The package should automatically start the service on debian. 11 | # On redhat, it doesn't. However, during test runs where we're 12 | # doing package upgrades, the redhat package *should* detect that 13 | # the service was running before the upgrade, and it should restart 14 | # it automatically. 15 | # 16 | # That leaves the case where we're on a redhat box and we're 17 | # running the tests as :install only (as opposed to :upgrade). 18 | # In that case we need to start the service ourselves here. 19 | os = test_config[:os_families][database.name] 20 | if test_config[:install_mode] == :install and [:redhat].include?(os) 21 | start_puppetdb(database) 22 | else 23 | # make sure it got started by the package install/upgrade 24 | sleep_until_started(database) 25 | end 26 | end 27 | 28 | configure_openvoxdb_termini(master, databases) 29 | end 30 | -------------------------------------------------------------------------------- /acceptance/tests/smoke.rb: -------------------------------------------------------------------------------- 1 | test_name "test postgresql database restart handling to ensure we recover from a restart" do 2 | step "clear puppetdb database" do 3 | clear_and_restart_puppetdb(database) 4 | end 5 | 6 | with_puppet_running_on master, { 7 | 'master' => { 8 | 'autosign' => 'true' 9 | }} do 10 | 11 | step "Run agents once to activate nodes" do 12 | run_agent_on agents, "--test --server #{master}" 13 | end 14 | end 15 | 16 | step "Verify that the number of active nodes is what we expect" do 17 | check_record_count("nodes", agents.length) 18 | end 19 | 20 | step "Verify that one factset was stored for each node" do 21 | check_record_count("factsets", agents.length) 22 | end 23 | 24 | step "Verify that one catalog was stored for each node" do 25 | check_record_count("catalogs", agents.length) 26 | end 27 | 28 | step "Verify that one report was stored for each node" do 29 | check_record_count("reports", agents.length) 30 | end 31 | 32 | restart_puppetdb(database) 33 | 34 | step "Verify puppetdb can be queried after restarting" do 35 | check_record_count("nodes", agents.length) 36 | end 37 | 38 | restart_postgres(database) 39 | 40 | step "Verify puppetdb can be queried after restarting" do 41 | check_record_count("nodes", agents.length) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /puppet/lib/puppet/face/node/deactivate.rb: -------------------------------------------------------------------------------- 1 | require 'puppet/util/puppetdb' 2 | require 'puppet/util/puppetdb/command_names' 3 | 4 | Puppet::Face.define(:node, '0.0.1') do 5 | 6 | CommandDeactivateNode = Puppet::Util::Puppetdb::CommandNames::CommandDeactivateNode 7 | 8 | action :deactivate do 9 | summary "Deactivate a set of nodes in PuppetDB" 10 | arguments " [ ...]" 11 | description <<-DESC 12 | This will issue '#{CommandDeactivateNode}' commands to the PuppetDB server for 13 | each node specified. The server is found by looking in 14 | $confdir/puppetdb.conf. If any command submissions fail, the process will 15 | be aborted. 16 | DESC 17 | 18 | when_invoked do |*args| 19 | 20 | opts = args.pop 21 | raise ArgumentError, "Please provide at least one node for deactivation" if args.empty? 22 | 23 | Puppet::Node.indirection.terminus_class = :puppetdb 24 | Puppet::Node.indirection.cache_class = nil 25 | 26 | args.inject({}) do |results,node| 27 | results.merge node => Puppet::Node.indirection.destroy(node)['uuid'] 28 | end 29 | end 30 | 31 | when_rendering(:console) do |value| 32 | value.map do |node,uuid| 33 | "Submitted '#{CommandDeactivateNode}' for #{node} with UUID #{uuid}" 34 | end.join("\n") 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test-resources/integration-puppetdb.conf: -------------------------------------------------------------------------------- 1 | global: { 2 | vardir: "" # filled out by the test harness 3 | logging-config: test-resources/logback-test.xml 4 | } 5 | 6 | database: { 7 | # These will be replaced by the test harness 8 | subname: "//localhost:5432/puppetdb" 9 | username: puppetdb 10 | password: puppetdb 11 | } 12 | 13 | command-processing: { 14 | threads: 6 15 | } 16 | 17 | nrepl: { 18 | enabled: false 19 | } 20 | 21 | jetty: { 22 | host: 0.0.0.0 23 | port: 0 24 | 25 | ssl-host: 0.0.0.0 26 | ssl-port: 0 # filled out by the test harness 27 | 28 | # Original settings 29 | 30 | ssl-ca-cert: ./test-resources/puppetserver/ssl/certs/ca.pem 31 | ssl-cert: ./test-resources/puppetserver/ssl/certs/localhost.pem 32 | ssl-key: ./test-resources/puppetserver/ssl/private_keys/localhost.pem 33 | 34 | ssl-protocols: ["TLSv1", "TLSv1.1", "TLSv1.2"] 35 | 36 | acceptor-threads: 4 37 | selector-threads: 4 38 | max-threads: 32 39 | } 40 | 41 | puppetdb: { 42 | } 43 | 44 | web-router-service: { 45 | "puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-webservice": "/metrics" 46 | "puppetlabs.trapperkeeper.services.status.status-service/status-service": "/status" 47 | "puppetlabs.puppetdb.pdb-routing/pdb-routing-service": "/pdb" 48 | "puppetlabs.puppetdb.dashboard/dashboard-redirect-service": "/" 49 | } 50 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/metrics/core.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.metrics.core 2 | (:require [metrics.reporters.jmx :refer [reporter]] 3 | [metrics.core :refer [new-registry]])) 4 | 5 | (defn new-metrics [domain & {:keys [jmx?] :or {jmx? true}}] 6 | (let [registry (new-registry)] 7 | (if-not jmx? 8 | {:registry registry} 9 | {:registry registry 10 | :reporter (reporter registry {:domain domain})}))) 11 | 12 | (def metrics-registries {:admin (new-metrics "puppetlabs.puppetdb.admin") 13 | :benchmark (new-metrics "puppetlabs.puppetdb.benchmark") 14 | :mq (new-metrics "puppetlabs.puppetdb.mq") 15 | :dlo (new-metrics "puppetlabs.puppetdb.dlo") 16 | :http (new-metrics "puppetlabs.puppetdb.http") 17 | :population (new-metrics "puppetlabs.puppetdb.population") 18 | :storage (new-metrics "puppetlabs.puppetdb.storage") 19 | :database (new-metrics "puppetlabs.puppetdb.database")}) 20 | 21 | (defn keyword->metric-name 22 | "Creates a metric name from a keyword. Applies the prefix so that it 23 | can be grouped in a way that is easy to view as a set of JMX MBeans" 24 | [prefix metric-name] 25 | (->> metric-name 26 | (conj prefix) 27 | (mapv name))) 28 | -------------------------------------------------------------------------------- /config/image_templates/ec2.yaml: -------------------------------------------------------------------------------- 1 | AMI: 2 | el-7-x86_64-west: 3 | :image: 4 | :pe: ami-c25448a3 5 | :foss: ami-c25448a3 6 | :region: us-west-2 7 | 8 | el-6-x86_64-west: 9 | :image: 10 | :pe: ami-aa8b039a 11 | :foss: ami-aa8b039a 12 | :region: us-west-2 13 | 14 | el-5-x86_64-west: 15 | :image: 16 | :pe: ami-85693fb5 17 | :foss: ami-85693fb5 18 | :region: us-west-2 19 | 20 | fedora-20-x86_64-west: 21 | :image: 22 | :pe: ami-c0b9daf0 23 | :foss: ami-c0b9daf0 24 | :region: us-west-2 25 | 26 | ubuntu-16.04-amd64-west: 27 | :image: 28 | :pe: ami-7a47b41a 29 | :foss: ami-7a47b41a 30 | :region: us-west-2 31 | 32 | ubuntu-15.10-amd64-west: 33 | :image: 34 | :pe: ami-ddad4dbd 35 | :foss: ami-ddad4dbd 36 | :region: us-west-2 37 | 38 | ubuntu-14.04-amd64-west: 39 | :image: 40 | :pe: ami-be524edf 41 | :foss: ami-be524edf 42 | :region: us-west-2 43 | 44 | ubuntu-12.04-amd64-west: 45 | :image: 46 | :pe: ami-d6e860e6 47 | :foss: ami-d6e860e6 48 | :region: us-west-2 49 | 50 | debian-8-amd64-west: 51 | :image: 52 | :pe: ami-17120727 53 | :foss: ami-17120727 54 | :region: us-west-2 55 | 56 | debian-7-amd64-west: 57 | :image: 58 | :pe: ami-6a574b0b 59 | :foss: ami-6a574b0b 60 | :region: us-west-2 61 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/meta.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.meta 2 | (:require [puppetlabs.puppetdb.middleware :as mid] 3 | [puppetlabs.puppetdb.http :as http] 4 | [puppetlabs.puppetdb.meta.version :as v] 5 | [puppetlabs.puppetdb.time :refer [now]] 6 | [puppetlabs.ring-middleware.core :as rmc] 7 | [puppetlabs.comidi :as cmdi] 8 | [bidi.schema :as bidi-schema] 9 | [puppetlabs.puppetdb.schema :as pls] 10 | [puppetlabs.i18n.core :refer [tru]])) 11 | 12 | (defn current-version-fn 13 | "Returns a function that always returns a JSON object with the running 14 | version of PDB." 15 | [version] 16 | (fn [_] 17 | (if version 18 | (http/json-response {:version version}) 19 | (http/error-response 20 | (tru "Could not find version") 404)))) 21 | 22 | (pls/defn-validated meta-routes :- bidi-schema/RoutePair 23 | [] 24 | (cmdi/context "/v1" 25 | (cmdi/context "/version" 26 | (cmdi/ANY "" [] 27 | (current-version-fn (v/version)))) 28 | (cmdi/ANY "/server-time" [] 29 | (http/json-response {:server_time (now)})))) 30 | 31 | (defn build-app 32 | [] 33 | (-> (meta-routes) 34 | mid/make-pdb-handler 35 | rmc/wrap-accepts-json 36 | mid/validate-no-query-params)) 37 | -------------------------------------------------------------------------------- /puppet/spec/unit/face/node/status_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'spec_helper' 4 | require 'puppet' 5 | require 'puppet/face' 6 | require 'puppet/network/http_pool' 7 | 8 | describe "node face: status" do 9 | let(:subject) { Puppet::Face[:node, :current] } 10 | let(:headers) do 11 | { 12 | "Accept" => "application/json", 13 | "Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8", 14 | } 15 | end 16 | 17 | it "should fail if no node is given" do 18 | expect { subject.status }.to raise_error ArgumentError, /provide at least one node/ 19 | end 20 | 21 | it "should fetch the status of each node" do 22 | http = stub 'http' 23 | Puppet::HTTP::Client.stubs(:new).returns(http) 24 | 25 | nodes = %w[a b c d e] 26 | nodes.each do |node| 27 | http.expects(:get).with do |uri, opts| 28 | headers == opts[:headers] && 29 | uri.path == "/pdb/query/v4/nodes/#{node}" 30 | end 31 | end 32 | 33 | subject.status(*nodes) 34 | end 35 | 36 | it "should CGI escape the node names" do 37 | http = stub 'http' 38 | Puppet::HTTP::Client.stubs(:new).returns(http) 39 | 40 | node = "foo/+*&bar" 41 | 42 | http.expects(:get).with do |uri, opts| 43 | headers == opts[:headers] && 44 | uri.path == "/pdb/query/v4/nodes/foo%2F%2B%2A%26bar" 45 | end 46 | 47 | subject.status(node) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /ext/bin/contributors-in-git-log: -------------------------------------------------------------------------------- 1 | """": # -*-python-*- 2 | command -v python3 > /dev/null && exec python3 "$0" "$@" 3 | command -v python2 > /dev/null && exec python2 "$0" "$@" 4 | echo "error: unable to find python3 or python2" 1>&2; exit 2 5 | """ 6 | 7 | # Given two git tags, prints out a list of contributors who authored commits in 8 | # that commit range. Alphabetically sorted by first name. Excludes CI commits. 9 | # Output is wrapped at 72 characters. 10 | # Ex: contributors-in-git-log 6.0.0..6.27.0 => Bob Ross, Dolly Parton, and John Cena 11 | 12 | from __future__ import print_function 13 | 14 | import os, re, sys 15 | from subprocess import check_output 16 | from textwrap import fill 17 | 18 | if len(sys.argv) != 2: 19 | print('Usage: contributors-in-git-log ', file=sys.stderr) 20 | print(' e.g.: contributors-in-git-log 6.0.0..6.0.1', file=sys.stderr) 21 | sys.exit(2) 22 | 23 | stdout = sys.stdout.buffer 24 | ignore = frozenset([b'Jenkins CI']) 25 | 26 | authors = check_output(('git', 'log', '--pretty=%aN', sys.argv[1])) 27 | authors = sorted(set(a for a in authors.splitlines() if a not in ignore)) 28 | 29 | if len(authors) == 1: 30 | stdout.write(authors[0]) 31 | else: 32 | out = b', '.join(authors[:-1]) + b', and ' + authors[-1] + b'\n' 33 | out = check_output(('fold', '-sw', '72'), input=out) 34 | out = re.sub(br' +$', b'', out, flags=re.MULTILINE) 35 | stdout.write(out) 36 | -------------------------------------------------------------------------------- /resources/puppetlabs/puppetdb/bootstrap.cfg: -------------------------------------------------------------------------------- 1 | # This file is used by the application framework (trapperkeeper) to 2 | # determine what services should be loaded at boot time. 3 | # For more info, see: 4 | # https://github.com/puppetlabs/trapperkeeper/wiki/Bootstrapping 5 | 6 | # Web Server 7 | puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service 8 | 9 | # Webrouting 10 | puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service 11 | 12 | # TK metrics - the authorization service is currently only used by the metrics service 13 | puppetlabs.trapperkeeper.services.authorization.authorization-service/authorization-service 14 | puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-webservice 15 | # TK status 16 | puppetlabs.trapperkeeper.services.status.status-service/status-service 17 | puppetlabs.trapperkeeper.services.scheduler.scheduler-service/scheduler-service 18 | 19 | # PuppetDB Services 20 | puppetlabs.puppetdb.cli.services/puppetdb-service 21 | puppetlabs.puppetdb.command/command-service 22 | puppetlabs.puppetdb.pdb-routing/maint-mode-service 23 | puppetlabs.puppetdb.pdb-routing/pdb-routing-service 24 | puppetlabs.puppetdb.config/config-service 25 | 26 | # NREPL 27 | puppetlabs.trapperkeeper.services.nrepl.nrepl-service/nrepl-service 28 | 29 | # Dashboard redirect for "/" (not "/pdb"): remove to disable 30 | puppetlabs.puppetdb.dashboard/dashboard-redirect-service 31 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/20_install_puppet.rb: -------------------------------------------------------------------------------- 1 | test_name "Install Puppet" do 2 | unless (test_config[:skip_presuite_provisioning]) 3 | if is_el8 && ([:upgrade_latest].include? test_config[:install_mode]) 4 | on(hosts, 'update-crypto-policies --set LEGACY') 5 | 6 | # Work around RedHat Bug 2224427 7 | on(hosts, 'yum install -y tzdata-java') 8 | end 9 | 10 | if (test_config[:skip_openvox_package_installation]) 11 | install_puppet_conf 12 | else 13 | step "Install Puppet" do 14 | install_puppet(puppet_repo_version(test_config[:platform_version], 15 | test_config[:install_mode], 16 | test_config[:nightly])) 17 | end 18 | end 19 | end 20 | 21 | step "Populate facts from each host" do 22 | populate_facts 23 | end 24 | 25 | pidfile = '/var/run/puppet/master.pid' 26 | 27 | master_facts = facts(master.name) 28 | 29 | if options[:type] == 'aio' then 30 | bounce_service( master, master['puppetservice'], 10 ) 31 | else 32 | with_puppet_running_on( 33 | master, 34 | :master => {:dns_alt_names => "puppet,#{master_facts['hostname']},#{master_facts['fqdn']}", 35 | :trace => 'true'}) do 36 | # PID file exists? 37 | step "PID file created?" do 38 | on master, "[ -f #{pidfile} ]" 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/meta_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.meta-test 2 | (:require [puppetlabs.puppetdb.cheshire :as json] 3 | [clojure.test :refer :all] 4 | [puppetlabs.puppetdb.testutils 5 | :refer [assert-success! dotestseq get-request]] 6 | [puppetlabs.puppetdb.meta :as meta] 7 | [puppetlabs.puppetdb.time 8 | :refer [ago seconds in-seconds interval parse-wire-datetime]] 9 | [puppetlabs.puppetdb.middleware :as mid])) 10 | 11 | (def endpoints [[:v1 "/v1"]]) 12 | 13 | (def parsed-body 14 | "Returns clojure data structures from the JSON body of 15 | ring response." 16 | (comp #(json/parse-string % true) :body)) 17 | 18 | (defn with-meta-app 19 | [request] 20 | (let [app (mid/wrap-with-puppetdb-middleware (meta/build-app))] 21 | (app request))) 22 | 23 | (deftest server-time-response 24 | (dotestseq [[_version endpoint] endpoints] 25 | (let [test-time (-> 1 seconds ago) 26 | response (-> (get-request (str endpoint "/server-time")) 27 | with-meta-app)] 28 | (assert-success! response) 29 | (let [server-time (-> response 30 | parsed-body 31 | :server_time 32 | parse-wire-datetime)] 33 | (is (> (in-seconds (interval test-time server-time)) 0)) 34 | (is (> 5 (in-seconds (interval test-time server-time)))))))) 35 | -------------------------------------------------------------------------------- /documentation/api/meta/v1/version.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Version endpoint" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/meta/v1/version.html" 5 | --- 6 | 7 | # Version endpoint 8 | 9 | [curl]: ../../query/curl.markdown#using-curl-from-localhost-non-sslhttp 10 | 11 | The `/version` endpoint can be used to retrieve version information from the PuppetDB server. 12 | 13 | ## `/pdb/meta/v1/version` 14 | 15 | This query endpoint will return version information about the running PuppetDB 16 | server. 17 | 18 | This endpoint does not use any URL parameters or query strings. 19 | 20 | ## `/pdb/meta/v1/version/latest` 21 | 22 | This query will display a message describing the latest version of PuppetDB. 23 | 24 | ### Response format 25 | 26 | The response will be in `application/json`, and will return a JSON map with a 27 | single key: `version`, whose value is a string representation of the version 28 | of the running PuppetDB server. 29 | 30 | {"version": "X.Y.Z"} 31 | 32 | ### Examples 33 | 34 | [Using `curl` from localhost][curl]: 35 | 36 | curl -X GET http://localhost:8080/pdb/meta/v1/version 37 | 38 | {"version": "X.Y.Z"} 39 | 40 | curl -X GET http://localhost:8080/pdb/meta/v1/version/latest 41 | 42 | { 43 | "newer" : false, 44 | "product" : "puppetdb", 45 | "link" : "https://docs.puppetlabs.com/puppetdb/2.3/release_notes.markdown", 46 | "message" : "Version 2.3.4 is now available!", 47 | "version" : "2.3.4" 48 | } 49 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/jdbc/internal.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.jdbc.internal 2 | "JDBC helper functions 3 | 4 | *External code should not call any of these functions directly, as they are* 5 | *subject to change without notice.*" 6 | (:require [puppetlabs.i18n.core :refer [tru]])) 7 | 8 | (defn limit-exception 9 | "Helper method; simply throws an exception with a message explaining 10 | that a query result limit was exceeded." 11 | [limit] 12 | (IllegalStateException. 13 | (tru "Query returns more than the maximum number of results (max: {0})" limit))) 14 | 15 | 16 | (defn limit-result-set! 17 | "Given a `limit` and a `result-set` (which is usually the result of a call to 18 | `clojure.java.jdbc/with-query-results`), this function verifies that the 19 | `result-set` does not contain more than `limit` results and then returns the 20 | results. 21 | 22 | If `limit` is zero, the original `result-set` is returned unmodified. 23 | 24 | Throws an exception if the `result-set` contains more than `limit` results." 25 | [limit result-set] 26 | {:pre [(and (integer? limit) (>= limit 0))]} 27 | (if (pos? limit) 28 | ;; we're doing a `take` with `limit + 1` here, so that we can 29 | ;; correctly identify whether the query *exceeded* the specified 30 | ;; limit. 31 | (let [limited-result-set (take (inc limit) result-set)] 32 | (when (> (count limited-result-set) limit) 33 | (throw (limit-exception limit))) 34 | limited-result-set) 35 | result-set)) 36 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/nio.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.nio 2 | (:require 3 | [me.raynes.fs :refer [delete-dir]] 4 | [puppetlabs.puppetdb.lint :refer [ignore-value]]) 5 | (:import 6 | [java.nio.file Path Files] 7 | [java.nio.file.attribute FileAttribute])) 8 | 9 | (defn create-temp-dir 10 | ([^String prefix] 11 | (Files/createTempDirectory prefix (into-array FileAttribute []))) 12 | ([^Path path ^String prefix] 13 | (ignore-value (Files/createDirectories path (into-array FileAttribute []))) 14 | (Files/createTempDirectory path prefix (into-array FileAttribute [])))) 15 | 16 | (defn resolve-path [^Path path ^String suffix] 17 | (.resolve path suffix)) 18 | 19 | (defn call-with-temp-dir-path 20 | "Calls (f temp-dir-path) after creating the temporary directory 21 | inside the parent path, and then deletes the directory if f doesn't 22 | throw an Exception. Prepends the prefix, if not nil, to the 23 | temporary directory's name." 24 | [parent prefix f] 25 | (let [tempdir (Files/createTempDirectory parent prefix 26 | (make-array FileAttribute 0)) 27 | tempdirstr (str (.toAbsolutePath tempdir)) 28 | result (try 29 | (f (.toAbsolutePath tempdir)) 30 | (catch Exception ex 31 | (binding [*out* *err*] 32 | (println "Error: leaving temp dir" tempdirstr)) 33 | (throw ex)))] 34 | (delete-dir tempdirstr) 35 | result)) 36 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/testutils/tar.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.testutils.tar 2 | (:require [puppetlabs.puppetdb.archive :as archive] 3 | [clojure.string :as str] 4 | [me.raynes.fs :as fs] 5 | [puppetlabs.puppetdb.utils :as utils] 6 | [puppetlabs.puppetdb.time :refer [now]])) 7 | 8 | (defn path 9 | "Creates a platform independent relative path built 10 | from `path-segments`" 11 | [& path-segments] 12 | (str/join java.io.File/separator path-segments)) 13 | 14 | (defn assoc-metadata 15 | "Creates an export/import metadata map with the current 16 | time." 17 | [tar-map] 18 | (assoc tar-map 19 | (path "puppetdb-bak" "export-metadata.json") 20 | {"timestamp" (now) 21 | "command_versions" {"replace_facts" 4 22 | "replace_catalog" 6 23 | "store_report" 5}})) 24 | 25 | (defn tar-entry->map-path 26 | [tar-entry] 27 | (-> (.getName tar-entry) fs/split rest vec)) 28 | 29 | (defn tar->map 30 | "Convert elements in an import/export tarball to a hashmap. 31 | Nested directories convert to nested maps with the files 32 | converted from JSON to clojure data structures" 33 | [tar-file] 34 | (with-open [tar-reader (archive/tarball-reader tar-file)] 35 | (reduce (fn [acc tar-entry] 36 | (assoc-in acc 37 | (tar-entry->map-path tar-entry) 38 | (utils/read-json-content tar-reader))) 39 | {} (archive/all-entries tar-reader)))) 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Leiningen 3 | /target/ 4 | /target-gems/ 5 | /pom.xml 6 | 7 | # Acceptance tests 8 | /junit/ 9 | /log/ 10 | /.beaker 11 | 12 | # Emacs 13 | *# 14 | *~ 15 | .#* 16 | 17 | # Local overide for testing config 18 | /test-resources/config/local.clj 19 | 20 | # Vagrant 21 | /.vagrant/ 22 | 23 | # A generalized temporary area for local use, tmp for 24 | # general temporary items, and scripts for any local 25 | # scripts. 26 | /tmp 27 | /scripts 28 | 29 | # Bundler files 30 | /vendor 31 | /Gemfile.lock 32 | /.vendor 33 | 34 | # Used by Puppet Labs packaging system 35 | /ext/packaging/ 36 | 37 | # VIM swap files 38 | *.swp 39 | 40 | # The leiningen temporary/local files 41 | .lein-failures 42 | .lein-deps-sum 43 | .lein-repl-history 44 | 45 | # lein-checkouts folder 46 | checkouts 47 | 48 | # RVM files for localised setups 49 | .ruby-gemset 50 | .ruby-version 51 | 52 | # clj-i18n 53 | /resources/puppetlabs/puppetdb/*.class 54 | /resources/locales.clj 55 | /mp-* 56 | /dev-resources/i18n/bin 57 | 58 | /.bundle/ 59 | /ext/test-conf/pgbin-requested 60 | /ext/test-conf/pgport-requested 61 | /ext/test-conf/pgver-requested 62 | /ext/test-conf/puppet-ref-requested 63 | /ext/test-conf/puppetserver-dep 64 | /ext/test-conf/puppetserver-ref-requested 65 | /puppet/state/ 66 | /puppetserver/ 67 | 68 | /.clj-kondo/.cache/ 69 | /.eastwood 70 | /.nrepl-port 71 | /pdb.bcpkix 72 | /pdb.bcprov 73 | 74 | # locust output files and python cache 75 | *.csv 76 | *report.html 77 | locust/load-test/__pycache__/ 78 | 79 | # inellij configs 80 | /.idea 81 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/query/regression_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.regression-test 2 | (:require 3 | [clojure.java.io :as io] 4 | [clojure.string :as str] 5 | [clojure.test :refer [deftest is testing]] 6 | [puppetlabs.puppetdb.cheshire :as json] 7 | [puppetlabs.puppetdb.testutils.db :refer [with-test-db]] 8 | [puppetlabs.puppetdb.testutils.http 9 | :refer [query-response with-http-app]] 10 | [puppetlabs.puppetdb.testutils.parse-yaml :refer [parse-yaml]])) 11 | 12 | 13 | (deftest collected-queries-still-working 14 | (with-test-db 15 | (with-http-app 16 | (doseq [file (->> "locust/load-test" 17 | io/as-file 18 | file-seq 19 | (filter #(str/ends-with? % ".yaml"))) 20 | request-data (parse-yaml (slurp file)) 21 | :let [{:keys [alias method path query]} request-data 22 | keyword-method (case method 23 | "GET" :get 24 | "POST" :post) 25 | relative-path (str/replace-first path #"^/pdb/query" "")]] 26 | (testing (str "testing " alias " in " file) 27 | (let [{:keys [status body]} 28 | (query-response keyword-method relative-path query)] 29 | ;; If body of response is not valid JSON, the test should fail 30 | (is (-> body slurp json/parse-string)) 31 | ;; If status of response is not 200, the test should fail 32 | (is (= 200 status)))))))) 33 | 34 | -------------------------------------------------------------------------------- /dev-resources/time-shift-export/input-archive/reports/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "logs" : [ { 3 | "file" : null, 4 | "line" : null, 5 | "tags" : [ "notice" ], 6 | "time" : "2021-05-13T11:11:01.000Z", 7 | "level" : "notice", 8 | "source" : "Puppet", 9 | "message" : "Finished catalog run in 0.35 seconds" 10 | }, { 11 | "file" : null, 12 | "line" : null, 13 | "tags" : [ "notice", "completed_stage", "main" ], 14 | "time" : "2021-05-13T11:10:00.000Z", 15 | "level" : "notice", 16 | "source" : "Stage[main]", 17 | "message" : "Would have triggered 'refresh' from 2 events" 18 | }], 19 | "start_time" : "2021-05-13T11:12:00.000Z", 20 | "certname" : "host-0", 21 | "producer_timestamp" : "2021-05-13T11:12:00.000Z", 22 | "end_time" : "2021-05-13T11:10:00.000Z", 23 | "resources" : [ { 24 | "corrective_change" : null, 25 | "events" : [ { 26 | "new_value" : "bar", 27 | "corrective_change" : null, 28 | "property" : "content", 29 | "name" : null, 30 | "old_value" : "foo", 31 | "status" : "success", 32 | "timestamp" : "2021-05-13T11:13:00.000Z", 33 | "message" : null 34 | } ], 35 | "file" : "/home/wyatt/.puppet/modules/concat/manifests/fragment.pp", 36 | "skipped" : false, 37 | "line" : 123, 38 | "resource_type" : "File", 39 | "resource_title" : "/home/wyatt/.puppet/var/concat/15-default-ssl.conf/fragments/10_default-ssl-docroot", 40 | "timestamp" : "2021-05-13T11:10:00.000Z", 41 | "containment_path" : [ "Stage[main]", "Loadtest", "Notify[foo]" ] 42 | } ] 43 | } 44 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/cli/version.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.cli.version 2 | "Version utility 3 | 4 | This simple command-line tool prints a list of info about 5 | the version of PuppetDB. It is useful for testing and other situations 6 | where you'd like to know some of the version details without having 7 | a running instance of PuppetDB. 8 | 9 | The output is currently formatted like the contents of a java properties file; 10 | each line contains a single property name, followed by an equals sign, followed 11 | by the property value." 12 | (:require 13 | [puppetlabs.puppetdb.cli.util :refer [exit run-cli-cmd]] 14 | [puppetlabs.puppetdb.meta.version :refer [version]] 15 | [puppetlabs.puppetdb.scf.migrate :refer [desired-schema-version]])) 16 | 17 | ;; TODO: Would like to add database info and some other things here, but that 18 | ;; will require us to have access to the configuration info. At present, the 19 | ;; configuration parsing code is scattered throughout services.clj and not 20 | ;; cleanly accessible from here. Perhaps we can revisit this once we've 21 | ;; refactored and cleaned up the configuration stuff a bit. 22 | 23 | (defn show-version [_args] 24 | (doseq [[key val] {"version" (version) 25 | "target_schema_version" (desired-schema-version)}] 26 | (println (format "%s=%s" key val)))) 27 | 28 | (defn cli 29 | "Runs the version command as directed by the command line args and 30 | returns an appropriate exit status.." 31 | [args] 32 | (run-cli-cmd #(do (show-version args) 0))) 33 | 34 | (defn -main [& args] 35 | (exit (cli args))) 36 | -------------------------------------------------------------------------------- /tasks/upload.rake: -------------------------------------------------------------------------------- 1 | namespace :vox do 2 | desc 'Upload artifacts from the output directory to S3. Requires the AWS CLI to be installed and configured appropriately.' 3 | task :upload, [:tag, :platform] do |_, args| 4 | endpoint = ENV.fetch('ENDPOINT_URL') 5 | bucket = ENV.fetch('BUCKET_NAME') 6 | component = 'openvoxdb' 7 | os = nil 8 | arch = nil 9 | if args[:platform] 10 | parts = args[:platform].split('-') 11 | os = parts[0].gsub('fedora','fc') + parts[1] 12 | arch = parts[2] 13 | end 14 | 15 | abort 'You must set the ENDPOINT_URL environment variable to the S3 server you want to upload to.' if endpoint.nil? || endpoint.empty? 16 | abort 'You must set the BUCKET_NAME environment variable to the S3 bucket you are uploading to.' if bucket.nil? || bucket.empty? 17 | abort 'You must provide a tag.' if args[:tag].nil? || args[:tag].empty? 18 | 19 | munged_tag = args[:tag].gsub('-', '.') 20 | s3 = "aws s3 --endpoint-url=#{endpoint}" 21 | 22 | # Ensure the AWS CLI isn't going to fail with the given parameters 23 | run_command("#{s3} ls s3://#{bucket}/") 24 | 25 | glob = "#{__dir__}/../output/**/*#{munged_tag}*" 26 | if os 27 | # "arch" is not used here because it's all noarch 28 | glob += "#{os}*" 29 | end 30 | files = Dir.glob(glob) 31 | abort 'No files for the given tag found in the output directory.' if files.empty? 32 | 33 | path = "s3://#{bucket}/#{component}/#{args[:tag]}" 34 | files.each do |f| 35 | run_command("#{s3} cp #{f} #{path}/#{File.basename(f)}", silent: false) 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /tasks/tag.rake: -------------------------------------------------------------------------------- 1 | def set_version(version) 2 | data = File.read('project.clj') 3 | data = data.sub(/\(def pdb-version "[^"]*"/,"(def pdb-version \"#{version}\"") 4 | File.write('project.clj', data) 5 | run_command("git add project.clj && git commit -m 'Set version to #{version}'", silent: true) 6 | end 7 | 8 | namespace :vox do 9 | desc 'Create tag and push to origin' 10 | task :tag, [:tag] do |_, args| 11 | abort 'You must provide a tag.' if args[:tag].nil? || args[:tag].empty? 12 | abort "#{args[:tag]} does not appear to be a valid version string in x.y.z format" unless Gem::Version.correct?(args[:tag]) 13 | version = Gem::Version.new(args[:tag]) 14 | snapshot_version = Gem::Version.new("#{args[:tag]}.0").bump # bump will only bump the next-to-last number 15 | 16 | # Update project.clj to set the version to the tag 17 | puts "Setting version to #{version}" 18 | set_version(version) 19 | 20 | # Run git command to get short SHA and one line description of the commit on HEAD 21 | branch = run_command('git rev-parse --abbrev-ref HEAD') 22 | sha = run_command('git rev-parse --short HEAD') 23 | msg = run_command('git log -n 1 --pretty=%B') 24 | 25 | puts "Branch: #{branch}" 26 | puts "SHA: #{sha}" 27 | puts "Commit: #{msg}" 28 | 29 | run_command("git tag -a #{args[:tag]} -m '#{args[:tag]}'") 30 | 31 | puts "Setting version after tag to #{snapshot_version}" 32 | set_version("#{snapshot_version}-SNAPSHOT") 33 | 34 | if ENV['NOPUSH'].nil? 35 | puts "Pushing to origin" 36 | run_command("git push origin && git push origin #{args[:tag]}") 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /documentation/api/query/v4/fact-names.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fact-names endpoint" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/query/v4/fact-names.html" 5 | --- 6 | # Fact-names endpoint 7 | 8 | [curl]: ../curl.markdown#using-curl-from-localhost-non-sslhttp 9 | [paging]: ./paging.markdown 10 | [query]: query.markdown 11 | 12 | The `/fact-names` endpoint can be used to retrieve all known fact names. 13 | 14 | ## `/pdb/query/v4/fact-names` 15 | 16 | This will return an alphabetical list of all known fact names, *including* those which are 17 | known only for deactivated nodes. 18 | 19 | ### URL parameters 20 | 21 | * `query`: optional. A JSON array containing the query in prefix notation 22 | (`["", "", ""]`). See the sections below for the 23 | supported operators and fields. For general info about queries, 24 | see [our guide to query structure.][query] 25 | 26 | If a query parameter is not provided, all results will be returned. 27 | 28 | ### Response format 29 | 30 | The response will be in `application/json`, and will contain an alphabetical 31 | JSON array containing fact names. Each fact name will appear only one time, 32 | regardless of how many nodes have that fact. 33 | 34 | [, , ..., , ] 35 | 36 | ### Examples 37 | 38 | [Using `curl` from localhost][curl]: 39 | 40 | curl -X GET http://localhost:8080/pdb/query/v4/fact-names 41 | 42 | ["kernel", "operatingsystem", "osfamily", "uptime"] 43 | 44 | ## Paging 45 | 46 | This query endpoint supports paged results via the common PuppetDB paging 47 | URL parameters. For more information, please see the documentation 48 | on [paging][paging]. 49 | 50 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/75_clean_out_puppet5_repos.rb: -------------------------------------------------------------------------------- 1 | if (test_config[:install_mode] == :upgrade_oldest) \ 2 | && !(test_config[:skip_presuite_provisioning]) 3 | 4 | step "Clean out puppet6 repos to prepare for puppet7 upgrade" do 5 | # skip this step for bullseye beacause it only installs puppet7 in upgrade_oldest 6 | if test_config[:install_mode] == :upgrade_oldest 7 | databases.each do |database| 8 | 9 | # need to remove puppet6 repo to avoid conflicts when upgrading 10 | uninstall_package(database, "puppet7-release") 11 | 12 | databases.each do |host| 13 | os = test_config[:os_families][host.name] 14 | 15 | # On RedHat systems, java 8 is still higher priority than java 8 16 | # To avoid the upgrade failing, we "pre-upgrade" the server to java 11 17 | if os == :redhat 18 | install_package(host, 'java-11-openjdk-headless') 19 | on(database, "alternatives --set java /usr/lib/jvm/java-11-openjdk-*/bin/java") 20 | end 21 | end 22 | 23 | # This is an upgrade oldest test, but we are upgrading now and want the 24 | # latest platform so specify the :install type to get the proper puppet repo 25 | # 26 | # this upgrades puppet-agent and puppetserver packages 27 | install_puppet(puppet_repo_version(test_config[:platform_version], 28 | :install, 29 | test_config[:nightly])) 30 | 31 | on(database, puppet('resource', 'host', 'updates.puppetlabs.com', 'ensure=present', "ip=127.0.0.1") ) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /acceptance/tests/security/puppetdb-ssl-setup/jetty-changes.rb: -------------------------------------------------------------------------------- 1 | test_name "puppetdb ssl-setup jetty.ini changes" do 2 | confd = "#{puppetdb_confdir(database)}/conf.d" 3 | bin_loc = "#{puppetdb_bin_dir(database)}" 4 | 5 | step "backup jetty.ini and setup template" do 6 | on database, "cp #{confd}/jetty.ini #{confd}/jetty.ini.bak.ssl_setup_tests" 7 | end 8 | 9 | step "check to make sure all settings were configured for jetty.ini" do 10 | ["ssl-host", "ssl-port", "ssl-key", "ssl-cert", "ssl-ca-cert"].each do |k| 11 | on database, "grep -e '^#{k} = ' #{confd}/jetty.ini" 12 | end 13 | end 14 | 15 | step "run puppetdb ssl-setup again to make sure it is idempotent" do 16 | on database, "#{bin_loc}/puppetdb ssl-setup" 17 | on database, "diff #{confd}/jetty.ini #{confd}/jetty.ini.bak.ssl_setup_tests" 18 | end 19 | 20 | step "purposely modify jetty.ini ssl-host and make sure puppetdb ssl-setup -f fixes it" do 21 | on database, "sed -i 's/^ssl-host = .*/ssl-host = foobarbaz/' #{confd}/jetty.ini" 22 | on database, "#{bin_loc}/puppetdb ssl-setup -f" 23 | on database, "grep -e '^ssl-host = foobarbaz' #{confd}/jetty.ini", :acceptable_exit_codes => [1] 24 | end 25 | 26 | step "purposely modify jetty.ini ssl-host and make sure puppetdb ssl-setup does not touch it" do 27 | on database, "sed -i 's/^ssl-host = .*/ssl-host = foobarbaz/' #{confd}/jetty.ini" 28 | on database, "#{bin_loc}/puppetdb ssl-setup" 29 | on database, "grep -e '^ssl-host = foobarbaz' #{confd}/jetty.ini" 30 | end 31 | 32 | step "restore original jetty.ini" do 33 | on database, "cp #{confd}/jetty.ini.bak.ssl_setup_tests #{confd}/jetty.ini" 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /documentation/api/wire_format/facts_format_v5.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Facts wire format, version 5" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/wire_format/facts_format_v5.html" 5 | --- 6 | 7 | # Facts wire format - v5 8 | 9 | Facts are represented as JSON. Unless otherwise noted, `null` is not 10 | allowed anywhere in the set of facts. 11 | 12 | {"certname": , 13 | "environment": , 14 | "producer_timestamp": , 15 | "producer": , 16 | "values": { 17 | : , 18 | ... 19 | }, 20 | "package_inventory": [, , ...] 21 | 22 | } 23 | 24 | The `"certname"` key is the certname the facts are associated with. 25 | 26 | The `"environment"` key is the environment associated to the node when the facts were collected. 27 | 28 | The `"values"` key points to a _JSON Object_ that represents the set 29 | of facts. Each key is the fact name, and the value is the fact value. 30 | 31 | The `"producer_timestamp"` key points to a timestamp reflecting 32 | the time of fact set submission from the Server to PuppetDB. 33 | 34 | The `"producer"` key is the certname of the Puppet Server that sent the fact set 35 | to PuppetDB. This field may be `null`. 36 | 37 | Fact names and values **must** be strings. 38 | 39 | The `"package_inventory"` keys is optional, if present, must be an array of . 40 | 41 | ## Encoding 42 | 43 | The entire fact set is expected to be valid JSON, which mandates UTF-8 44 | encoding. 45 | 46 | ### Data type: `` 47 | 48 | An array of three strings: 49 | 50 | [ "", "", "" ] 51 | -------------------------------------------------------------------------------- /resources/ext/config/conf.d/auth.conf: -------------------------------------------------------------------------------- 1 | authorization: { 2 | version: 1 3 | rules: [ 4 | { 5 | # Allow unauthenticated access to the status service endpoint 6 | match-request: { 7 | path: "/status/v1/services" 8 | type: path 9 | method: get 10 | } 11 | allow-unauthenticated: true 12 | sort-order: 500 13 | name: "puppetlabs status service - full" 14 | }, 15 | { 16 | match-request: { 17 | path: "/status/v1/simple" 18 | type: path 19 | method: get 20 | } 21 | allow-unauthenticated: true 22 | sort-order: 500 23 | name: "puppetlabs status service - simple" 24 | }, 25 | { 26 | # Allow nodes to access the metrics service 27 | # for puppetdb, the metrics service is the only 28 | # service using the authentication service 29 | match-request: { 30 | path: "/metrics" 31 | type: path 32 | method: [get, post] 33 | } 34 | allow: "*" 35 | sort-order: 500 36 | name: "puppetlabs puppetdb metrics" 37 | }, 38 | { 39 | # Deny everything else. This ACL is not strictly 40 | # necessary, but illustrates the default policy 41 | match-request: { 42 | path: "/" 43 | type: path 44 | } 45 | deny: "*" 46 | sort-order: 999 47 | name: "puppetlabs deny all" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /test-resources/localhost.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B 3 | AQEFAAOCAg8AMIICCgKCAgEAu3UBmgcP1S+ro+v07e4d2QrNUuOIHmOh4uFpZpmT 4 | J0Gp3UWInFEWtcVBwLbxfNqmYavFPFJkr/QBYdo57E3GAnizpONw1lWRMg1EG17t 5 | QLyaU9eAhyE+i3ZgV1qpFuNiHHrJa/unpvIKaHvxHI9o640gFMgYaj8j4p5y+IQf 6 | tm1xs5uSpgJwHCalx7CkTLfzsXHTes2WV5CTb+AQJ2UWvUS+jkyp3VQOTPZ1GqIX 7 | TTh2XNByGU2XV9fIx9c4dNCeTKmK5hSGWyxFniU5VcoOp7nO/fGNomXx2av00hLc 8 | /4Ql0DMwlLQWaH0i1qTKIwe/OECZlacjI4v/qx6bYtaRlGqKl/nOTZmLgdpM5Mt+ 9 | 4HIF7dLo3T1maO0XG7NOH78vKSEFS0QoM2BV55tql06+kkhwCxhZEV7HMJOXkJ3R 10 | IDbtThQIcUZW85QJ5HJpzCvTXEFJ0oS2S8ElV5ovwVDHMfi9e6Mhq7GMB9jF1o+O 11 | S92YzGLI2f7m42WWJ8SzTrN6C2qc64o319g1dTsabpPqUllxqbnIaSkTbEvKZwdG 12 | tyzBcnxRMgozF2cenyI7svxO4Y2K2BirChl7wRrz8qsfNXUS6YABkD4TW887FbaY 13 | s6M9jDOYHxDmbCIbZB0UhJLpuqNP+59eVI2uxR4qTYIxB6/sIDC1ztMf3P+rMKUy 14 | SgECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCCcP7l8pB3++q5zMY05ICFGtnn 15 | mIccJYLKJPhMxmk9ZU6BxOtInPzhtb+/RH8KahQmXvgylXKUupNG0jr/CjdTMUp+ 16 | MrBYEAzjHNa88CLQCvVgM2PG7geshDTyd+7LxSSKKw3/LegQyHRVwuRYaUcGAaFt 17 | GL69xi0DCa7Ydp3+SGIBwor+uolZ4ejcejBkydPDK9T6vW4n1smwIaEPVKsG4ydt 18 | hbgoiIkAwhxl8rHIB91A/Cc6lrzU2k5PnLcfpu/tU2p8mT3lRjvxlSSi6KK/3KAl 19 | y5Whc0yBfjJqYweqht3DjL6sqqXGNvFu48JFJRQDfEOzCxVK6bvVAjM+l2f45f2t 20 | FFZFIOwRnK+wndpK+bbfM+fj/oDCSPvigisyCZ/TA5EuYvV5CK8ByJvxdpuwN7xy 21 | hJfYv4s/cN5wl9qT3Qi77J3ynOBTvG6seWXe97DI3UEgpEAP6boYH8ZPskGIu9dH 22 | Vgj76HJoFYkjRTLqUgLW5ovWYmNvG9k6QG+uVWfPO/tYjYGXdV8HJUXlY6QNJGeu 23 | rpkspmhFZ19kT0MMJrTIrd/GsN1tvk7sATvpYKcrSa2WkiJydLb2TwkH03U0Fa8U 24 | rvFL+Ge90VGIPdNKcFMKupk0Q2BnA/qj9epZtN5hKovwTSU0Tbw/r0lsHwHLdSJG 25 | KYtJKWhJ+iXcV4ddpw== 26 | -----END CERTIFICATE REQUEST----- 27 | -------------------------------------------------------------------------------- /ext/bin/run-external-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Creates a PuppetDB uberjar and runs tests against it 4 | 5 | set -ueo pipefail 6 | 7 | failures=0 8 | 9 | # Log failed command 10 | failure() { 11 | if test "$TERM"; then 12 | echo "===== $(tput setaf 1)FAILED:$(tput sgr 0) $@" 1>&2 13 | else 14 | echo "===== FAILED: $@" 1>&2 15 | fi 16 | ((failures++)) || true 17 | } 18 | 19 | # Log successful command 20 | success() { 21 | echo "===== OK: $@" 1>&2 22 | } 23 | 24 | # Run a command and log success or failure based on exit status 25 | run() { 26 | echo -e "\n===== testing $@" 1>&2 27 | rc=0 28 | "$@" || rc=$? 29 | if test "$rc" -eq 0; then 30 | success "$@" 31 | else 32 | failure "$@" 33 | fi 34 | } 35 | 36 | # Create a PuppetDB uberjar 37 | run lein uberjar 38 | 39 | unset PDBBOX 40 | export PDB_JAR="$(pwd)/target/puppetdb.jar" 41 | 42 | # Run tests 43 | run ext/test/oom-causes-shutdown 44 | run ext/test/top-level-cli 45 | run ext/test/upgrade-and-exit 46 | run ext/test/database-migration-config 47 | run ext/test/schema-mismatch-causes-pdb-shutdown 48 | 49 | # If there are no failures, say so and exit 50 | if test "$failures" -eq 0; then 51 | echo "failures: $failures" 1>&2 52 | exit 0 53 | fi 54 | 55 | # If there are any failures, print them out 56 | # For some reason github's macos TERM=dumb doesn't work with tput: 57 | # tput: No value for $TERM and no -T specified 58 | # though dumb appears to work fine in Linux. 59 | if tput longname 2>/dev/null 1>&2; then 60 | echo "$(tput setaf 1)failures: $failures$(tput sgr 0)" 1>&2 61 | else 62 | echo "failures: $failures" 1>&2 63 | fi 64 | 65 | exit 2 66 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/cli/pdb_dataset_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.cli.pdb-dataset-test 2 | (:require 3 | [clojure.test :refer [deftest is testing]] 4 | [puppetlabs.puppetdb.cli.pdb-dataset :refer [validate-cli!]] 5 | [puppetlabs.puppetdb.utils :as utils :refer [with-captured-throw]] 6 | [clojure.string :as str] 7 | [puppetlabs.kitchensink.core :as kitchensink]) 8 | (:import 9 | [clojure.lang ExceptionInfo])) 10 | 11 | (defn dataset 12 | [& args] 13 | (with-redefs [utils/try-process-cli (fn [body] (body))] 14 | (validate-cli! args))) 15 | 16 | (deftest returned-error 17 | (testing "when required input parameter is missing" 18 | (let [response (with-captured-throw (dataset))] 19 | (is (= ExceptionInfo (class response))) 20 | (when (= ExceptionInfo (class response)) 21 | (is (= ::kitchensink/cli-error (:kind (ex-data response)))) 22 | (is (str/includes? (:msg (ex-data response)) 23 | "Missing required argument '--dumpfile'!"))))) 24 | 25 | (testing "when provided data is invalid" 26 | (let [response (with-captured-throw (dataset "-t" "invalid-date" "-d" "dumpfile"))] 27 | (is (= ExceptionInfo (class response))) 28 | (when (= ExceptionInfo (class response)) 29 | (is (= ::kitchensink/cli-error (:kind (ex-data response)))) 30 | (is (str/includes? (:msg (ex-data response)) 31 | "Error: time shift date must be in UTC format!")))))) 32 | 33 | (deftest correct-usage 34 | (testing "when the time shift date is provided" 35 | (let [response (dataset "-t" "2021-04-14T18:56" "-d" "dumpfile")] 36 | (is (= {:dumpfile "dumpfile" 37 | :timeshift-to #inst "2021-04-14T18:56:00.000000000-00:00"} response))))) 38 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build openvoxdb 3 | 4 | permissions: 5 | contents: read # minimal required permissions to clone repo 6 | 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | ref: 11 | description: 'Tag to build' 12 | required: true 13 | deb_platform_list: 14 | description: 'A comma-separated list of deb-based platforms to build for, excluding the architecture (e.g. ubuntu-24.04,debian-12). Do not include spaces. If not provided, will use the default list of platforms supported by OpenVox Server and DB.' 15 | required: false 16 | type: string 17 | rpm_platform_list: 18 | description: 'A comma-separated list of rpm-based platforms to build for, excluding the architecture (e.g. el-9,amazon-2023). Do not include spaces. If not provided, will use the default list of platforms supported by OpenVox Server and DB.' 19 | required: false 20 | type: string 21 | ezbake-ref: 22 | description: |- 23 | Branch/tag from ezbake that will be used for openvoxdb/server builds. 24 | type: string 25 | default: 'main' 26 | ezbake-ver: 27 | description: 'The version specified in project.clj in the given ezbake-ref. Will default to the version found in project.clj in this repo if not specified.' 28 | type: string 29 | required: false 30 | jobs: 31 | build: 32 | uses: 'openvoxproject/shared-actions/.github/workflows/build_ezbake.yml@main' 33 | with: 34 | ref: ${{ inputs.ref }} 35 | deb_platform_list: ${{ inputs.deb_platform_list }} 36 | rpm_platform_list: ${{ inputs.rpm_platform_list }} 37 | ezbake-ref: ${{ inputs.ezbake-ref }} 38 | ezbake-ver: ${{ inputs.ezbake-ver }} 39 | secrets: inherit 40 | -------------------------------------------------------------------------------- /test-resources/puppetserver/bootstrap-7.x.cfg: -------------------------------------------------------------------------------- 1 | puppetlabs.services.request-handler.request-handler-service/request-handler-service 2 | puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service 3 | puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service 4 | puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service 5 | puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service 6 | puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service 7 | puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service 8 | puppetlabs.services.master.master-service/master-service 9 | puppetlabs.services.legacy-routes.legacy-routes-service/legacy-routes-service 10 | puppetlabs.services.puppet-admin.puppet-admin-service/puppet-admin-service 11 | puppetlabs.trapperkeeper.services.authorization.authorization-service/authorization-service 12 | puppetlabs.services.versioned-code-service.versioned-code-service/versioned-code-service 13 | puppetlabs.trapperkeeper.services.scheduler.scheduler-service/scheduler-service 14 | puppetlabs.trapperkeeper.services.status.status-service/status-service 15 | puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-service 16 | puppetlabs.services.jruby.jruby-metrics-service/jruby-metrics-service 17 | puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service 18 | # To enable the CA service, leave the following line uncommented 19 | puppetlabs.services.ca.certificate-authority-service/certificate-authority-service 20 | # To disable the CA service, comment out the above line and uncomment the line below 21 | #puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service 22 | -------------------------------------------------------------------------------- /test-resources/puppetserver/bootstrap.cfg: -------------------------------------------------------------------------------- 1 | puppetlabs.services.request-handler.request-handler-service/request-handler-service 2 | puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service 3 | puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service 4 | puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service 5 | puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service 6 | puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service 7 | puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service 8 | puppetlabs.services.master.master-service/master-service 9 | puppetlabs.services.legacy-routes.legacy-routes-service/legacy-routes-service 10 | puppetlabs.services.puppet-admin.puppet-admin-service/puppet-admin-service 11 | puppetlabs.trapperkeeper.services.authorization.authorization-service/authorization-service 12 | puppetlabs.services.versioned-code-service.versioned-code-service/versioned-code-service 13 | puppetlabs.trapperkeeper.services.scheduler.scheduler-service/scheduler-service 14 | puppetlabs.trapperkeeper.services.status.status-service/status-service 15 | puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-service 16 | puppetlabs.services.jruby.jruby-metrics-service/jruby-metrics-service 17 | puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service 18 | # To enable the CA service, leave the following line uncommented 19 | puppetlabs.services.ca.certificate-authority-service/certificate-authority-service 20 | # To disable the CA service, comment out the above line and uncomment the line below 21 | #puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service 22 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/query/facts.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.facts 2 | "Fact query generation" 3 | (:require [puppetlabs.puppetdb.cheshire :as json] 4 | [puppetlabs.puppetdb.scf.storage-utils :as sutils] 5 | [puppetlabs.puppetdb.facts :as facts] 6 | [puppetlabs.puppetdb.query :as query] 7 | [puppetlabs.puppetdb.utils :as utils] 8 | [puppetlabs.puppetdb.schema :as pls] 9 | [schema.core :as s])) 10 | 11 | ;; SCHEMA 12 | 13 | (def row-schema 14 | (query/wrap-with-supported-fns 15 | {(s/optional-key :certname) s/Str 16 | (s/optional-key :environment) (s/maybe s/Str) 17 | (s/optional-key :name) s/Str 18 | (s/optional-key :value) s/Str})) 19 | 20 | (def converted-row-schema 21 | (query/wrap-with-supported-fns 22 | {(s/optional-key :certname) s/Str 23 | (s/optional-key :environment) (s/maybe s/Str) 24 | (s/optional-key :name) s/Str 25 | (s/optional-key :value) s/Any})) 26 | 27 | ;; MUNGE 28 | 29 | (pls/defn-validated deserialize-fact-value :- converted-row-schema 30 | "Coerce values for each row to the proper stored type." 31 | [row :- row-schema] 32 | (utils/update-when row [:value] json/parse-string)) 33 | 34 | (defn munge-result-rows 35 | [_ _] 36 | (fn [rows] 37 | (if (empty? rows) 38 | [] 39 | (map #(utils/update-when % [:value] sutils/parse-db-json) rows)))) 40 | 41 | (defn munge-path-result-rows 42 | [_ _] 43 | (fn [rows] 44 | (map #(utils/update-when % [:path] facts/string-to-factpath) rows))) 45 | 46 | (defn munge-name-result-rows 47 | [_ _] 48 | (fn [rows] 49 | (map :name rows))) 50 | 51 | (defn munge-package-inventory 52 | [_ _] 53 | (fn [rows] 54 | (map #(utils/update-when % [:package_inventory] (partial map vec)) rows))) 55 | -------------------------------------------------------------------------------- /locust/run-load-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, subprocess, os 4 | from datetime import datetime 5 | 6 | def usage(): 7 | usage = """ 8 | This script only adds defaults if there are no options 9 | Usage: run-load-test [options] 10 | 11 | Defaults: 12 | -H [--host] http://localhost:8080 13 | -u [--users] 1 14 | -s [--spawn_rate] 1 15 | -t [--run_time] 1m 16 | -f [--locustfile] locust/load-test/load-test.py 17 | -T [--tags] example 18 | -c [--csv] 19 | --html .html 20 | """ 21 | print(usage) 22 | 23 | def add_default_args(program_args): 24 | defaults = [['--headless'], ['-H', 'http://localhost:8080'], 25 | ["-u", '1'], ["-r", '1'], ["-t", '1m'], ['-f', locust_filepath], 26 | ['--html', f'{timestamp}.html'], ['--tags', 'example'], ['--csv', timestamp]] 27 | additional_args = [] 28 | for default_arg in defaults: 29 | found = False 30 | for arg in program_args: 31 | if arg == default_arg[0]: 32 | found = True 33 | if not found: 34 | additional_args += default_arg 35 | 36 | return additional_args + program_args 37 | 38 | subprocess.run([sys.executable, "-m", "pip", "install", "locust", "pyyaml", "-U"]) 39 | 40 | missing_defaults = [] 41 | dir_path = os.path.dirname(os.path.realpath(__file__)) 42 | workdir = os.getcwd() 43 | locust_filepath = os.path.join(dir_path, 'load-test', 'load-test.py') 44 | timestamp = datetime.now().strftime("%d-%m-%YT%H:%M:%S") 45 | 46 | sys_args = sys.argv[1:] 47 | if '--help' in sys_args: 48 | subprocess.run(["locust", "--help"]) 49 | usage() 50 | exit() 51 | 52 | locust_args = add_default_args(sys_args) 53 | subprocess.run(["locust"] + locust_args) 54 | -------------------------------------------------------------------------------- /ext/bin/pdbbox-env: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Runs a command using an existing PostgreSQL sandbox inside a PuppetDB sandbox 4 | # Requires pgbox to already be installed and on PATH 5 | # Sets up PuppetDB environment variables for PostgreSQL roles and passwords used for testing 6 | # PDBBOX environment variable must be set and contain the path to your PuppetDB sandbox 7 | # You should first create a PuppetDB sandbox with ext/bin/pdbbox-init 8 | 9 | set -ueo pipefail 10 | 11 | usage() { echo 'Usage: PDBBOX=BOX_DIR pdbbox-env CMD [ARG...]'; } 12 | 13 | # PDBBOX must be set to an existing PuppetDB sandbox directory 14 | # CMD must be at least one token 15 | if test "$#" -lt 1 -o -z "$PDBBOX"; then 16 | usage 1>&2 17 | exit 1 18 | fi 19 | 20 | pgport="$(cat "$PDBBOX/pg/port")" 21 | pghost="$(tail -1 "$PDBBOX/pg/bind-addrs")" 22 | 23 | # Set PGBOX to let pgbox command to know where PostgreSQL sandbox is located 24 | # PostgreSQL sandbox is expected to be in PuppetDB sandbox created with ext/bin/pdbbox-init 25 | export PGBOX="$PDBBOX/pg" 26 | # All environment variables below are loaded by puppetlabs.puppetdb.testutils.db/test-env 27 | export PDB_TEST_DB_HOST="$pghost" 28 | export PDB_TEST_DB_PORT="$pgport" 29 | export PDB_TEST_DB_USER=pdb_test 30 | # The passwords are read from files in the PuppetDB sandbox 31 | export PDB_TEST_DB_USER_PASSWORD="$(cat "$PDBBOX/test-pass")" 32 | export PDB_TEST_DB_READ=pdb_test_read 33 | export PDB_TEST_DB_READ_PASSWORD="$(cat "$PDBBOX/test-pass-read")" 34 | export PDB_TEST_DB_MIGRATOR=pdb_test_migrator 35 | export PDB_TEST_DB_MIGRATOR_PASSWORD="$(cat "$PDBBOX/test-pass-migrator")" 36 | export PDB_TEST_DB_ADMIN=pdb_test_admin 37 | export PDB_TEST_DB_ADMIN_PASSWORD="$(cat "$PDBBOX/test-pass-admin")" 38 | 39 | # Run provided command using PostgreSQL sandbox 40 | exec pgbox env "$@" 41 | -------------------------------------------------------------------------------- /puppet/spec/unit/indirector/node/puppetdb_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rspec 2 | 3 | require 'spec_helper' 4 | 5 | require 'puppet/indirector/node/puppetdb' 6 | require 'puppet/util/puppetdb/command_names' 7 | require 'json' 8 | require 'date' 9 | require 'time' 10 | 11 | describe Puppet::Node::Puppetdb do 12 | 13 | before :each do 14 | Puppet::Node.indirection.stubs(:terminus).returns(subject) 15 | end 16 | 17 | let(:node) { "something.example.com" } 18 | let(:producer_timestamp) { Puppet::Util::Puppetdb.to_wire_time(Time.now) } 19 | 20 | def destroy 21 | Puppet::Node.indirection.destroy(node) 22 | end 23 | 24 | describe "#destroy" do 25 | let(:nethttpok) { Net::HTTPOK.new('1.1', 200, 'OK') } 26 | let(:responseok) { create_http_response("mock url", nethttpok) } 27 | let(:http) { mock 'http' } 28 | before :each do 29 | Puppet::HTTP::Client.expects(:new).returns http 30 | end 31 | 32 | it "should POST a '#{Puppet::Util::Puppetdb::CommandNames::CommandDeactivateNode}' command" do 33 | responseok.stubs(:body).returns '{"uuid": "a UUID"}' 34 | http.expects(:post).with do |uri,body,headers| 35 | req = JSON.parse(body) 36 | req["certname"] == node && 37 | extract_producer_timestamp(req) <= Time.now.to_i 38 | end.returns responseok 39 | 40 | destroy 41 | end 42 | 43 | it "should log a deprecation warning if one is returned from PuppetDB" do 44 | nethttpok['x-deprecation'] = 'A horrible deprecation warning!' 45 | responseok.stubs(:body).returns '{"uuid": "a UUID"}' 46 | 47 | Puppet.expects(:deprecation_warning).with do |msg| 48 | msg =~ /A horrible deprecation warning!/ 49 | end 50 | 51 | http.stubs(:post).returns responseok 52 | 53 | destroy 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.core-test 2 | (:require 3 | [clojure.test :refer :all] 4 | [puppetlabs.puppetdb.cli.util :as cliu] 5 | [puppetlabs.puppetdb.core :as core] 6 | [puppetlabs.trapperkeeper.logging :refer [root-logger]] 7 | [puppetlabs.trapperkeeper.testutils.logging 8 | :refer [with-log-level with-logged-event-maps]])) 9 | 10 | (deftest handling-of-invalid-jdk-versions 11 | (let [logs? #{"services" "upgrade"}] 12 | (letfn [(check [subcommand version] 13 | (let [err (java.io.StringWriter.) 14 | [result err log] 15 | (binding [*err* err] 16 | (with-log-level (root-logger) :error 17 | (with-logged-event-maps log 18 | ;; Relies on clj's left-to-right arg eval order 19 | [(with-redefs [cliu/java-version (constantly version)] 20 | (core/run-subcommand subcommand [])) 21 | (str err) @log])))] 22 | (is (= cliu/err-exit-status result)) 23 | ;; Right now the output may also include clojure warning lines 24 | (is (re-find #"(?s)(?:^|\n)error: PuppetDB doesn't support.*" err)) 25 | (if-not (logs? subcommand) 26 | (is (= [] log)) 27 | (do 28 | (is (= 1 (count log))) 29 | (let [[{:keys [logger level message]}] log] 30 | (is (= "puppetlabs.puppetdb.cli.tk-util" logger)) 31 | (is (= :error level)) 32 | (is (re-find #"PuppetDB doesn't support.*" message)))))))] 33 | (doseq [cmd ["help" "version" "benchmark" "services" "upgrade"]] 34 | (check cmd "1.5.0"))))) 35 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/command/constants.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.command.constants 2 | (:require [clojure.set :as set] 3 | [clojure.string :as str])) 4 | 5 | (def command-names 6 | {:configure-expiration "configure expiration" 7 | :replace-catalog "replace catalog" 8 | :replace-catalog-inputs "replace catalog inputs" 9 | :replace-facts "replace facts" 10 | :deactivate-node "deactivate node" 11 | :store-report "store report"}) 12 | 13 | (def command-keys (set/map-invert command-names)) 14 | 15 | (def supported-command-versions 16 | {"configure expiration" #{1} 17 | "replace facts" #{2 3 4 5} 18 | "replace catalog" #{4 5 6 7 8 9} 19 | "replace catalog inputs" #{1} 20 | "store report" #{3 4 5 6 7 8} 21 | "deactivate node" #{1 2 3}}) 22 | 23 | (def latest-catalog-version (apply max (supported-command-versions "replace catalog"))) 24 | (def latest-report-version (apply max (supported-command-versions "store report"))) 25 | (def latest-facts-version (apply max (supported-command-versions "replace facts"))) 26 | (def latest-configure-expiration-version (apply max (supported-command-versions "configure expiration"))) 27 | (def latest-catalog-inputs-version (apply max (supported-command-versions "replace catalog inputs"))) 28 | (def latest-deactivate-node-version (apply max (supported-command-versions "deactivate node"))) 29 | 30 | (def latest-command-versions 31 | {:replace_catalog latest-catalog-version 32 | :store_report latest-report-version 33 | :replace_facts latest-facts-version 34 | :configure_expiration latest-configure-expiration-version 35 | :replace_catalog_inputs latest-catalog-inputs-version}) 36 | 37 | (defn normalize-command-name 38 | "Normalize command name from an incoming request's query param" 39 | [command] 40 | (str/replace command "_" " ")) 41 | -------------------------------------------------------------------------------- /ext/bin/require-pgbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Installs pgbox onto machine 4 | # Supports at least Debian and MacOS 5 | 6 | set -uexo pipefail 7 | 8 | script_home="$(cd "$(dirname "$0")" && pwd)" 9 | 10 | default=0.0.0 11 | 12 | cmdname="$(basename "$0")" 13 | 14 | usage() { echo "Usage: $cmdname VERSION INSTALLDIR_IF_NEEDED"; } 15 | 16 | misuse() { usage 1>&2; exit 2; } 17 | 18 | # Hashmap of pgbox checksums 19 | declare -A known_hash 20 | known_hash[0.0.0]=c4dd424ddbcaf33cb0d3ef51255943cbdd87446ea7bf220528441849de35cfef 21 | 22 | # Verify two arguments were given 23 | test "$#" -eq 2 || misuse 24 | 25 | ver="$1" 26 | if test "$ver" = default; then 27 | ver="$default" 28 | fi 29 | 30 | install="$2" 31 | hash="${known_hash[$ver]}" 32 | 33 | # Verify checksum is known for requested version 34 | if ! test "$hash"; then 35 | echo "$cmdname: don't know sha256sum for $ver" 1>&2 36 | exit 2 37 | fi 38 | 39 | # Exit if installed version is same as requested 40 | if command -v pgbox; then 41 | curver="$(pgbox version | cut -d' ' -f2)" 42 | if test "$curver" = "$ver"; then 43 | exit 0 44 | fi 45 | fi 46 | 47 | # Create temporary directory 48 | tmpdir="$(mktemp -d "$cmdname-XXXXXX")" 49 | tmpdir="$(cd "$tmpdir" && pwd)" 50 | trap "$(printf 'rm -rf %q' "$tmpdir")" EXIT 51 | 52 | # Download pgbox from GitLab releases 53 | cd "$tmpdir" 54 | curl -O "https://gitlab.com/pgbox-org/pgbox/raw/82e512d37a7c6c2ae72d1293ea98945eaaae51f4/pgbox" 55 | obshash="$("$script_home/sha256sum" < pgbox | cut -d' ' -f1)" 56 | cd .. 57 | 58 | # Verify checksum of pgbox executable 59 | if test "$obshash" != "$hash"; then 60 | echo "$cmdname: sha256sum $obshash != $hash" 1>&2 61 | exit 2 62 | fi 63 | 64 | # Install pgbox 65 | mkdir -p "$install/bin" 66 | mv -i "$tmpdir/pgbox" "$install/bin" 67 | chmod +x "$install/bin/pgbox" 68 | -------------------------------------------------------------------------------- /test-resources/puppetlabs/puppetdb/ssl/private_keys/keyonly.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3986OFH0Vkw3i 3 | bJ8BicJzJjqEuYwZYrm9QPBN1fTOgEXHQrGVL8L1XA6QT1XknBT4o3zw+PuQuvUy 4 | Gm9pyTl4/wkt5llBPg6tfL7JhgFpKvDaZzxa51lKvnRUVYuLwNL+hKPvvsPOLdcF 5 | +/x3+cGzc+M7FpPLm/V+VGqiCoc9LnDOh9iSUD5ONrKjzHVzNT0IFSSGNEhYMIcG 6 | Ogf0hmFCnd1w9mcghnm+qMKN5LVdT6maPtpTAKaDYVvX0UiZldWP172r2fu22sNN 7 | B5JNMJ4LFQ7pOO6psyMZmPidhCBHlueya2Vq3PURH9G3heYhQyX/AYnXaT7wTZk4 8 | Jfr+oVCnAgMBAAECggEAWQHQMfW/vxxy70XWeIwKRGQOlAChw/Z8HxC4MzB9TRvK 9 | pumhuahuDwAHG9MGn6DUlKek34HXVOLfluorVWdCI0RhUI/ORz6bI1zjgeUP8a4Q 10 | 8dpY3TJphTw4VEU/StJ0QygxmOEXIz4SdpbAQ9vW19gN7JyzzSMb149IODYX0IVA 11 | 8bJI1oV9ig2vp+JZyOVQLpXn5EdKlHtVjInSGOMPyFNZ1QkcyQFoozSw/XJ8vyk3 12 | 4bskjaTyF85cgkTgwy674iVnKu+3wVpcyzq8elJDEzKz6CH6nQO6/xTHv+tm6yQu 13 | f3HuXEaIysGw0i6qbEtP5VSx6DCANcwdWUwtTVrboQKBgQDqxh23m/2uh2U9d5xW 14 | BhUWIGXBIBgEYxdXayh69796Bg2HHtOt5VeRPXHHLGaieYC9qUUHm2ppJBx2LdDQ 15 | M7UGoShmyUrnA8HDrjn6BHkmeFGjDdsw5Y4t2QRh3B0rEFIwYoSonYpnWDnJh6Qe 16 | SU/adCN+j8z59HcIqLC20bLU7QKBgQDImcd45OWZTqfptiJ7HfJApQtX8uv+fk2V 17 | dtbJEOMfmIOH9GSGguygUjccC7jVGMm63KNaDs1/gW7o8/pPUZGhGV48oG2qmlfD 18 | FlRc6vmxNSBv73h1h/XfTS1aKjzSm5U+66e3S0v9ObO4Y0ybSQhI/GkkuIaIK6G0 19 | hnfDqZ69YwKBgHkK7fVlUpSyL+tSCON9PU/sIipBHsDcSgODNxq3Mxx5lG3u4dpN 20 | XSl/0XKMHNp15H9kjzeN1H4i5R21H2zIy/OAEXF1JM4YbMUzxaZ2ufOwov57PWBn 21 | ajePJShDMTKrtoRFtvmsR9hib2DNMzt+NtJ9gHNXNqpEdT6cooePdDE9AoGABG0Q 22 | L6StRDKuFcQFAr5oZ/C8TVZ4yoay44dZudn7iOjujgNgbG9bFTZ7LM09aMZBPTQ9 23 | DnGhKx0J+23WqgVctzc+EwxfHxKEuTM27U3p8HUBoDaia8VyMVkclQ61hNgV9Oty 24 | KeMpbA7n3juipxJ3clTPZRYFMd/0k20cG18Ut40CgYEAngAQc4wZjvQSLlcmHw2V 25 | xWa5Reug51D609MRXfHJOXtyVmlAFAOcTe/P/xr+0R7uAz9JHiSLBiXzxROmeczW 26 | HjO5uOTfh+UjUl8Dfy5R4mq3XVLtdMTfBG8lxPsKUMUvOEwc+JHh0JriwN2+aVqv 27 | CRMCwm+RRhWwCqOoqKfNBio= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/http/sync_version_checking.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.http.sync-version-checking 2 | (:require [clojure.test :refer :all] 3 | [puppetlabs.puppetdb.constants :as constants] 4 | [puppetlabs.puppetdb.http :as http] 5 | [puppetlabs.puppetdb.testutils.http 6 | :refer [deftest-http-app query-response *app*]] 7 | [puppetlabs.puppetdb.testutils :refer [query-request]])) 8 | 9 | (def endpoints [[:v4 "/v4"]]) 10 | 11 | (deftest-http-app sync-version-checking-headers 12 | [[_version endpoint] endpoints 13 | method [:get :post]] 14 | (let [req-with-sync-ver (fn [ver] 15 | (*app* 16 | (query-request 17 | method endpoint 18 | ["from" "nodes"] 19 | {:headers {"x-pdb-sync-ver" ver}}))) 20 | sync-ver constants/pdb-sync-ver] 21 | 22 | (testing "should succeed when sync versions are equal" 23 | (is (= 200 (:status (req-with-sync-ver (str sync-ver)))))) 24 | 25 | (testing "should succed when sync version missing" 26 | (is (= 200 (:status (query-response method endpoint ["from" "nodes"]))))) 27 | 28 | (testing "should fail when sync versions mismatched" 29 | (is (= (req-with-sync-ver (str (inc sync-ver))) 30 | {:status 409 31 | :headers {"Content-Type" http/error-response-content-type} 32 | :body "PDB sync request version 3 too new for this server (expected 2)."}))) 33 | 34 | (testing "should fail when x-pdb-sync-ver input is invalid" 35 | (is (= (req-with-sync-ver "abc") 36 | {:status 400 37 | :headers {"Content-Type" http/error-response-content-type} 38 | :body 39 | "The x-pdb-sync-ver header: abc cannot be converted to an int."}))))) 40 | -------------------------------------------------------------------------------- /ci/bin/prep-and-run-in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Prepares a CI machine for tests and runs tests 4 | # Ex: prep-and-run-in github core/openjdk8/pg-13 5 | 6 | set -uexo pipefail 7 | 8 | usage() { echo 'Usage: $(basename "$0") HOST_TYPE PDB_TEST_SPEC'; } 9 | misuse() { usage 1>&2; exit 2; } 10 | 11 | # Validate arguments 12 | test $# -eq 2 || misuse 13 | host_type="$1" 14 | spec="$2" 15 | 16 | # If machine has apt-get, prepare a Debian environment 17 | if command -v apt-get > /dev/null; then 18 | cmd=$(printf 'cd %q && ext/bin/prep-debianish-root --for %q --install ci/local' \ 19 | "$(pwd)" "$spec") 20 | sudo -i /bin/sh -c "$cmd" 21 | ext/bin/prep-debianish --for "$spec" --install ci/local 22 | # If machine is running MacOS, prepare a MacOS environment 23 | elif [[ "$OSTYPE" == darwin* ]]; then 24 | ext/bin/prep-macos --for "$spec" --install ci/local 25 | # If machine is not MacOS or Debian-like, abort 26 | else 27 | echo "Don't know how to run tests on $OSTYPE yet" 1>&2 28 | exit 2 29 | fi 30 | 31 | # If running on a MacOS instance on GitHub... 32 | if [ "$host_type" = github ] && [[ "$OSTYPE" != darwin* ]]; then 33 | # The current ubuntu image sets this to point to /usr/local/... 34 | # which causes chaos with respect to some built-in lein 35 | # auto-install behaviors when it discovers we don't have write 36 | # access there. Since as far as I know there's no reason it 37 | # should be set, unset it. 38 | mkdir -p ci/local/etc 39 | echo 'unset LEIN_HOME' >> ci/local/etc/pdb-test-env 40 | fi 41 | 42 | # The pdb-test-env script gets created by prep-macos or prep-debianish 43 | # It contains environment variables that should be loaded before running tests 44 | if test -e ci/local/etc/pdb-test-env; then 45 | cat ci/local/etc/pdb-test-env 1>&2 46 | source ci/local/etc/pdb-test-env 47 | fi 48 | 49 | # Run the requested tests 50 | ci/bin/run "$spec" 51 | -------------------------------------------------------------------------------- /documentation/api/admin/v1/archive.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Archive endpoint" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/admin/v1/archive.html" 5 | --- 6 | # Archive endpoint 7 | 8 | [curl]: ../../query/curl.markdown#using-curl-from-localhost-non-sslhttp 9 | 10 | The `/archive` endpoint can be used for importing and exporting PuppetDB 11 | archives. 12 | 13 | ## `POST /pdb/admin/v1/archive` 14 | 15 | This endpoint can be used for streaming a PuppetDB archive into PuppetDB. 16 | 17 | ### Request format 18 | 19 | The request should be a multipart POST and have `Content-Type: multipart/mixed`. 20 | 21 | ### URL parameters 22 | 23 | * `archive`: required. The archive file to import to the PuppetDB. This archive 24 | must have a file called `puppetdb-bak/metadata.json` as the first entry in the 25 | tarfile with a key `command_versions` which is a JSON object mapping PuppetDB 26 | command names to their version. 27 | 28 | ### Response format 29 | 30 | The response will be in `application/json`, and will return a JSON map upon 31 | successful completion of the importation: 32 | 33 | {"ok": true} 34 | 35 | ### Example 36 | 37 | [Using `curl` from localhost][curl]: 38 | 39 | curl -X POST http://localhost:8080/pdb/admin/v1/archive \ 40 | -F "archive=@example_backup_archive.tgz" 41 | 42 | {"ok": true} 43 | 44 | ## `GET /pdb/admin/v1/archive` 45 | 46 | This endpoint can be used to stream a tarred, gzipped backup archive of PuppetDB 47 | to your local machine. 48 | 49 | ### URL parameters 50 | 51 | * `anonymization_profile`: optional. The level of anonymization applied to the 52 | archive files. 53 | 54 | ### Response format 55 | 56 | The response will be a `application/octet-stream`, and will return a `tar.gz` 57 | archive. 58 | 59 | ### Example 60 | 61 | [Using `curl` from localhost][curl]: 62 | 63 | curl -X GET http://localhost:8080/pdb/admin/v1/archive -o puppetdb-export.tgz 64 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/factsets.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.factsets 2 | (:require 3 | [clojure.set :as set] 4 | [puppetlabs.puppetdb.schema :as pls] 5 | [schema.core :as s])) 6 | 7 | ;; SCHEMA 8 | 9 | (def fact-query-schema 10 | "Schema for a single fact map" 11 | {:name s/Str 12 | :value s/Any 13 | (s/optional-key :environment) s/Str 14 | (s/optional-key :certname) s/Str}) 15 | 16 | (def facts-expanded-query-schema 17 | "Facts expanded data format." 18 | {(s/optional-key :data) [fact-query-schema] 19 | :href String}) 20 | 21 | (def factset-query-schema 22 | "Final schema for a single factset." 23 | {(s/optional-key :certname) String 24 | (s/optional-key :environment) (s/maybe s/Str) 25 | (s/optional-key :timestamp) pls/Timestamp 26 | (s/optional-key :producer_timestamp) (s/maybe pls/Timestamp) 27 | (s/optional-key :hash) (s/maybe s/Str) 28 | (s/optional-key :facts) facts-expanded-query-schema}) 29 | 30 | (def facts-wireformat-schema 31 | {:certname s/Str 32 | :values {s/Keyword s/Any} 33 | :environment (s/maybe s/Str) 34 | :producer_timestamp (s/cond-pre pls/Timestamp (s/maybe s/Str))}) 35 | 36 | ;; TRANSFORMATIONS 37 | 38 | (pls/defn-validated fact-query->wire-v5 39 | [fact :- fact-query-schema] 40 | (-> fact 41 | (dissoc :environment :certname))) 42 | 43 | (pls/defn-validated facts-list-to-map :- {s/Keyword s/Any} 44 | [facts :- [fact-query-schema]] 45 | (zipmap (map (comp keyword :name) facts) 46 | (map :value facts))) 47 | 48 | (pls/defn-validated facts-expanded->wire-v5 :- {s/Keyword s/Any} 49 | [facts :- facts-expanded-query-schema] 50 | (facts-list-to-map 51 | (map fact-query->wire-v5 (:data facts)))) 52 | 53 | (defn factsets-query->wire-v5 [factsets] 54 | (map 55 | #(-> % 56 | (dissoc :hash :timestamp) 57 | (update :facts facts-expanded->wire-v5) 58 | (set/rename-keys {:facts :values})) 59 | factsets)) 60 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | 2 | {:hooks 3 | {:analyze-call 4 | {clojure.test.check.clojure-test/defspec hooks/defspec 5 | murphy/with-final hooks/with-final 6 | puppetlabs.puppetdb.command-test/with-message-handler hooks/one-binding-then-body 7 | puppetlabs.puppetdb.http.explore-test/check-json-response hooks/check-json-response 8 | puppetlabs.puppetdb.jdbc-test/deftest-antonyms hooks/deftest-antonyms 9 | puppetlabs.puppetdb.testutils.http/deftest-http-app hooks/deftest-http-app 10 | puppetlabs.puppetdb.testutils.queue/with-stockpile hooks/one-binding-then-body 11 | puppetlabs.puppetdb.testutils/with-coordinated-fn hooks/with-coordinated-fn 12 | puppetlabs.trapperkeeper.core/defservice hooks/defservice 13 | puppetlabs.trapperkeeper.core/service hooks/service 14 | puppetlabs.trapperkeeper.services/defservice hooks/defservice 15 | puppetlabs.trapperkeeper.testutils.logging/with-log-output hooks/one-binding-then-body 16 | puppetlabs.trapperkeeper.testutils.logging/with-logged-event-maps hooks/one-binding-then-body 17 | puppetlabs.trapperkeeper.testutils.webserver/with-test-webserver hooks/with-test-webserver}} 18 | :lint-as 19 | {murphy/try! clojure.core/try 20 | puppetlabs.puppetdb.schema/defn-validated schema.core/defn 21 | puppetlabs.puppetdb.testutils/dotestseq clojure.core/doseq 22 | puppetlabs.puppetdb.testutils/with-wrapped-fn-args clojure.core/let 23 | puppetlabs.puppetdb.scf.storage-test/deftest-db clojure.test/deftest} 24 | :linters 25 | {:deprecated-var 26 | {:exclude 27 | {puppetlabs.puppetdb.jdbc/call-with-array-converted-query-rows {:namespaces [".*"]} 28 | puppetlabs.puppetdb.testutils.services/call-with-puppetdb-instance {:namespaces [".*"]} 29 | puppetlabs.puppetdb.testutils.services/with-puppetdb-instance {:namespaces [".*"]}}} 30 | :refer-all {:exclude [clojure.test]} 31 | :unresolved-symbol 32 | {:exclude 33 | [(puppetlabs.comidi/GET) 34 | (puppetlabs.comidi/POST)]}}} 35 | -------------------------------------------------------------------------------- /test-resources/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFITCCAwmgAwIBAgIUF+P4dos98GCpUFwuXXPhp1EIkxowDQYJKoZIhvcNAQEL 3 | BQAwHzEdMBsGA1UEAwwUUHVwcGV0IENBOiBsb2NhbGhvc3QwHhcNMjUwNzE5MTkx 4 | MzQyWhcNMjcxMDIyMTkxMzQyWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0G 5 | CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC7dQGaBw/VL6uj6/Tt7h3ZCs1S44ge 6 | Y6Hi4WlmmZMnQandRYicURa1xUHAtvF82qZhq8U8UmSv9AFh2jnsTcYCeLOk43DW 7 | VZEyDUQbXu1AvJpT14CHIT6LdmBXWqkW42Iceslr+6em8gpoe/Ecj2jrjSAUyBhq 8 | PyPinnL4hB+2bXGzm5KmAnAcJqXHsKRMt/OxcdN6zZZXkJNv4BAnZRa9RL6OTKnd 9 | VA5M9nUaohdNOHZc0HIZTZdX18jH1zh00J5MqYrmFIZbLEWeJTlVyg6nuc798Y2i 10 | ZfHZq/TSEtz/hCXQMzCUtBZofSLWpMojB784QJmVpyMji/+rHpti1pGUaoqX+c5N 11 | mYuB2kzky37gcgXt0ujdPWZo7Rcbs04fvy8pIQVLRCgzYFXnm2qXTr6SSHALGFkR 12 | Xscwk5eQndEgNu1OFAhxRlbzlAnkcmnMK9NcQUnShLZLwSVXmi/BUMcx+L17oyGr 13 | sYwH2MXWj45L3ZjMYsjZ/ubjZZYnxLNOs3oLapzrijfX2DV1Oxpuk+pSWXGpuchp 14 | KRNsS8pnB0a3LMFyfFEyCjMXZx6fIjuy/E7hjYrYGKsKGXvBGvPyqx81dRLpgAGQ 15 | PhNbzzsVtpizoz2MM5gfEOZsIhtkHRSEkum6o0/7n15Uja7FHipNgjEHr+wgMLXO 16 | 0x/c/6swpTJKAQIDAQABo2AwXjAcBgNVHREEFTATgglsb2NhbGhvc3SCBnB1cHBl 17 | dDAdBgNVHQ4EFgQU3H/vbD4+oZskVHdoTef+8M+Bz6AwHwYDVR0jBBgwFoAUuGjb 18 | 33RvBu0Ax/HF8VpDPbsYMq4wDQYJKoZIhvcNAQELBQADggIBAAv3L0uvbddoCQYS 19 | upOnfdJ27smn3QV6xcOVcSy8/qsbhoLgYykslAC5iq+cUiMlACjqM8PC5Q5s/xhh 20 | 3DO5mqv2xJ1xzDIUuy8MGCtJvWJ5p442SXPZMXN3cHp3PK2H/LBPXL/tJei8LGLo 21 | PLsbZnTEkRrE9v5sIgk5RqG90PnvDt+oPoTl0yUg9E3zh6Gpw12GdLYpjkaZ32QO 22 | YoZUO8rbR7dDLAEFXcDjAtq1qejYKCisRhcuQVlmS3Rucyj7iF2HdbGuFCA4PUl8 23 | Q8JXsEP8GBki0CG2V35AOa+6Ah0EHsCjSdvaGOj5sxCFoHIq/BQlrHaxVnZSRCQ6 24 | nFVY7rkE/J1rtyQ0LapwFX67sW1SGdsuv0TqaDJEPPtwG1/ha+4pwfKKnBjRGsjx 25 | Z8SDCUy9h6/cNxTr/Mxknd2CtHgpl9V8IT2UW4RnBVRtqaCvm9x5w2rsyHuhMkhV 26 | leQuq4mojjN1W9Ou+V8N/qTdhMyg7Of4sZURwz277dlTVxMLlylnu/Lo1S7V02VX 27 | 5SLRVqTdGYD35qApSCMqjpU+GiPpYJnTeu9UDX+iycMJJoEwHhuLLyQsk9//884+ 28 | LEG9FQ6WF9RvobcIagFj2aPaTS/EC9z2p0I2Ds3MaXQ0KEcddc4YXOdgAzlbiSxS 29 | ffN1lWv7jaaiMlOWyP8ALvYnYBdT 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /acceptance/setup/pre_suite/90_install_devel_puppetdb.rb: -------------------------------------------------------------------------------- 1 | step "Install development build of PuppetDB on the PuppetDB server" do 2 | databases.each do |database| 3 | os = test_config[:os_families][database.name] 4 | 5 | case test_config[:install_type] 6 | when :git 7 | Log.notify("Install puppetdb from source") 8 | Log.error database 9 | 10 | enable_https_apt_sources(database) 11 | install_postgres(database) 12 | install_puppetdb_via_rake(database) 13 | start_puppetdb(database) 14 | when :package 15 | Log.notify("Installing puppetdb from package; install mode: '#{test_config[:install_mode].inspect}'") 16 | 17 | configure_postgresql_repos_on_el(database) 18 | enable_https_apt_sources(database) 19 | install_puppetdb(database) 20 | 21 | if test_config[:validate_package_version] 22 | validate_package_version(database) 23 | end 24 | 25 | # The package should automatically start the service on debian. On redhat, 26 | # it doesn't. However, during test runs where we're doing package upgrades, 27 | # the redhat package *should* detect that the service was running before the 28 | # upgrade, and it should restart it automatically. 29 | # 30 | # That leaves the case where we're on a redhat box and we're running the 31 | # tests as :install only (as opposed to :upgrade). In that case we need 32 | # to start the service ourselves here. 33 | if test_config[:install_mode] == :install and [:redhat].include?(os) 34 | start_puppetdb(database) 35 | else 36 | # make sure it got started by the package install/upgrade 37 | sleep_until_started(database) 38 | end 39 | 40 | end 41 | end 42 | 43 | case test_config[:install_type] 44 | when :git 45 | install_puppetdb_termini_via_rake(master, databases) 46 | when :package 47 | install_puppetdb_termini(master, databases) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /ext/bin/prep-debianish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Prepares Debian-like Linux machines for running PuppetDB tests 4 | # Installs JDK, Leiningen, and pgbox and creates 5 | # --for takes a test spec like core/openjdk8/pg-9.6 6 | 7 | set -uxeo pipefail 8 | 9 | usage() { 10 | echo 'Usage: $(basename "$0") --for PDB_TEST_SPEC --install DIR' 11 | } 12 | 13 | misuse() { usage 1>&2; exit 2; } 14 | 15 | # Validate arguments 16 | spec='' 17 | install='' 18 | while test $# -gt 0; do 19 | case "$1" in 20 | --install) 21 | test $# -gt 1 || misuse 22 | install="$2" 23 | shift 2 24 | ;; 25 | --for) 26 | test $# -gt 1 || misuse 27 | spec="$2" 28 | shift 2 29 | ;; 30 | *) misuse ;; 31 | esac 32 | done 33 | 34 | test "$spec" || misuse 35 | test "$install" || misuse 36 | flavor=$(ext/bin/flavor-from-spec "$spec") 37 | 38 | # If flavor is not recognized, do nothing 39 | case "$flavor" in 40 | core|ext|core+ext|int|lint) 41 | jdk="$(ext/bin/jdk-from-spec "$spec")" 42 | jdkver="${jdk##*jdk}" 43 | mkdir -p "$install" 44 | abs_install=$(cd "$install" && pwd) 45 | export PATH="$abs_install/bin:$PATH" 46 | # Install JDK using cache if within 24 hours of last installation 47 | ext/bin/require-jdk --expire "$jdk" "$abs_install" 48 | # Create testing script with updated PATH 49 | mkdir -p "$abs_install/etc" 50 | printf ' 51 | export JAVA_HOME=%q/jdk 52 | export PATH="$JAVA_HOME/bin:$PATH" 53 | export PATH=%q/bin:"$PATH" 54 | hash -r 55 | ' \ 56 | "$abs_install" "$abs_install" \ 57 | > "$abs_install"/etc/pdb-test-env 58 | source "$abs_install"/etc/pdb-test-env 59 | # Install leiningen and pgbox 60 | ext/bin/require-leiningen default "$abs_install" 61 | ext/bin/require-pgbox default "$abs_install" 62 | ;; 63 | esac 64 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/cli/util.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.cli.util 2 | "As this namespace is required by both the tk and non-tk subcommands, 3 | it must remain very lightweight, so that subcommands like 4 | \"version\" aren't slowed down by loading the entire logging 5 | subsystem or trapperkeeper, etc." 6 | (:require 7 | [puppetlabs.i18n.core :refer [trs]])) 8 | 9 | (def err-exit-status 2) 10 | 11 | ;; Testing hook 12 | (defn java-version [] (System/getProperty "java.version")) 13 | 14 | (defn jdk-support-status 15 | "Returns :official, :tested, :deprecated, :unknown, or :unsupported." 16 | [version] 17 | (cond 18 | (re-matches #"1\.[1234567]($|(\..*))" version) :unsupported 19 | (re-matches #"1\.[89]($|(\..*))" version) :deprecated 20 | (re-matches #"10($|(\..*))" version) :deprecated 21 | (re-matches #"11($|(\..*))" version) :tested 22 | (re-matches #"17($|(\..*))" version) :official 23 | (re-matches #"21($|(\..*))" version) :tested 24 | :else :unknown)) 25 | 26 | (defn jdk-unsupported-msg [version] 27 | (let [status (jdk-support-status version)] 28 | (case status 29 | (:unknown) {:warn (trs "JDK {0} is neither tested nor supported. Please use JDK 11, 17 or 21" version)} 30 | (:deprecated) {:warn (trs "JDK {0} is deprecated, please upgrade to JDK 11, 17 or 21" version)} 31 | (:official :tested) nil 32 | {:error (trs "PuppetDB doesn''t support JDK {0}" version)}))) 33 | 34 | (defn run-cli-cmd [f] 35 | (let [jdk (java-version)] 36 | (if-let [{:keys [warn error]} (jdk-unsupported-msg jdk)] 37 | (if error 38 | (do 39 | (binding [*out* *err*] (println (trs "error:") error)) 40 | err-exit-status) 41 | (do 42 | (binding [*out* *err*] (println (trs "warn:") warn)) 43 | (f))) 44 | (f)))) 45 | 46 | (defn exit [status] 47 | (shutdown-agents) 48 | (binding [*out* *err*] (flush)) 49 | (flush) 50 | (System/exit status)) 51 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/nodes.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.nodes 2 | "Puppet nodes parsing 3 | 4 | Functions that handle conversion of nodes from wire format to 5 | internal PuppetDB format, including validation." 6 | (:require 7 | [puppetlabs.puppetdb.schema :as pls] 8 | [schema.core :as s])) 9 | 10 | ;; SCHEMA 11 | 12 | (def expire-wireformat-schema 13 | {:facts s/Bool}) 14 | 15 | (def configure-expiration-wireformat-schema 16 | {:certname s/Str 17 | :expire expire-wireformat-schema 18 | (s/optional-key :producer_timestamp) (s/maybe pls/Timestamp)}) 19 | 20 | (def nodes-wireformat-schema 21 | {:certname s/Str 22 | :deactivated (s/maybe s/Str) 23 | :expired (s/maybe pls/Timestamp) 24 | :catalog_timestamp (s/maybe pls/Timestamp) 25 | :facts_timestamp (s/maybe pls/Timestamp) 26 | :report_timestamp (s/maybe pls/Timestamp) 27 | :catalog_environment (s/maybe s/Str) 28 | :facts_environment (s/maybe s/Str) 29 | :report_environment (s/maybe s/Str) 30 | :latest_report_status (s/maybe s/Str) 31 | :latest_report_hash (s/maybe s/Str) 32 | :latest_report_noop (s/maybe s/Bool) 33 | :latest_report_noop_pending (s/maybe s/Bool) 34 | :cached_catalog_status (s/maybe s/Str) 35 | :latest_report_corrective_change (s/maybe s/Bool) 36 | :latest_report_job_id (s/maybe s/Str) 37 | :expires_facts (s/maybe s/Bool) 38 | :expires_facts_updated (s/maybe pls/Timestamp)}) 39 | 40 | (pls/defn-validated nodes-query->configure-expiration-wire-v1 41 | :- [configure-expiration-wireformat-schema] 42 | [nodes :- [nodes-wireformat-schema]] 43 | (->> nodes 44 | (filter :expires_facts_updated) 45 | (map (fn [x] 46 | {:certname (:certname x) 47 | :expire {:facts (:expires_facts x)} 48 | :producer_timestamp (:expires_facts_updated x)})))) 49 | 50 | (def deactivate-node-wireformat-schema 51 | {:certname s/Str 52 | (s/optional-key :producer_timestamp) (s/maybe pls/Timestamp)}) 53 | -------------------------------------------------------------------------------- /test-resources/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFRzCCAy+gAwIBAgIUEbQuwy4OQbLZh84ZKpZeGpgnGXgwDQYJKoZIhvcNAQEL 3 | BQAwHzEdMBsGA1UEAwwUUHVwcGV0IENBOiBsb2NhbGhvc3QwHhcNMjUwNzE5MTkw 4 | OTI2WhcNMzUwNzE3MTkwOTI2WjAfMR0wGwYDVQQDDBRQdXBwZXQgQ0E6IGxvY2Fs 5 | aG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJ3sJX1OmQpE9iLU 6 | uYasqwjKc0ykbN+D7TQWfy6LTx2s5ikRFSg0uvOwLL5rV1PUWYZXVFoRIyRN/3vC 7 | QVOeM/fHEHy8jZbdHMXX3621guNopnn9pw/nT45KZPz3137UDft+4KZqmnH04ynM 8 | dLXScsoShoy1iIwkdpUHUh3ewMloxUnQRMLUBKAac1x0SLu8PWbq3bWyODOGKuId 9 | dDpki2ISWYmEGPey+jBXlnSgz7NF0SbwDNsXF8Z1Mpar+serZcbQljR4lcKEpF9e 10 | Atq51ax1y1yCzoZRZrRWmSvK4/VT9zx0WmJ41Yp0j50gperAWMv25xfbJOvICqw1 11 | EYRx9hwAXz4KyKMdc4CQSd25Ug52C1/85TyIGn1tEIWYb6L1dXoyYsDiaxRBuTVx 12 | 38qShGSEI1r3nEoZjAyKtXAFXoxm87aIbxFTEbz7s/JE/h9LuJr7Khze5yLUToBG 13 | nEDJ4HJZlichQlWW41ZzAAZXDtLptcd1CzSLSLcZgMUraXmwYfxFV85cg7p8ynC1 14 | JLIR+zm6MS/GCUWJRsCWtGBu9fZ9XS5NDU8RelR7dIS2cxBJQXtZYR9UTjM3akp+ 15 | FskPQh/bo478D4pISS1Eq/v1vLU/V3CfL9XnfnCRlTE8/+wSEn56uxyBhcscOLSj 16 | g3ru19M4YTFHnJ+ioUgKFpugfAjdAgMBAAGjezB5MA8GA1UdEwEB/wQFMAMBAf8w 17 | NwYJYIZIAYb4QgENBCoWKFB1cHBldCBSdWJ5L09wZW5TU0wgSW50ZXJuYWwgQ2Vy 18 | dGlmaWNhdGUwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS4aNvfdG8G7QDH8cXx 19 | WkM9uxgyrjANBgkqhkiG9w0BAQsFAAOCAgEAkiMUrwV+8TM212pvx2UbxYuUw2yO 20 | rBHprs/kZSZA7jsDkwpt970PQl9RCu7Ob3Hn+3QomlF6IbuHPE1Ymu+BHiV86M8G 21 | aeaAFlBmoMDk4k/beyF54bWqr/DpCYDA6Y9LjF9GWspg4J7tfqTq3nNnWMb+o0kY 22 | R8VuU6hPgKO28SkAP/VQxx3Sroxj5kPi+hDdzLly5v2Mq2+Nk9tnuvUt3tqry8nT 23 | nbPYwV5wl1xabe71LWBxKow0+MRjNPXldGa/Su3pA2HNz7G1zf0lqRTWgj2rt3wF 24 | S5pr6IE288foybIzaGczHUE5S//SojwvlzXjXU7Xwfm4fQvhZE+ldVjgQava/G1h 25 | VY3plZHzvCO0L7vp/XPAdwEYH8Dw/ugkZzOpk6d4nvlTNlFTZ/8A/SaA6d7Cv3XW 26 | RWXUCIEDIrQig6AsWcBwlAqiV0a3njdnovM3K/N9cC5s8EZjXvE5ggp+drRfIOG5 27 | PZUGdvc1Cc3xInIai/ZVsKA2U3lQjjzo/P7RJ3P4ZNGmGjcNSGLCEIUayBr4iT23 28 | pufvcFnTq7Aibr7BVuB2JUTbDeTZ41DSntPiUHKUkGnmo1JL+g8eUkPAarKSIadO 29 | zU5oUr+lyCjnBZO+fDjMdd7y+Ldb2/xozvAyb1dlqhjMaFa7mZNuC0muK1wTDOQk 30 | imGgGf7Jx5AGUPM= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /test-resources/puppetlabs/puppetdb/ssl/certs/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNDCCAxygAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBw 3 | ZXQgQ0E6IGV4cGxvc2l2bzAeFw0xMzAxMDEyMTQ0MTZaFw0xODAxMDEyMTQ0MTZa 4 | MB8xHTAbBgNVBAMMFFB1cHBldCBDQTogZXhwbG9zaXZvMIICIjANBgkqhkiG9w0B 5 | AQEFAAOCAg8AMIICCgKCAgEAv55rxHbHHFuov+Js47hU8n394nGYplAocF88hDgX 6 | 6MLG75pzXZ7WqA+uEHxm7KkarEXxBZqkTUlWsxFa0UpgjEQlRDUxnlLsbJL1/HYP 7 | SB1+J7a8UNGvZRh2Ich3rbQ8+tv4UQRR8dr7tOQE+EPL0M492FYx/t/0pRHDErKD 8 | RJOHxxJLkpapIavObg/bZDEoLf8+ZaU/DyBENJmL4lhHl6i/KhBinuR4UnUfdxZ7 9 | dVTid7gNGk9VGot3x0LWdXYqQ/sxNb/qx6M/0MR5PuO9Pdsmn1SHyRyUcIjX1l6Y 10 | H4YH+ryoff0EWEtJxsGrrptXWX7groQpyRZrdknU74cCKyR1JY+7DyV7D8Ixgf1j 11 | MU7GKEglVB0qHURWzQgfsNeq3kk9I+dQr8KQvkv83cVYVM7mONA+EyMju8i7u4Oq 12 | 5XvpZvBANw+UZwB2lNTDdtBdC+XHQIAM9DYwpLyz20WlAmAZOFzHqe3k6ewwbU+n 13 | UEZgauvnmFsN1DbYeLtLabm1P7QgI4pxGfXXhOVCexIDTZ8mv1s3fsCP0tXmKQhp 14 | j25FvNJ6k/1xBPKc/PHB2Bms402GaK7lknaYBxIGUzgT2sw/GlcxWiPnV6BUHPwW 15 | aivELyu/hRu+Jr6bMsmlC0ZS4ZllrvsAitTPYvHdshQ6BmyHkSr6ZzrcijILGfFg 16 | 0esCAwEAAaN7MHkwNwYJYIZIAYb4QgENBCoWKFB1cHBldCBSdWJ5L09wZW5TU0wg 17 | SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF 18 | MAMBAf8wHQYDVR0OBBYEFPdrBes9dJY2zNm2UsqhdDwtGMncMA0GCSqGSIb3DQEB 19 | CwUAA4ICAQC0oieRsLuDOMYdsUVWj2qsSjjf7SgpdjdBJ23eY9DO1ePz4/MkJG8d 20 | 5oWFy9bTBWEDev7/9MWizHtjk/V+AglLaPJB2GyR9zfGeodRx860iqQdhrEs3B9U 21 | pGat1kKCDY3yee38Up4I0HyPdjTCPu0FcM+k4ySu1HKvhr0elOo3Y4d/UWGFfc6U 22 | bF4K385j1IKtN2O7Iu6DoYZNu+InZ95xiRhApzTRJop3PM+FznYNavFaOlFS5mcj 23 | tbKiQjN+8XrpmHJVQniEz4qKY5yZO+IbPBxP8lZOJYvKYtMu2KsbxDd8s/6s40dT 24 | SpHu7lEgBe4gQXDMkegHo4npJoiXlHh97pgRgj6DLeUiKtrq31NYRD5gQ+KpKdtg 25 | z4mvOcJUXitMV3LtoUGnYNwUXNjvNuMAiFlAQ5fRDTX5dnJTxa3+qMn07zV2J+9a 26 | +Wdqy6x4IBFHdpfhiySeJ0ERq7JnOiRZHsTqGSF6Bg+X1S7b+cXnXyvhnEZbAomF 27 | kU8pJt6XBLvtlq5s5wqKdndqLdQGdcvHcEcEi+s4zDwQsVmYeFkSnk+uiRESp4s3 28 | 8y3ecGihNxpfl2Ro2mrrf/2qj+voZ+brs0XzQiT6yuZX1B7I7IF28Q3qwweDAjut 29 | s1G1ntQA9La615iefs2DAkdCS4ROU8/TFz30I08PNpOrwMhg6QDGGA== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/core.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.core 2 | "PuppetDBs normal entry point. Dispatches to command line subcommands." 3 | (:require 4 | [clojure.string :as str] 5 | [puppetlabs.puppetdb.cli.util 6 | :refer [err-exit-status exit run-cli-cmd]])) 7 | 8 | (def usage-lines 9 | ["Available subcommands:" 10 | " version Display version information" 11 | " services Run PuppetDB" 12 | " upgrade Upgrade to latest version and exit" 13 | " benchmark Run development-only benchmarking tool" 14 | " fact-storage-benchmark" 15 | " generate Create sample data files for benchmarking" 16 | " help Display usage summary" 17 | "For help on a given subcommand, invoke it with -h"]) 18 | 19 | (defn usage 20 | [stream] 21 | (binding [*out* stream] 22 | (println (str/join "\n" usage-lines)))) 23 | 24 | (defn help [args] 25 | (if (zero? (count args)) 26 | (do (usage *out*) 0) 27 | (do (usage *err*) err-exit-status))) 28 | 29 | ;; Resolve the subcommands dynamically to avoid loading the world just 30 | ;; to print the version. 31 | (defn run-resolved [cli-name fn-name args] 32 | (let [namespace (symbol (str "puppetlabs.puppetdb.cli." cli-name))] 33 | (require (vector namespace)) 34 | (apply (ns-resolve namespace fn-name) args))) 35 | 36 | (defn run-subcommand 37 | "Runs the given subcommand, which should handle shutdown and the 38 | process exit status itself." 39 | [subcommand args] 40 | (case subcommand 41 | "help" (run-cli-cmd #(help args)) 42 | "upgrade" (run-resolved "services" 'cli [args {:upgrade-and-exit? true}]) 43 | "services" (run-resolved "services" 'cli [args]) 44 | 45 | ("benchmark" "fact-storage-benchmark" "version" "generate") 46 | (run-resolved subcommand 'cli [args]) 47 | 48 | (do 49 | (usage *err*) 50 | err-exit-status))) 51 | 52 | (defn -main 53 | [subcommand & args] 54 | (exit (run-subcommand subcommand args))) 55 | -------------------------------------------------------------------------------- /puppet/spec/unit/util/puppetdb_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rspec 2 | # encoding: UTF-8 3 | 4 | require 'spec_helper' 5 | require 'digest/sha1' 6 | require 'puppet/util/puppetdb' 7 | require 'puppet/util/puppetdb/command_names' 8 | require 'puppet/util/puppetdb/http' 9 | require 'json' 10 | 11 | 12 | class FakeHttpResponse 13 | def initialize(body) 14 | @body = body 15 | end 16 | attr_reader :body 17 | end 18 | 19 | describe Puppet::Util::Puppetdb do 20 | subject { Object.new.extend described_class } 21 | 22 | describe "#submit_command" do 23 | let(:payload) { {'resistance' => 'futile', 'opinion' => 'irrelevant'} } 24 | let(:command1) { Puppet::Util::Puppetdb::Command.new("OPEN SESAME", 1, 'foo.localdomain', Time.now.utc, 25 | payload.merge(:uniqueprop => "command1")) } 26 | 27 | it "should submit the command" do 28 | # careful here... since we're going to stub Command.new, we need to 29 | # make sure we reference command1 first, because it calls Command.new. 30 | command1.expects(:submit).once 31 | Puppet::Util::Puppetdb::Command.expects(:new).once.returns(command1) 32 | subject.submit_command(command1.certname, 33 | command1.command, 34 | command1.producer_timestamp_utc, 35 | command1.version) { command1.payload } 36 | end 37 | 38 | end 39 | 40 | describe ".query_puppetdb" do 41 | let(:response) { JSON.generate({'certname' => 'futile', 'status' => 'irrelevant'}) } 42 | let(:query) { ["=", "type", "Foo"] } 43 | let(:http_response) { FakeHttpResponse.new(response) } 44 | it "should query PuppetDB" do 45 | # careful here... since we're going to stub Command.new, we need to 46 | # make sure we reference command1 first, because it calls Command.new. 47 | Puppet::Util::Puppetdb::Http.expects(:action).once.returns(http_response) 48 | Puppet::Util::Puppetdb.query_puppetdb(query) 49 | end 50 | 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /test-resources/puppetlabs/puppetdb/ssl/certs/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFSjCCAzKgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBw 3 | ZXQgQ0E6IGV4cGxvc2l2bzAeFw0xMzAxMDEyMTQ0MjhaFw0xODAxMDEyMTQ0Mjha 4 | MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC 5 | AgoCggIBANCEYBvEIdTXFOMwz18wahs6tg26C+LT2XOwQspb/Aj5WT2EBwnG2leD 6 | CzfOAKyhHDL6jRqrYU32jqoqBzmzkeVsHzqOMNFosyvcBLU8zyLZU+IP1rjJCyE8 7 | xx9HsdhPKJj93f/gSNR5NQlRcZfqahhOwh/nYdY3pFiNgjUoRwhV2Q01n+ku8WJw 8 | kLVT1TREW9TiSWk7cHWF/ZltPOMMxvJ9q0kXh8sVYK4Gtt3pphTUW0qgXQ2NnNWT 9 | W+7vciRjnHxeoY3q6ZG7vZ8HewYKR4W8D6FA32xCsWELSsWlAABt1lBjKGas/fiY 10 | SeDqSfIxknFn/CIM9AIp2PLS3wh5e0o98qey9AN2WRyG7Qs0ijhwKx9bsMxbM0LR 11 | 5jXuXjBnjGQ69fwCjwlUsOSpNPWLibM+GmxvhghJgAlH5dD4+GqN77WLncQTWYXX 12 | GnOw5efVivS4bgU3t8l8mHLH6quLolR1KLfCv+HuqkvRposAqqLwKH+dhlbq1Y+i 13 | 4siPxfYV5NZ092Z9R0F4BPEmLhKngkK+/eQXxLY2zfaR6Ns83yRJfMXRyElECX/+ 14 | RBT1LyIRZg+MbsRg7DsKWI0plzxso/4CgSmYSfPku5nkekrMN34YhUtcxsdHSmY1 15 | 5/p2olvKpTJj3e5fa2KVswcv77FsC17gIfMXqvN3tITP+q1LLJHNAgMBAAGjgZsw 16 | gZgwNwYJYIZIAYb4QgENBCoWKFB1cHBldCBSdWJ5L09wZW5TU0wgSW50ZXJuYWwg 17 | Q2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQGCCsGAQUF 18 | BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT3awXrPXSWNszZ 19 | tlLKoXQ8LRjJ3DANBgkqhkiG9w0BAQsFAAOCAgEAYTFx++uptZxgptFmkPfT1f2W 20 | 6djOOVULmlLGPC6Ovbe5v0ksA2hbLW3eSmfL28Ku0WC8gRl0/PhyiyW77M1jp9dV 21 | ztsFkXjMiIIcY0B7Hgqh1kpK1CFvSbsD3piXcDLlZ1CwSAXuohp+J2fUblHRfAUD 22 | Th9qrm3g4uNFp0wXxO1+GgXeDrGRqYosb0wAhB7/BhW2WbOFtVYdFoyyXJFJYx/3 23 | Gj7ZTE3rvGGxOEEuww0pFmuGCflZYxEu15Rynej7soGaE80+wRk+gMS26WKwRQ/0 24 | TGovOHSLo/fpOjjHIoqbQLH3S08jUfAYjjP7Rd01SiztUjZMILC2WCnpdYN+2O9O 25 | rGTj2Zl3oRtE67NwxgKlo2GIFghSF366XOF8O4z1e9id6u5XEdoz8uGFkHyMu79N 26 | cdYcUtmAqLvJ0Ubewg+TfNDcfk1akNtHtIJDNqFwHlZ9R1GIupHQs10R4YxJv3I2 27 | LojJbtcgWcDg9StwCHRA0SuLrWnHPnm+glzXM5HTNJZ6vcrM0SrcnZ59p3o3ZULL 28 | JTJikA+pcs+WAWz1yTNg/ywxYPnFrs8A4MEC43XFe2dS96a7VcNqpMMya+DAWWCS 29 | +51lDlMLuz+q5WHDGh3ouTNYMdcSLo8bHzKInDYnK/DzUpdJ5OKYwqfxv5n0bJs6 30 | fRA2buaQo0zJeid7G2Q= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/crl.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIICmTCBggIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBwZXQgQ0E6 3 | IGxvY2FsaG9zdBcNMjIwMjA4MjI0OTEyWhcNMzcwMjA1MjI0OTE0WqAvMC0wHwYD 4 | VR0jBBgwFoAUEvfpMG3GZMu3oJCQabI53zkxLlMwCgYDVR0UBAMCAQAwDQYJKoZI 5 | hvcNAQELBQADggIBALoLYLSlzz7b1uAJxKweRbu6DIJXSAVUiVQxQEAy+2Ojv+B+ 6 | VVNuR14zJ5Fz3/V3cc4d7f1ifzQZW329EWSVw4MJt8YsBhffOFS6h2/1mgTJjnxz 7 | N3Tn+rLJoqbPgyUbExGk0b+M0abhobi3ynJZ9H7YDXz5StX9AjdAkP10DuF9YHb7 8 | 5AJobZFw1GaNISyZM7cduVeTbFFDDCfxnLK759Clbb5PGsVbGONxBgXWwn7pqPMN 9 | ksacUIWWhouRJ7Bg/WQ7buQs4jbkf+a2fmDqmRnZ0+GByNbuCNzWRnjnfrKfTYJH 10 | hh3DcHTxNHLGSz2gvlHywzTNx/R0gGseljGGt2TB6XQ7u8jN+IFgC5vLXN27S05s 11 | 88fY9bP+yRC+P9HTRJ87AQlT789CXa/5B9Ml1+IsDdCdRWasq13sFtFNp08DwcH5 12 | l8venCzkJ6Pv4R+4RLTThEBFuQFNgRt46s4kFZQ8TTLYmAg8cE5/7kJMtaPSttLZ 13 | 1rdz/oSc5U44S8TF6sFeumozF0u/9RAK5+qt5PcSV3Ek4t3dAB7nTe3pJB7m7yrq 14 | p//HG/WdSXoh/hlT7ANKxyVAUfaIiK7BFiN2q+EMLQXX7PQa3jd1ps79wReEO8AY 15 | 6yyaLeVjBEgRBb+mIES08wuuyBDusuaIEL18h9T7TMC0JO8Luq4/5R3Mz5VU 16 | -----END X509 CRL----- 17 | -----BEGIN X509 CRL----- 18 | MIICozCBjAIBATANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB5QdXBwZXQgUm9v 19 | dCBDQTogOTFkNjIxMjRlNTg4NTMXDTIyMDIwODIyNDkxMloXDTM3MDIwNTIyNDkx 20 | M1qgLzAtMB8GA1UdIwQYMBaAFJVmUAVa4iPk3U5Jm56pygR6OYwSMAoGA1UdFAQD 21 | AgEAMA0GCSqGSIb3DQEBCwUAA4ICAQB/H9oLpxySjc6UqgsT4meJ2RmUV3/s0wGx 22 | rObEZww30QzV6hvD46A/o7tlhkkyXbiTL7K/5zeE/Ixf/ySestNp+X4c0cZKJ/yZ 23 | 6tsYOG52gTDIcAF1glvDkyZEjBAIlfwhHl2SaZr1mwWzhoB+y34L4vz7OGt4iTXL 24 | CUdZ3+zwX/+RcV1bS/WJNFiOEG0DOprSzIK5bzcwstJTfuF+Zb16SWKyadv3PQIJ 25 | Dh2aFImrKMQcCMx4ZIINiiot7hjXpnI1DtlFBaE9A2q1O6BhI8TWO1Gicoqy1fGY 26 | +3d7gjNUVKTZ4ia6gTJOsIvlf5ZDFfM1ruPB5izUJBISl9+b+D3k0k/QshmgNPk6 27 | Q0iP+UnI2zmNTESA1VyymN8e/HrB8xFK5+NKDO5k3vS3i0gK5zl+lB/e21AWYeOs 28 | at0XEtyf4TYuMfb4asgeEX7wR8hRy79zmasoL85UHWla1Q+z4ZLTtUQ/s0WuK13c 29 | BoDwyub98RXe1PRHdDNwb0qnCpsaxHzYYewoNH0WOJ2/twb60JGocXge57+Pxlw0 30 | Vh2x59RElisCz6+TOxI2DUfxHmCya3245PZK+G0sD3qtSPiK+C/w9SRlHhnFXM0f 31 | xw0tja5Yk+UpGVZMcqrNzvQuRuNdKTcn7aZPnwpd98+aryoC/RmdtiUhHQAgz+bq 32 | gjo4Vg5ykw== 33 | -----END X509 CRL----- 34 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/ca/ca_crl.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIICmTCBggIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBwZXQgQ0E6 3 | IGxvY2FsaG9zdBcNMjIwMjA4MjI0OTEyWhcNMzcwMjA1MjI0OTE0WqAvMC0wHwYD 4 | VR0jBBgwFoAUEvfpMG3GZMu3oJCQabI53zkxLlMwCgYDVR0UBAMCAQAwDQYJKoZI 5 | hvcNAQELBQADggIBALoLYLSlzz7b1uAJxKweRbu6DIJXSAVUiVQxQEAy+2Ojv+B+ 6 | VVNuR14zJ5Fz3/V3cc4d7f1ifzQZW329EWSVw4MJt8YsBhffOFS6h2/1mgTJjnxz 7 | N3Tn+rLJoqbPgyUbExGk0b+M0abhobi3ynJZ9H7YDXz5StX9AjdAkP10DuF9YHb7 8 | 5AJobZFw1GaNISyZM7cduVeTbFFDDCfxnLK759Clbb5PGsVbGONxBgXWwn7pqPMN 9 | ksacUIWWhouRJ7Bg/WQ7buQs4jbkf+a2fmDqmRnZ0+GByNbuCNzWRnjnfrKfTYJH 10 | hh3DcHTxNHLGSz2gvlHywzTNx/R0gGseljGGt2TB6XQ7u8jN+IFgC5vLXN27S05s 11 | 88fY9bP+yRC+P9HTRJ87AQlT789CXa/5B9Ml1+IsDdCdRWasq13sFtFNp08DwcH5 12 | l8venCzkJ6Pv4R+4RLTThEBFuQFNgRt46s4kFZQ8TTLYmAg8cE5/7kJMtaPSttLZ 13 | 1rdz/oSc5U44S8TF6sFeumozF0u/9RAK5+qt5PcSV3Ek4t3dAB7nTe3pJB7m7yrq 14 | p//HG/WdSXoh/hlT7ANKxyVAUfaIiK7BFiN2q+EMLQXX7PQa3jd1ps79wReEO8AY 15 | 6yyaLeVjBEgRBb+mIES08wuuyBDusuaIEL18h9T7TMC0JO8Luq4/5R3Mz5VU 16 | -----END X509 CRL----- 17 | -----BEGIN X509 CRL----- 18 | MIICozCBjAIBATANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB5QdXBwZXQgUm9v 19 | dCBDQTogOTFkNjIxMjRlNTg4NTMXDTIyMDIwODIyNDkxMloXDTM3MDIwNTIyNDkx 20 | M1qgLzAtMB8GA1UdIwQYMBaAFJVmUAVa4iPk3U5Jm56pygR6OYwSMAoGA1UdFAQD 21 | AgEAMA0GCSqGSIb3DQEBCwUAA4ICAQB/H9oLpxySjc6UqgsT4meJ2RmUV3/s0wGx 22 | rObEZww30QzV6hvD46A/o7tlhkkyXbiTL7K/5zeE/Ixf/ySestNp+X4c0cZKJ/yZ 23 | 6tsYOG52gTDIcAF1glvDkyZEjBAIlfwhHl2SaZr1mwWzhoB+y34L4vz7OGt4iTXL 24 | CUdZ3+zwX/+RcV1bS/WJNFiOEG0DOprSzIK5bzcwstJTfuF+Zb16SWKyadv3PQIJ 25 | Dh2aFImrKMQcCMx4ZIINiiot7hjXpnI1DtlFBaE9A2q1O6BhI8TWO1Gicoqy1fGY 26 | +3d7gjNUVKTZ4ia6gTJOsIvlf5ZDFfM1ruPB5izUJBISl9+b+D3k0k/QshmgNPk6 27 | Q0iP+UnI2zmNTESA1VyymN8e/HrB8xFK5+NKDO5k3vS3i0gK5zl+lB/e21AWYeOs 28 | at0XEtyf4TYuMfb4asgeEX7wR8hRy79zmasoL85UHWla1Q+z4ZLTtUQ/s0WuK13c 29 | BoDwyub98RXe1PRHdDNwb0qnCpsaxHzYYewoNH0WOJ2/twb60JGocXge57+Pxlw0 30 | Vh2x59RElisCz6+TOxI2DUfxHmCya3245PZK+G0sD3qtSPiK+C/w9SRlHhnFXM0f 31 | xw0tja5Yk+UpGVZMcqrNzvQuRuNdKTcn7aZPnwpd98+aryoC/RmdtiUhHQAgz+bq 32 | gjo4Vg5ykw== 33 | -----END X509 CRL----- 34 | -------------------------------------------------------------------------------- /puppet/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | dir = File.expand_path(File.dirname(__FILE__)) 2 | $LOAD_PATH.unshift File.join(dir, "../lib") 3 | # Maybe puppetlabs_spec_helper is in a directory next to puppetdb. If not, we 4 | # don't fail any worse than we already would. 5 | $LOAD_PATH.push File.join(dir, "../../../puppetlabs_spec_helper") 6 | 7 | require 'cgi' 8 | require 'rspec' 9 | require 'rspec/expectations' 10 | require 'puppetlabs_spec_helper/puppet_spec_helper' 11 | require 'tmpdir' 12 | require 'fileutils' 13 | require 'puppet' 14 | require 'puppet/util/puppetdb' 15 | require 'puppet/util/log' 16 | 17 | 18 | def create_environmentdir(environment) 19 | envdir = File.join(Puppet[:environmentpath], environment) 20 | if not Dir.exist?(envdir) 21 | Dir.mkdir(envdir) 22 | end 23 | end 24 | 25 | def extract_producer_timestamp(command) 26 | DateTime.parse(command["producer_timestamp"]).to_time.to_i 27 | end 28 | 29 | def assert_command_req(expected_payload, actual_payload) 30 | req = JSON.parse(actual_payload) 31 | actual_producer_timestamp = extract_producer_timestamp(req) 32 | req.delete("producer_timestamp") 33 | req == expected_payload && 34 | actual_producer_timestamp <= Time.now.to_i 35 | end 36 | 37 | def assert_valid_producer_ts(path) 38 | _, param_str = path.split "?" 39 | params = CGI::parse(param_str) 40 | return false if params["producer-timestamp"].size != 1 41 | Time.iso8601(params["producer-timestamp"].first) 42 | end 43 | 44 | def create_http_response(url, nethttp) 45 | if Puppet::PUPPETVERSION.to_f < 7 46 | Puppet::HTTP::Response.new(nethttp, url) 47 | else 48 | Puppet::HTTP::ResponseNetHTTP.new(url, nethttp) 49 | end 50 | end 51 | 52 | RSpec.configure do |config| 53 | 54 | config.before :each do 55 | @logs = [] 56 | Puppet::Util::Log.level = :info 57 | Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs)) 58 | 59 | def test_logs 60 | @logs.map(&:message) 61 | end 62 | 63 | end 64 | 65 | config.expect_with :rspec do |c| 66 | c.syntax = [:should, :expect] 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/dashboard_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.dashboard-test 2 | (:require [puppetlabs.puppetdb.dashboard 3 | :refer [build-app dashboard-routes default-meter-defs]] 4 | [puppetlabs.puppetdb.http :as http] 5 | [clojure.test :refer :all] 6 | [ring.mock.request :refer [request]] 7 | [puppetlabs.puppetdb.testutils.services :as svc-utils] 8 | [puppetlabs.puppetdb.utils :refer [base-url->str-with-prefix]] 9 | [puppetlabs.puppetdb.testutils :as tu] 10 | [puppetlabs.puppetdb.testutils.dashboard :as dtu] 11 | [puppetlabs.puppetdb.middleware :as mid] 12 | [puppetlabs.puppetdb.cheshire :as json])) 13 | 14 | (deftest dashboard-resource-requests 15 | (testing "dashboard redirect works" 16 | (let [handler (mid/make-pdb-handler dashboard-routes) 17 | {:keys [status headers]} (handler (request :get "/"))] 18 | (is (= status 302)) 19 | (is (= "/pdb/dashboard/index.html" (get headers "Location"))))) 20 | (testing "dashboard data request works" 21 | (let [handler (build-app default-meter-defs) 22 | {:keys [status body]} (handler (request :get "/data")) 23 | body (json/parse-string body true)] 24 | (is (= status 200)) 25 | (is (seq? body)) 26 | (is (= (count body) 27 | (->> body (map :id) distinct count)))))) 28 | 29 | (deftest dashboard-routing 30 | (svc-utils/call-with-single-quiet-pdb-instance 31 | (fn [] 32 | (let [root-resp (svc-utils/get-unparsed (svc-utils/root-url-str))] 33 | (tu/assert-success! root-resp) 34 | (is (dtu/dashboard-page? root-resp))) 35 | 36 | (let [data-resp (-> svc-utils/*base-url* 37 | (assoc :prefix "/pdb/dashboard/data") 38 | base-url->str-with-prefix 39 | svc-utils/get-unparsed)] 40 | (tu/assert-success! data-resp) 41 | (is (http/json-utf8-ctype? (get-in data-resp [:headers "content-type"]) )) 42 | (is (seq? (json/parse-string (:body data-resp) true))))))) 43 | -------------------------------------------------------------------------------- /resources/ext/ezbake.conf: -------------------------------------------------------------------------------- 1 | ezbake: { 2 | pe: {} 3 | foss: { 4 | redhat: { dependencies: ["openvox-agent >= 8.21.0"], 5 | preinst: [], 6 | postinst: [ 7 | "/opt/puppetlabs/server/bin/puppetdb config-migration", 8 | "/opt/puppetlabs/server/bin/puppetdb ssl-setup", 9 | "", 10 | "# puppetdb changed its main namespace in 6.3 and the current rpm", 11 | "# upgrade strategy fails to restart the server correctly because", 12 | "# it ends up using the templated stop from the new package which ", 13 | "# refers to the wrong namespace for the older pdb that's still", 14 | "# running. Kill the old pdb if found and start the new one.", 15 | "# These changes can be removed in PuppetDB version 8.", 16 | "(maybe_dead=''", 17 | " pid=\"$(pgrep -f \"puppetdb.* -m puppetlabs.puppetdb.main\")\"", 18 | " if test $? -eq 0; then", 19 | " kill \"$pid\" 2>/dev/null", 20 | " for i in {1..75}; do # Wait up to ~15s", 21 | " if kill -0 \"$pid\" 2>/dev/null; then", 22 | " maybe_dead=1", 23 | " break", 24 | " fi", 25 | " sleep 0.2", 26 | " done", 27 | " if test -z \"$maybe_dead\"; then", 28 | " echo 'Unable to kill puppetdb server cleanly; sending KILL' 1>&2", 29 | " kill -9 \"$pid\"", 30 | " fi", 31 | " /sbin/service puppetdb start > /dev/null 2>&1", 32 | " fi)" 33 | ] 34 | }, 35 | debian: { dependencies: ["openvox-agent (>= 8.21.0)"], 36 | preinst: [], 37 | postinst: ["/opt/puppetlabs/server/bin/puppetdb config-migration", 38 | "/opt/puppetlabs/server/bin/puppetdb ssl-setup"] } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /acceptance/tests/security/puppetdb-ssl-setup/no-puppet-certs.rb: -------------------------------------------------------------------------------- 1 | test_name "puppetdb ssl-setup with no puppet certs" do 2 | db_conf_dir = puppetdb_confdir(database) 3 | db_ssl_dir = "#{db_conf_dir}/ssl" 4 | db_confd = "#{puppetdb_confdir(database)}/conf.d" 5 | bin_loc = "#{puppetdb_bin_dir(database)}" 6 | 7 | ssl_dir = on(database, "puppet config print ssldir --section master").stdout.chomp 8 | 9 | step "backup jetty.ini and puppetdb ssl certs" do 10 | on database, "cp #{db_confd}/jetty.ini #{db_confd}/jetty.ini.bak.ssl_setup_tests" 11 | on database, "rm -rf #{db_ssl_dir}.bak.ssl_setup_tests" 12 | on database, "if [ -e #{db_ssl_dir} ]; then mv #{db_ssl_dir} #{db_ssl_dir}.bak.ssl_setup_tests; fi" 13 | end 14 | 15 | teardown do 16 | on database, "cp #{db_confd}/jetty.ini.bak.ssl_setup_tests #{db_confd}/jetty.ini" 17 | # This restores the certs if they weren't restored already 18 | on database, "if [ ! -e #{ssl_dir} -a -e #{ssl_dir}.bak.ssl_setup_tests ]; then mv #{ssl_dir}.bak.ssl_setup_tests #{ssl_dir}; fi" 19 | on database, "if [ ! -e #{db_ssl_dir} -a -e #{db_ssl_dir}.bak.ssl_setup_tests ]; then mv #{db_ssl_dir}.bak.ssl_setup_tests #{db_ssl_dir}; fi" 20 | end 21 | 22 | # The goal of this test is, make sure the user receives a nice error when 23 | # the agent certs are missing, as this implies Puppet has not ran yet. 24 | step "run puppetdb ssl-setup with no puppet certs, and make sure it returns a meaningful error" do 25 | on database, "rm -rf #{ssl_dir}.bak.ssl_setup_tests" 26 | on database, "mv #{ssl_dir} #{ssl_dir}.bak.ssl_setup_tests" 27 | result = on database, "#{bin_loc}/puppetdb ssl-setup", :acceptable_exit_codes => [1] 28 | assert_match(/Warning: Unable to find all puppet certificates to copy/, result.output) 29 | end 30 | 31 | # Now we restore the certificates 32 | step "restore certificates" do 33 | on database, "rm -rf #{ssl_dir}" 34 | on database, "mv #{ssl_dir}.bak.ssl_setup_tests #{ssl_dir}" 35 | end 36 | 37 | step "retest puppetdb ssl-setup again now there are certs" do 38 | on database, "#{bin_loc}/puppetdb ssl-setup" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/certs/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFmjCCA4KgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBw 3 | ZXQgQ0E6IGxvY2FsaG9zdDAeFw0yMjAyMDgyMjQ5MTJaFw0zNzAyMDUyMjQ5MTRa 4 | MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC 5 | AgoCggIBALQWsayBs2PuA8ML3zxtveuqMc439r3UTkIEXlHrGP1zLuiuKKnRMe0L 6 | 7e0vITp5H0bzhHpDzUh/Sj2zwdCveqk8/wCk1YhUhM3TRHCjwwD4y1ELNQfJGOMp 7 | ntsurcidlFZfTMm/MTv+96O1kX3dJiLZBajeA0NmPo69T5ndWkGTlMtUKkHkoG6X 8 | xEwLtg1YEFHeolv8HALzuvtDKiS+/exbuwtOsNJ03tuPyBVNixGN8bgL/0VgKe2i 9 | UaB0c4s6EyGqHXh1ISQfxkkiQKlyn0ihod2+kUonSFbnD7dqL/f5VvedIgTqsymK 10 | 8US1E8neJZVvguE2xLejDSg3YYGxU6RnOrjyj4lVqakfMlIl4xOA23ik4H9LEWwz 11 | ZZ2FT8p5bNHxi2iu4iSCN5OoqdOC7dc3+qqe7lsff61db+0ntck4PBYz6d9SkPF6 12 | i1Si4OdAnqRhS2gns9c4Xjb1iI4WZ16B3zSNWM92n+nFV4c6QluSRq4nWwmT2wLD 13 | e2XLycHwe2qOFSJCJxEjfActdrqx0Bhl5OFYoNxuMVT0FK4cdphjDKdO5XiyfQgA 14 | wUiUcp6YmWE5fG0GzyCGjwl2OeVl+4fVDuZ5zLyqdL1BWmhXdwTB9kQYTQBPdMqp 15 | 5HH9g2WgBda8U+VOXA4QM9Jt6RNCNJWh4VLIo4DEireTARfMHdljAgMBAAGjgesw 16 | gegwDAYDVR0TAQH/BAIwADAxBglghkgBhvhCAQ0EJBYiUHVwcGV0IFNlcnZlciBJ 17 | bnRlcm5hbCBDZXJ0aWZpY2F0ZTAfBgNVHSMEGDAWgBQS9+kwbcZky7egkJBpsjnf 18 | OTEuUzAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/ 19 | BAQDAgWgMB0GA1UdDgQWBBSQWFEuHHH7DaaBoSOjRijaSmcGsTAVBgsrBgEEAYKM 20 | TAEDJwQGDAR0cnVlMBwGA1UdEQQVMBOCBnB1cHBldIIJbG9jYWxob3N0MA0GCSqG 21 | SIb3DQEBCwUAA4ICAQAehauayF0L+E/l2Nts0QH4vlEJjQIUdB1Zc7l7dqSetANB 22 | vfB/erwBHRmi061Daadm4R9P40g1gKkAYP4vF4JTc31UI4dUtFyXWKzXjQYbG7wg 23 | 25L7ioj0Y0mOamGIK/am2b6v56OgAhv9CxCAM7NG+uotLomgmcKS2ROiWH/kfNIU 24 | YKgO/PkEJsY3G2gCPpnYkzxXsYZxSAnb4++O9WE0OoNS42PZ9mKGMLlTneANkFeB 25 | Pq+85Po1DlOFs7l9yhrieC3MmUXzqahah53MhBv2Ap6qm4+AQq1NiP6Kyh9Pp1yV 26 | 6aPxR0+b5tWLtd7oeSBbBTHYEz3ToLbzdXbjDnAvBikQI6W/ff7ffi3g8Zb/ZJZy 27 | n9DZAU7zpNS8F0m6Ejfcpxjbi4exJHxk9F527W/rT1Rzal1Qr4faY5U9lCLJthg0 28 | 6VX9rGVv16IJnabRWyRUcWLwVsYZfvieAnhbFZFMWyI0b22eqRzVqDHgTJzVfMhJ 29 | GRfUJ5Wcw0YQyNgvp/dhAgNHzcxz/bMW7dAZjDAJuadw2elK3cH2BkbGeJiPZoYn 30 | RP+8scFvl4iEPcWEOYW3Wu8/JF0ccXj4xKUCBh3WOwe4JIQ+sidqN9GxH1bIuquP 31 | 7gXLl/QjRQIh2NBh3pXdUgzb3VL2G9y0s91GlrgbtpiS+8XboVPMe/K/1xSokQ== 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/cli/time_shift_export_test.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.cli.time-shift-export-test 2 | (:require 3 | [clojure.test :refer [deftest is testing]] 4 | [puppetlabs.puppetdb.cli.time-shift-export :refer [time-shift-export-wrapper, file-exists?]] 5 | [puppetlabs.puppetdb.utils :as utils :refer [with-captured-throw]] 6 | [clojure.string :as str] 7 | [puppetlabs.kitchensink.core :as kitchensink]) 8 | (:import 9 | [clojure.lang ExceptionInfo])) 10 | 11 | (defn time-shift 12 | [& args] 13 | (with-redefs [utils/try-process-cli (fn [body] (body))] 14 | (time-shift-export-wrapper args))) 15 | 16 | (deftest returned-error 17 | (testing "when required input parameter is missing" 18 | (let [response (with-captured-throw (time-shift))] 19 | (is (= ExceptionInfo (class response))) 20 | (when (= ExceptionInfo (class response)) 21 | (is (= ::kitchensink/cli-error (:kind (ex-data response)))) 22 | (is (str/includes? (:msg (ex-data response)) 23 | "Missing required argument '--input'!"))))) 24 | 25 | (testing "when required input parameter is invalid" 26 | (with-redefs [file-exists? (fn [_] false)] 27 | (let [response (with-captured-throw (time-shift "-i" "/path/archive"))] 28 | (is (= ExceptionInfo (class response))) 29 | (when (= ExceptionInfo (class response)) 30 | (is (= ::kitchensink/cli-error (:kind (ex-data response)))) 31 | (is (str/includes? (:msg (ex-data response)) 32 | "Error: input archive path: /path/archive, must be valid!")))))) 33 | 34 | (testing "when provided data is invalid" 35 | (with-redefs [file-exists? (fn [_] true)] 36 | (let [response (with-captured-throw (time-shift "-i" "/path/archive" "-t" "invalid-date"))] 37 | (is (= ExceptionInfo (class response))) 38 | (when (= ExceptionInfo (class response)) 39 | (is (= ::kitchensink/cli-error (:kind (ex-data response)))) 40 | (is (str/includes? (:msg (ex-data response)) 41 | "Error: time shift date: invalid-date, must be in UTC format!"))))))) 42 | -------------------------------------------------------------------------------- /test-resources/puppetserver/ssl/ca/signed/localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFmjCCA4KgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBw 3 | ZXQgQ0E6IGxvY2FsaG9zdDAeFw0yMjAyMDgyMjQ5MTJaFw0zNzAyMDUyMjQ5MTRa 4 | MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC 5 | AgoCggIBALQWsayBs2PuA8ML3zxtveuqMc439r3UTkIEXlHrGP1zLuiuKKnRMe0L 6 | 7e0vITp5H0bzhHpDzUh/Sj2zwdCveqk8/wCk1YhUhM3TRHCjwwD4y1ELNQfJGOMp 7 | ntsurcidlFZfTMm/MTv+96O1kX3dJiLZBajeA0NmPo69T5ndWkGTlMtUKkHkoG6X 8 | xEwLtg1YEFHeolv8HALzuvtDKiS+/exbuwtOsNJ03tuPyBVNixGN8bgL/0VgKe2i 9 | UaB0c4s6EyGqHXh1ISQfxkkiQKlyn0ihod2+kUonSFbnD7dqL/f5VvedIgTqsymK 10 | 8US1E8neJZVvguE2xLejDSg3YYGxU6RnOrjyj4lVqakfMlIl4xOA23ik4H9LEWwz 11 | ZZ2FT8p5bNHxi2iu4iSCN5OoqdOC7dc3+qqe7lsff61db+0ntck4PBYz6d9SkPF6 12 | i1Si4OdAnqRhS2gns9c4Xjb1iI4WZ16B3zSNWM92n+nFV4c6QluSRq4nWwmT2wLD 13 | e2XLycHwe2qOFSJCJxEjfActdrqx0Bhl5OFYoNxuMVT0FK4cdphjDKdO5XiyfQgA 14 | wUiUcp6YmWE5fG0GzyCGjwl2OeVl+4fVDuZ5zLyqdL1BWmhXdwTB9kQYTQBPdMqp 15 | 5HH9g2WgBda8U+VOXA4QM9Jt6RNCNJWh4VLIo4DEireTARfMHdljAgMBAAGjgesw 16 | gegwDAYDVR0TAQH/BAIwADAxBglghkgBhvhCAQ0EJBYiUHVwcGV0IFNlcnZlciBJ 17 | bnRlcm5hbCBDZXJ0aWZpY2F0ZTAfBgNVHSMEGDAWgBQS9+kwbcZky7egkJBpsjnf 18 | OTEuUzAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/ 19 | BAQDAgWgMB0GA1UdDgQWBBSQWFEuHHH7DaaBoSOjRijaSmcGsTAVBgsrBgEEAYKM 20 | TAEDJwQGDAR0cnVlMBwGA1UdEQQVMBOCBnB1cHBldIIJbG9jYWxob3N0MA0GCSqG 21 | SIb3DQEBCwUAA4ICAQAehauayF0L+E/l2Nts0QH4vlEJjQIUdB1Zc7l7dqSetANB 22 | vfB/erwBHRmi061Daadm4R9P40g1gKkAYP4vF4JTc31UI4dUtFyXWKzXjQYbG7wg 23 | 25L7ioj0Y0mOamGIK/am2b6v56OgAhv9CxCAM7NG+uotLomgmcKS2ROiWH/kfNIU 24 | YKgO/PkEJsY3G2gCPpnYkzxXsYZxSAnb4++O9WE0OoNS42PZ9mKGMLlTneANkFeB 25 | Pq+85Po1DlOFs7l9yhrieC3MmUXzqahah53MhBv2Ap6qm4+AQq1NiP6Kyh9Pp1yV 26 | 6aPxR0+b5tWLtd7oeSBbBTHYEz3ToLbzdXbjDnAvBikQI6W/ff7ffi3g8Zb/ZJZy 27 | n9DZAU7zpNS8F0m6Ejfcpxjbi4exJHxk9F527W/rT1Rzal1Qr4faY5U9lCLJthg0 28 | 6VX9rGVv16IJnabRWyRUcWLwVsYZfvieAnhbFZFMWyI0b22eqRzVqDHgTJzVfMhJ 29 | GRfUJ5Wcw0YQyNgvp/dhAgNHzcxz/bMW7dAZjDAJuadw2elK3cH2BkbGeJiPZoYn 30 | RP+8scFvl4iEPcWEOYW3Wu8/JF0ccXj4xKUCBh3WOwe4JIQ+sidqN9GxH1bIuquP 31 | 7gXLl/QjRQIh2NBh3pXdUgzb3VL2G9y0s91GlrgbtpiS+8XboVPMe/K/1xSokQ== 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /test/puppetlabs/puppetdb/integration/file_with_binary_template.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.integration.file-with-binary-template 2 | (:require 3 | [clojure.test :refer :all] 4 | [clojure.string :as str] 5 | [me.raynes.fs :as fs] 6 | [puppetlabs.puppetdb.integration.fixtures :as int])) 7 | 8 | (deftest ^:integration file-with-binary-template 9 | ;; Puppet 8 removes the ability to serialize binary files without using the 10 | ;; rich data type Binary, or installing the puppet-pson gem 11 | (when (< 8 12 | (->> (int/bundle-exec {} "puppet" "--version") 13 | :out 14 | str/trim-newline 15 | (re-matches #"^(\d+).*") 16 | second 17 | (Integer/parseInt))) 18 | (with-open [pg (int/setup-postgres) 19 | pdb (int/run-puppetdb pg {}) 20 | ps (int/run-puppet-server [pdb] {})] 21 | 22 | (let [temp-dir (fs/temp-dir "file-with-binary-template") 23 | file-resource-path (str temp-dir "/binary-file") 24 | binary-template-path (fs/absolute "test-resources/binary-template.erb")] 25 | 26 | (testing "Run puppet with a binary template" 27 | (int/run-puppet ps pdb 28 | (str "file { '" file-resource-path "':" 29 | " content => template('" binary-template-path "')," 30 | " tag => 'binary_file'," 31 | "}") 32 | {:certname "binary-file-agent" 33 | ;; this is a workaround for puppet's present inability to automatically 34 | ;; downgrade from json to pson; it will be removed once 35 | ;; that feature is added. 36 | :extra-puppet-args ["--preferred_serialization_format" "pson"]})) 37 | 38 | (testing "PDB should have stored the resource" 39 | (is (= {:certname "binary-file-agent" 40 | :type "File" 41 | :title file-resource-path} 42 | (first (int/pql-query pdb "resources [certname, type, title] { tag = 'binary_file' }"))))))))) 43 | -------------------------------------------------------------------------------- /test-resources/puppetlabs/puppetdb/cli/export/tiny-catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "certname" : "myhost.localdomain", 3 | "code_id" : null, 4 | "producer_timestamp": "2014-07-10T22:33:54.781Z", 5 | "edges" : [ { 6 | "relationship" : "contains", 7 | "target" : { 8 | "title" : "/etc/apt/preferences.d/puppetlabs.pref", 9 | "type" : "File" 10 | }, 11 | "source" : { 12 | "title" : "puppetlabs", 13 | "type" : "Apt::Pin" 14 | } 15 | } ], 16 | "resources" : [ { 17 | "exported" : false, 18 | "title" : "/etc/apt/preferences.d/puppetlabs.pref", 19 | "line" : 60, 20 | "parameters" : { 21 | "group" : "root", 22 | "mode" : "0644", 23 | "content" : "Package: *\nPin: origin \"apt.puppetlabs.com\"\nPin-Priority: 900\n", 24 | "ensure" : "present", 25 | "owner" : "root" 26 | }, 27 | "tags" : [ "file", "apt::pin", "apt", "pin", "puppetlabs", "class", "os::linux::debian", "os", "linux", "debian", "os::linux", "role::base", "role", "base", "role::server", "server", "node", "myhost.localdomain" ], 28 | "type" : "File", 29 | "file" : "/Users/nicklewis/projects/puppetlabs-modules/dist/apt/manifests/pin.pp" 30 | }, { 31 | "exported" : false, 32 | "title" : "puppetlabs", 33 | "line" : 74, 34 | "parameters" : { 35 | "priority" : "900", 36 | "origin" : "apt.puppetlabs.com", 37 | "wildcard" : true, 38 | "ensure" : "present", 39 | "release" : "" 40 | }, 41 | "tags" : [ "apt::pin", "apt", "pin", "puppetlabs", "class", "os::linux::debian", "os", "linux", "debian", "os::linux", "role::base", "role", "base", "role::server", "server", "node", "myhost.localdomain" ], 42 | "type" : "Apt::Pin", 43 | "file" : "/Users/nicklewis/projects/puppetlabs-modules/site/os/manifests/linux/debian.pp" 44 | } ], 45 | "version" : "1330463446", 46 | "transaction_uuid" : "68b08e2a-eeb1-4322-b241-bfdf151d294c", 47 | "catalog_uuid" : "68b08e2a-eeb1-4322-b241-bfdf151d294c", 48 | "environment" : "DEV" 49 | } 50 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/query/resources.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.resources 2 | "Resource querying 3 | 4 | This implements resource querying, using the query compiler in 5 | `puppetlabs.puppetdb.query`, basically by munging the results into the 6 | right format and picking out the desired columns." 7 | (:import [org.postgresql.util PGobject]) 8 | (:require [puppetlabs.puppetdb.query :as query] 9 | [puppetlabs.puppetdb.schema :as pls] 10 | [puppetlabs.puppetdb.utils :as utils] 11 | [schema.spec.core :as spec] 12 | [schema.spec.leaf :as leaf] 13 | [schema.core :as s])) 14 | 15 | ;; SCHEMA 16 | 17 | (defrecord OptionalNameMatching [pattern] 18 | s/Schema 19 | (spec [this] (leaf/leaf-spec 20 | (spec/precondition this 21 | #(re-matches pattern (name %)) 22 | #(list 're-matches pattern %)))) 23 | (explain [_] '(re-matches pattern))) 24 | 25 | (defn optional-matching-keyword 26 | "A regex pattern to check the keyword for." 27 | [pattern] 28 | (->OptionalNameMatching pattern)) 29 | 30 | (def row-schema 31 | "Resource query row schema." 32 | (query/wrap-with-supported-fns 33 | {(s/optional-key :certname) s/Str 34 | (s/optional-key :environment) (s/maybe s/Str) 35 | (s/optional-key :exported) s/Bool 36 | (s/optional-key :file) (s/maybe s/Str) 37 | (s/optional-key :line) (s/maybe s/Int) 38 | (s/optional-key :parameters) (s/maybe PGobject) 39 | (optional-matching-keyword #"parameters\..*") (s/maybe PGobject) 40 | (s/optional-key :resource) s/Str 41 | (s/optional-key :tags) [(s/maybe s/Str)] 42 | (s/optional-key :title) s/Str 43 | (s/optional-key :type) s/Str})) 44 | 45 | ;; MUNGE 46 | 47 | (pls/defn-validated row->resource 48 | "Convert resource query row into a final resource format." 49 | [row :- row-schema] 50 | (utils/update-when row [:parameters] #(or % {}))) 51 | 52 | (pls/defn-validated munge-result-rows 53 | "Munge the result rows so that they will be compatible with the version 54 | specified API specification" 55 | [_ _] 56 | (fn [rows] 57 | (map row->resource rows))) 58 | -------------------------------------------------------------------------------- /documentation/api/wire_format/deactivate_node_format_v3.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Deactivate node wire format, version 3" 3 | layout: default 4 | canonical: "/puppetdb/latest/api/wire_format/deactivate_node_format_v3.html" 5 | --- 6 | 7 | # Deactivate node wire format - v3 8 | 9 | PuppetDB receives deactivate node commands from Puppet Servers in the following wire format. 10 | 11 | ## Deactivate node command format 12 | 13 | ### Version 14 | 15 | This is **version 3** of the deactivate node command. 16 | 17 | ### Encoding 18 | 19 | The command is serialized as JSON, which requires strict UTF-8 encoding. 20 | 21 | ### Upgrade notes 22 | 23 | Previous versions of this command required only the certname, as a raw JSON 24 | string. It is now formatted as a JSON map, and the `producer_timestamp` property 25 | has been added. 26 | 27 | ### Main data type: Deactivate node 28 | 29 | { 30 | "certname": , 31 | "producer_timestamp": 32 | } 33 | 34 | #### `certname` 35 | 36 | String. The name of the node for which the catalog was compiled. 37 | 38 | #### `producer_timestamp` 39 | 40 | DateTime. The time of command submission from the Puppet Server to PuppetDB, 41 | according to the clock on the Puppet Server. 42 | 43 | `producer_timestamp` is optional but *highly* recommended. When provided, it is 44 | used to determine the precedence between this command and other commands that 45 | modify the same node. This field is provided by, and should thus reflect the 46 | clock of, the Puppet Server. 47 | 48 | When `producer_timestamp` is not provided, the PuppetDB server's local time is 49 | used. If another command is received for a node while a non-timestamped 50 | "deactivate node" command is pending processing, the results are *undefined*. 51 | 52 | ### Data type: `` 53 | 54 | A JSON string. Because the command is UTF-8, these must also be UTF-8. 55 | 56 | ### Data type: `` 57 | 58 | A JSON string representing a date and time (with time zone), formatted based on 59 | the recommendations in ISO 8601. For example, for a UTC time, the string would be 60 | formatted as `YYYY-MM-DDThh:mm:ss.sssZ`. For non-UTC time, the `Z` may be replaced 61 | with `±hh:mm` to represent the specific timezone. 62 | -------------------------------------------------------------------------------- /src/puppetlabs/puppetdb/query/fact_contents.clj: -------------------------------------------------------------------------------- 1 | (ns puppetlabs.puppetdb.query.fact-contents 2 | (:require [puppetlabs.puppetdb.facts :as f] 3 | [puppetlabs.puppetdb.scf.storage-utils :as sutils] 4 | [puppetlabs.puppetdb.query :as query] 5 | [puppetlabs.puppetdb.schema :as pls] 6 | [puppetlabs.puppetdb.utils :as utils] 7 | [schema.core :as s])) 8 | 9 | (def row-schema 10 | (query/wrap-with-supported-fns 11 | {(s/optional-key :certname) s/Str 12 | (s/optional-key :environment) (s/maybe s/Str) 13 | (s/optional-key :path) [s/Str] 14 | (s/optional-key :path_types) s/Str 15 | (s/optional-key :name) s/Str 16 | (s/optional-key :value) (s/maybe s/Any)})) 17 | 18 | (def converted-row-schema 19 | (query/wrap-with-supported-fns 20 | {(s/optional-key :certname) s/Str 21 | (s/optional-key :environment) (s/maybe s/Str) 22 | (s/optional-key :path) f/fact-path 23 | (s/optional-key :name) s/Str 24 | (s/optional-key :value) s/Any})) 25 | 26 | (defn adjust-path-types 27 | "Adjusts the types in the :path based on the signature provided by 28 | path_types to ensure that array indexes are returned as integers 29 | rather than strings." 30 | [{:keys [path path_types] :as row}] 31 | (if-not path 32 | (do 33 | (assert (not path_types)) 34 | row) 35 | (do 36 | (assert path_types) 37 | (assert (= (count path) (count path_types))) 38 | (-> row 39 | (dissoc :path_types) 40 | (assoc :path (mapv (fn [^String part sig] 41 | (case sig 42 | \s part 43 | \i (Long/valueOf part))) 44 | path path_types)))))) 45 | 46 | (pls/defn-validated munge-result-row :- converted-row-schema 47 | "Coerce the value of a row to the proper type, and convert the path back to 48 | an array structure." 49 | [row :- row-schema] 50 | (-> (adjust-path-types row) 51 | (utils/update-when [:value] sutils/parse-db-json))) 52 | 53 | (pls/defn-validated munge-result-rows 54 | "Munge resulting rows for fact-contents endpoint." 55 | [_ _] 56 | (fn [rows] 57 | (map munge-result-row rows))) 58 | --------------------------------------------------------------------------------