pax_global_header00006660000000000000000000000064127121611220014505gustar00rootroot0000000000000052 comment=2d499449b38338e292cabc90a86420fbfb67a89c hiera-3.2.0/000077500000000000000000000000001271216112200125775ustar00rootroot00000000000000hiera-3.2.0/.gemspec000066400000000000000000000025551271216112200142320ustar00rootroot00000000000000# -*- encoding: utf-8 -*- # # PLEASE NOTE - This gemspec is intended for use with Bundler from source, not # for building the official Gem. Please see the [packaging # repository](https://github.com/puppetlabs/packaging) for information on how # to build release packages. begin require 'hiera/version' rescue LoadError $LOAD_PATH.unshift(File.expand_path("../lib", __FILE__)) require 'hiera/version' end Gem::Specification.new do |s| s.name = "hiera" version = Hiera.version mdata = version.match(/(\d+\.\d+\.\d+)/) s.version = mdata ? mdata[1] : version s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Puppet Labs"] s.date = "2013-04-03" s.description = "A pluggable data store for hierarcical data" s.email = "info@puppetlabs.com" s.executables = ["hiera"] s.files = ["bin/hiera"] s.homepage = "https://github.com/puppetlabs/hiera" s.require_paths = ["lib"] s.rubygems_version = "1.8.25" s.summary = "Light weight hierarchical data store" if s.respond_to? :specification_version then s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, [">= 0"]) end end hiera-3.2.0/.gitignore000066400000000000000000000002731271216112200145710ustar00rootroot00000000000000pkg test.rb ext/packaging .bundle/ vendor/ Gemfile.lock Gemfile.local /.project .idea/ /acceptance/.bundle /acceptance/junit /acceptance/log /acceptance/tmp /acceptance/merged_options.rb hiera-3.2.0/.travis.yml000066400000000000000000000006451271216112200147150ustar00rootroot00000000000000language: ruby sudo: false bundler_args: --without development script: "bundle exec rake $CHECK" notifications: email: false rvm: - 2.3.0 - 2.2.4 - 2.1.8 - 2.0.0 - 1.9.3 env: - "CHECK=spec" - "CHECK=commits" matrix: exclude: - rvm: 2.3.0 env: "CHECK=commits" - rvm: 2.2.4 env: "CHECK=commits" - rvm: 2.0.0 env: "CHECK=commits" - rvm: 1.9.3 env: "CHECK=commits" hiera-3.2.0/CONTRIBUTING.md000066400000000000000000000057721271216112200150430ustar00rootroot00000000000000# How to contribute Third-party patches are essential for keeping hiera great. We simply can't access the huge number of platforms and myriad configurations for running hiera. We want to keep it as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. ## Getting Started * Make sure you have a [Jira account](http://tickets.puppetlabs.com) * Make sure you have a [GitHub account](https://github.com/signup/free) * Submit a ticket for your issue, assuming one does not already exist. * Clearly describe the issue including steps to reproduce when it is a bug. * Make sure you fill in the earliest version that you know has the issue. * Fork the repository on GitHub ## Making Changes * Create a topic branch from where you want to base your work. * This is usually the master branch. * Only target release branches if you are certain your fix must be on that branch. * To quickly create a topic branch based on master; `git branch fix/master/my_contribution master` then checkout the new branch with `git checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch. * Make commits of logical units. * Check for unnecessary whitespace with `git diff --check` before committing. * Make sure your commit messages are in the proper format. ```` (#99999) Make the example in CONTRIBUTING imperative and concrete Without this patch applied the example commit message in the CONTRIBUTING document is not a concrete example. This is a problem because the contributor is left to imagine what the commit message should look like based on a description rather than an example. This patch fixes the problem by making the example concrete and imperative. The first line is a real life imperative statement with a ticket number from our issue tracker. The body describes the behavior without the patch, why this is a problem, and how the patch fixes the problem when applied. ```` * Make sure you have added the necessary tests for your changes. * Run _all_ the tests to assure nothing else was accidentally broken. ## Submitting Changes * Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla). * Push your changes to a topic branch in your fork of the repository. * Submit a pull request to the repository in the puppetlabs organization. * Update your ticket to mark that you have submitted code and are ready for it to be reviewed. * Include a link to the pull request in the ticket # Additional Resources * [More information on contributing](http://links.puppetlabs.com/contribute-to-puppet) * [Bug tracker (Jira)](http://tickets.puppetlabs.com) * [Contributor License Agreement](http://links.puppetlabs.com/cla) * [General GitHub documentation](http://help.github.com/) * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) * #puppet-dev IRC channel on freenode.org hiera-3.2.0/COPYING000066400000000000000000000261411271216112200136360ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2010, 2011 R.I.Pienaar, Puppet Labs Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hiera-3.2.0/Gemfile000066400000000000000000000025561271216112200141020ustar00rootroot00000000000000source ENV['GEM_SOURCE'] || "https://rubygems.org" gem "hiera", :path => File.dirname(__FILE__), :require => false gem 'deep_merge', :require => false group :development do gem 'watchr' end group :development, :test do gem 'rake', "~> 10.1.0" gem 'rspec', "~> 3.3", :require => false gem "rspec-legacy_formatters", "~> 1.0", :require => false gem 'mocha', "~> 0.10.5", :require => false gem 'json', "~> 1.7", :require => false, :platforms => :ruby gem "yarjuf", "~> 2.0" end require 'yaml' data = YAML.load_file(File.join(File.dirname(__FILE__), 'ext', 'project_data.yaml')) bundle_platforms = data['bundle_platforms'] data['gem_platform_dependencies'].each_pair do |gem_platform, info| next if gem_platform =~ /mingw/ if bundle_deps = info['gem_runtime_dependencies'] bundle_platform = bundle_platforms[gem_platform] or raise "Missing bundle_platform" platform(bundle_platform.intern) do bundle_deps.each_pair do |name, version| gem(name, version, :require => false) end end end end platform(:mingw_19) do gem 'win32console', '~> 1.3.2', :require => false end mingw = [:mingw] mingw << :x64_mingw if Bundler::Dsl::VALID_PLATFORMS.include?(:x64_mingw) platform(*mingw) do gem 'win32-dir', '~> 0.4.8', :require => false end if File.exists? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end # vim:ft=ruby hiera-3.2.0/LICENSE000066400000000000000000000012321271216112200136020ustar00rootroot00000000000000Puppet - Automating Configuration Management. Copyright (C) 2012-2014 Puppet Labs Inc Puppet Labs can be contacted at: info@puppetlabs.com Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hiera-3.2.0/README.md000066400000000000000000000245651271216112200140720ustar00rootroot00000000000000# Hiera [![Build Status](https://travis-ci.org/puppetlabs/hiera.png?branch=master)](https://travis-ci.org/puppetlabs/hiera) A simple pluggable Hierarchical Database. - **Tutorials:** Check the docs directory for tutorials. ## Why? Hierarchical data is a good fit for the representation of infrastructure information. Consider the example of a typical company with 2 datacenters and on-site development, staging etc. All machines need: - ntp servers - sysadmin contacts By thinking about the data in a hierarchical manner you can resolve these to the most correct answer easily:
     /------------- DC1 -------------\             /------------- DC2 -------------\
    | ntpserver: ntp1.dc1.example.com |           | ntpserver: ntp1.dc2.example.com |
    | sysadmin: dc1noc@example.com    |           |                                 |
    | classes: users::dc1             |           | classes: users::dc2             |
     \-------------------------------/             \-------------------------------/
                                \                      /
                                  \                  /
                           /------------- COMMON -------------\
                          | ntpserver: 1.pool.ntp.org          |
                          | sysadmin: "sysadmin@%{domain}"     |
                          | classes: users::common             |
                           \----------------------------------/
In this simple example machines in DC1 and DC2 have their own NTP servers, additionaly DC1 has its own sysadmin contact - perhaps because its a remote DR site - while DC2 and all the other environments would revert to the common contact that would have the machines domain fact expanded into the result. The _classes_ variable can be searched using the array method which would build up a list of classes to include on a node based on the hierarchy. Machines in DC1 would have the classes _users::common_ and _users::dc1_. The other environment like development and staging would all use the public NTP infrastructure. This is the data model that extlookup() have promoted in Puppet, Hiera has taken this data model and extracted it into a standalone project that is pluggable and have a few refinements over extlookup. ## Enhancements over Extlookup Extlookup had just one backend, Hiera can be extended with your own backends and represent a few enhancements over the base Extlookup approach thanks to this. ### Multiple backends are queried If you have a YAML and Puppet backend loaded and your users provide module defaults in the Puppet backend you can use your YAML data to override the Puppet data. If the YAML doesnt provide an answer the Puppet backend will get an opportunity to provide an answer. ### More scope based variable expansion Extlookup could parse data like %{foo} into a scope lookup for the variable foo. Hiera retains this ability and any Arrays or Hashes will be recursively searched for all strings that will then be parsed. The datadir and defaults are now also subject to variable parsing based on scope. ### No CSV support by default We have not at present provided a backward compatible CSV backend. A converter to YAML or JSON should be written. When the CSV backend was first chosen for Puppet the Puppet language only supports strings and arrays of strings which mapped well to CSV. Puppet has become (a bit) better wrt data and can now handle hashes and arrays of hashes so it's a good time to retire the old data format. ### Array Searches Hiera can search through all the tiers in a hierarchy and merge the result into a single array. This is used in the hiera-puppet project to replace External Node Classifiers by creating a Hiera compatible include function. ### Qualified Key Lookup You can use a qualified key to lookup a value that is contained inside a hash or array:
$ hiera user
{"name"=>"kim", "home"=>"/home/kim"}
$ hiera user.name
kim
$ hiera ssh_users
["root", "jeff", "gary", "hunter"]
$ hiera ssh_users.0
root
### Use quotes to disable qualified key behavior In case you have dotted keys and thus want to avoid using the qualified key semantics, you can put segments of a dotted key, or the whole key, within quotes. Given the following data:
# yaml
a:
  b.c:
    d: 'Data for a => b.c => d'
it is possible to do a lookup of the data like this:
$ hiera 'a."b.c".d'
Data for a => b.c => d
Quoting works in interpolation expressions as well. Interpolating from global scope:
# yaml
other.key: 'scope data: %{a."b.c".d}'
or using an interpolation method:
# yaml
a:
  b.c:
    d: 'Data for a => b.c => d'
other.key: 'hiera data %{hiera("a.''b.c''.d")}'
Note that two single quotes are used to escape a single quote inside a single quoted string (that's YAML syntax, not Hiera) and that the quoted key must be quoted in turn. ## Future Enhancements * More backends should be created * A webservice that exposes the data * Tools to help maintain the data files. Ideally this would be Foreman and Dashboard with their own backends ## Installation Hiera is available as a Gem called _hiera_ and out of the box it comes with just a single YAML backend. Hiera is also available as a native package via apt (http://apt.puppetlabs.com) and yum (http://yum.puppetlabs.com). Instructions for adding these repositories can be found at http://docs.puppetlabs.com/guides/installation.html#debian-and-ubuntu and http://docs.puppetlabs.com/guides/installation.html#enterprise-linux respectively. At present JSON (github/ripienaar/hiera-json) and Puppet (hiera-puppet) backends are availble. ## Configuration You can configure Hiera using a YAML file or by providing it Hash data in your code. There isn't a default config path - the CLI script will probably assume _/etc/hiera.yaml_ though. The default data directory for file based storage is _/var/lib/hiera_. A sample configuration file can be seen here:
---
:backends:
  - yaml
  - puppet

:logger: console

:hierarchy:
  - "sites/%{location}"
  - common

:yaml:
   :datadir: /etc/puppetlabs/code/hieradata

:puppet:
   :datasource: data
This configuration will require YAML files in _/etc/puppetlabs/code/hieradata_ these need to contain Hash data, sample files matching the hierarchy described in the _Why?_ section are below: _/etc/puppetlabs/code/hieradata/sites/dc1.yaml_:
---
ntpserver: ntp1.dc1.example.com
sysadmin: dc1noc@example.com
_/etc/puppetlabs/code/hieradata/sites/dc2.yaml_:
---
ntpserver: ntp1.dc2.example.com
_/etc/puppetlabs/code/hieradata/common.yaml_:
---
sysadmin: "sysadmin@%{domain}"
ntpserver: 1.pool.ntp.org
## Querying from CLI You can query your data from the CLI. By default the CLI expects a config file in _/etc/hiera.yaml_ but you can pass _--config_ to override that. This example searches Hiera for node data. Scope is loaded from a Puppet created YAML facts store as found on your Puppet Masters. If no data is found and the facts had a location=dc1 fact the default would be _sites/dc1_
$ hiera acme_version 'sites/%{location}' --yaml /opt/puppetlabs/puppet/cache/yaml/facts/example.com.yaml
You can also supply extra facts on the CLI, assuming Puppet facts did not have a location fact:
$ hiera acme_version 'sites/%{location}' location=dc1 --yaml /opt/puppetlabs/puppet/cache/yaml/facts/example.com.yaml
Or if you use MCollective you can fetch the scope from a remote node's facts:
$ hiera acme_version 'sites/%{location}' -m box.example.com
You can also do array merge searches on the CLI:
$ hiera -a classes location=dc1
["users::common", "users::dc1"]
## Querying from code This is the same query programatically as in the above CLI example:
require 'rubygems'
require 'hiera'
require 'puppet'

# load the facts for example.com
scope = YAML.load_file("/opt/puppetlabs/puppet/cache/yaml/facts/example.com.yaml")

# create a new instance based on config file
hiera = Hiera.new(:config => "/etc/puppetlabs/code/hiera.yaml")

# resolve the 'acme_version' variable based on scope
#
# given a fact location=dc1 in the facts file this will default to a branch sites/dc1
# and allow hierarchical overrides based on the hierarchy defined in the config file
puts "ACME Software Version: %s" % [ hiera.lookup("acme_version", "sites/%{location}", scope) ]
## Extending There exist 2 backends at present in addition to the bundled YAML one. ### JSON This can be found on github under _ripienaar/hiera-json_. This is a good example of file based backends as Hiera provides a number of helpers to make writing these trivial. ### Puppet This is much more complex and queries the data from the running Puppet state, it's found on GitHub under _ripienaar/hiera-puppet_. This is a good example to learn how to map your internal program state into what Hiera wants as I needed to do with the Puppet Scope. It includes a Puppet Parser Function to query the data from within Puppet. When used in Puppet you'd expect Hiera to log using the Puppet infrastructure, this plugin includes a Puppet Logger plugin for Hiera that uses the normal Puppet logging methods for all logging. ## License See LICENSE file. ## Support Please log tickets and issues at our [JIRA tracker](http://tickets.puppetlabs.com). A [mailing list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is available for asking questions and getting help from others. In addition there is an active #puppet channel on Freenode. We use semantic version numbers for our releases, and recommend that users stay as up-to-date as possible by upgrading to patch releases and minor releases as they become available. Bugfixes and ongoing development will occur in minor releases for the current major version. Security fixes will be backported to a previous major version on a best-effort basis, until the previous major version is no longer maintained. For example: If a security vulnerability is discovered in Hiera 1.3.0, we would fix it in the 1 series, most likely as 1.3.1. Maintainers would then make a best effort to backport that fix onto the latest Hiera release they carry. Long-term support, including security patches and bug fixes, is available for commercial customers. Please see the following page for more details: [Puppet Enterprise Support Lifecycle](http://puppetlabs.com/misc/puppet-enterprise-lifecycle) ## MAINTAINERS * Thomas Hallgren * Henrik Lindberg hiera-3.2.0/Rakefile000066400000000000000000000055501271216112200142510ustar00rootroot00000000000000# These two need to be loaded sperately. # If we load packaging.rake first, and it isn't available, # running `rake spec` in a bare repo will fail since neither # rubygems nor `rake_task` will have loaded. However, when we load # packaging.rake last, package building fails. begin load File.join(File.dirname(__FILE__), 'ext', 'packaging', 'packaging.rake') rescue LoadError end begin require 'rubygems' require 'rspec/core/rake_task' rescue LoadError end Dir['tasks/**/*.rake'].each { |t| load t } build_defs_file = 'ext/build_defaults.yaml' if File.exist?(build_defs_file) begin require 'yaml' @build_defaults ||= YAML.load_file(build_defs_file) rescue Exception => e STDERR.puts "Unable to load yaml from #{build_defs_file}:" STDERR.puts e end @packaging_url = @build_defaults['packaging_url'] @packaging_repo = @build_defaults['packaging_repo'] raise "Could not find packaging url in #{build_defs_file}" if @packaging_url.nil? raise "Could not find packaging repo in #{build_defs_file}" if @packaging_repo.nil? namespace :package do desc "Bootstrap packaging automation, e.g. clone into packaging repo" task :bootstrap do if File.exist?("ext/#{@packaging_repo}") puts "It looks like you already have ext/#{@packaging_repo}. If you don't like it, blow it away with package:implode." else cd 'ext' do %x{git clone #{@packaging_url}} end end end desc "Remove all cloned packaging automation" task :implode do rm_rf "ext/#{@packaging_repo}" end end end task :spec do sh %{rspec #{ENV['TEST'] || ENV['TESTS'] || 'spec'}} end task :test => :spec desc "verify that commit messages match CONTRIBUTING.md requirements" task(:commits) do # This git command looks at the summary from every commit from this branch not in master. # Ideally this would compare against the branch that a PR is submitted against, but I don't # know how to get that information. Absent that, comparing with master should work in most cases. %x{git log --no-merges --pretty=%s master..$HEAD}.each_line do |commit_summary| # This regex tests for the currently supported commit summary tokens: maint, doc, packaging, or hi-. # The exception tries to explain it in more full. if /^\((maint|doc|packaging|hi-\d+)\)|revert/i.match(commit_summary).nil? raise "\n\n\n\tThis commit summary didn't match CONTRIBUTING.md guidelines:\n" \ "\n\t\t#{commit_summary}\n" \ "\tThe commit summary (i.e. the first line of the commit message) should start with one of:\n" \ "\t\t(hi-) # this is most common and should be a ticket at tickets.puppetlabs.com\n" \ "\t\t(doc)\n" \ "\t\t(maint)\n" \ "\t\t(packaging)\n" \ "\n\tThis test for the commit summary is case-insensitive.\n\n\n" end end end task :default do sh 'rake -T' end hiera-3.2.0/acceptance/000077500000000000000000000000001271216112200146655ustar00rootroot00000000000000hiera-3.2.0/acceptance/Gemfile000066400000000000000000000011651271216112200161630ustar00rootroot00000000000000source ENV['GEM_SOURCE'] || "https://rubygems.org" def location_for(place, fake_version = nil) if place =~ /^(git:[^#]*)#(.*)/ [fake_version, { :git => $1, :branch => $2, :require => false }].compact elsif place =~ /^file:\/\/(.*)/ ['>= 0', { :path => File.expand_path($1), :require => false }] else [place, { :require => false }] end end gem "beaker", *location_for(ENV['BEAKER_VERSION'] || "~> 2.32") gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'] || "~> 0.3") gem 'rake', "~> 10.1.0" if File.exists? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end hiera-3.2.0/acceptance/Rakefile000066400000000000000000000260351271216112200163400ustar00rootroot00000000000000require 'rake/clean' require 'pp' require 'yaml' require 'securerandom' require 'fileutils' require 'beaker-hostgenerator' require 'beaker/dsl/install_utils' extend Beaker::DSL::InstallUtils ONE_DAY_IN_SECS = 24 * 60 * 60 REPO_CONFIGS_DIR = "repo-configs" CLEAN.include('*.tar', REPO_CONFIGS_DIR, 'merged_options.rb') module HarnessOptions DEFAULTS = { :type => 'git', :helper => ['lib/helper.rb'], :tests => ['tests'], :log_level => 'debug', :color => false, :root_keys => true, :ssh => { :keys => ["~/.ssh/id_rsa-acceptance"], }, :xml => true, :timesync => false, :repo_proxy => true, :add_el_extras => false, :preserve_hosts => 'onfail', } class Aggregator attr_reader :mode def initialize(mode) @mode = mode end def get_options(file_path) puts file_path if File.exists? file_path options = eval(File.read(file_path), binding) else puts "No options file found at #{File.expand_path(file_path)}" end options || {} end def get_mode_options get_options("./config/#{mode}/options.rb") end def get_local_options get_options("./local_options.rb") end def final_options(intermediary_options = {}) mode_options = get_mode_options local_overrides = get_local_options final_options = DEFAULTS.merge(mode_options) final_options.merge!(intermediary_options) final_options.merge!(local_overrides) return final_options end end def self.options(mode, options) final_options = Aggregator.new(mode).final_options(options) final_options end end def beaker_test(mode = :aio, options = {}) final_options = HarnessOptions.options(mode, options) if mode == :git final_options[:install] ||= [] final_options[:install] << "#{build_git_url('hiera')}##{sha}" end options_file = 'merged_options.rb' File.open(options_file, 'w') do |merged| merged.puts <<-EOS # Copy this file to local_options.rb and adjust as needed if you wish to run # with some local overrides. EOS merged.puts(final_options.pretty_inspect) end tests = ENV['TESTS'] || ENV['TEST'] tests_opt = "--tests=#{tests}" if tests target = ENV['TEST_TARGET'] if config and File.exists?(config) config_opt = "--hosts=#{config}" elsif target cli = BeakerHostGenerator::CLI.new([target, '--disable-default-role', '--osinfo-version', '1']) ENV['BEAKER_HOSTS'] = "tmp/#{target}-#{SecureRandom.uuid}.yaml" FileUtils.mkdir_p('tmp') File.open(config, 'w') do |fh| fh.print(cli.execute) end config_opt = "--hosts=#{config}" end overriding_options = ENV['OPTIONS'] args = ["--options-file", options_file, config_opt, tests_opt, overriding_options].compact begin sh("beaker", *args) ensure if (hosts_file = config || final_options[:hosts_file]) && hosts_file !~ /preserved_config/ cp(hosts_file, "log/latest/config.yml") generate_config_for_latest_hosts if final_options[:preserve_hosts] || overriding_options =~ /--preserve-hosts/ end mv(options_file, "log/latest") puts "\n\n" end end def generate_config_for_latest_hosts preserved_config_hash = { 'HOSTS' => {} } config_hash = YAML.load_file('log/latest/config.yml').to_hash nodes = config_hash['HOSTS'].map do |node_label,hash| { :node_label => node_label, :platform => hash['platform'] } end pre_suite_log = File.read('log/latest/pre_suite-run.log') nodes.each do |node_info| possible_hostname_match = /^(\w+) \(#{node_info[:node_label]}\)/.match(pre_suite_log) hostname = (possible_hostname_match || Array.new)[1] fqdn = hostname ? "#{hostname}.delivery.puppetlabs.net" : "unknown" preserved_config_hash['HOSTS'][fqdn] = { 'roles' => [ 'agent'], 'platform' => node_info[:platform], } preserved_config_hash['HOSTS'][fqdn]['roles'].unshift('master') if node_info[:node_label] =~ /master/ end puts "\n\n" pp preserved_config_hash puts "\n\n" File.open('log/latest/preserved_config.yaml', 'w') do |config_file| YAML.dump(preserved_config_hash, config_file) end rescue Errno::ENOENT => e puts "Couldn't generate log #{e}" end def list_preserved_configurations(secs_ago = ONE_DAY_IN_SECS) preserved = {} Dir.glob('log/*_*').each do |dir| preserved_config_path = "#{dir}/preserved_config.yaml" yesterday = Time.now - secs_ago.to_i if preserved_config = File.exists?(preserved_config_path) directory = File.new(dir) if directory.ctime > yesterday hosts = [] preserved_config = YAML.load_file(preserved_config_path).to_hash preserved_config['HOSTS'].each do |hostname,values| hosts << "#{hostname}: #{values['platform']}, #{values['roles']}" end preserved[hosts] = directory.to_path end end end preserved.map { |k,v| [v,k] }.sort { |a,b| a[0] <=> b[0] }.reverse end def list_preserved_hosts(secs_ago = ONE_DAY_IN_SECS) hosts = Set.new Dir.glob('log/**/pre*suite*run.log').each do |log| yesterday = Time.now - secs_ago.to_i File.open(log, 'r') do |file| if file.ctime > yesterday file.each_line do |line| matchdata = /^(\w+) \(.*?\) \$/.match(line.encode!('UTF-8', 'UTF-8', :invalid => :replace)) hosts.add(matchdata[1]) if matchdata end end end end hosts end # Plagiarized from Beaker::Vcloud#cleanup def destroy_preserved_hosts(hosts = nil, secs_ago = ONE_DAY_IN_SECS) secs_ago ||= ONE_DAY_IN_SECS hosts ||= list_preserved_hosts(secs_ago) require 'beaker/hypervisor/vsphere_helper' vsphere_credentials = VsphereHelper.load_config("#{ENV['HOME']}/.fog") puts "Connecting to vSphere at #{vsphere_credentials[:server]}" + " with credentials for #{vsphere_credentials[:user]}" vsphere_helper = VsphereHelper.new( vsphere_credentials ) vm_names = hosts.to_a pp vm_names vms = vsphere_helper.find_vms vm_names vm_names.each do |name| unless vm = vms[name] puts "Couldn't find VM #{name} in vSphere!" next end if vm.runtime.powerState == 'poweredOn' puts "Shutting down #{vm.name}" start = Time.now vm.PowerOffVM_Task.wait_for_completion puts "Spent %.2f seconds halting #{vm.name}" % (Time.now - start) end start = Time.now vm.Destroy_Task puts "Spent %.2f seconds destroying #{vm.name}" % (Time.now - start) end vsphere_helper.close end def print_preserved(preserved) preserved.each_with_index do |entry,i| puts "##{i}: #{entry[0]}" entry[1].each { |h| puts " #{h}" } end end def beaker_run_type type = ENV['TYPE'] || :aio type = type.to_sym end def sha ENV['SHA'] end def config ENV['BEAKER_HOSTS'] end namespace :ci do namespace :git do task :test do sh("beaker", "--type", "git", "--load-path", "lib", "--hosts", ENV['BEAKER_HOSTS'], "--pre-suite", "setup/git", "--install", "PUPPET/#{ENV['PUPPET'] || 'master'},FACTER/#{ENV['FACTER'] || 'master'},HIERA/#{ENV['HIERA'] || 'master'}", "--tests", "tests", "--ntp", "--xml", "--debug", "--timeout", "1200", "--add-el-extras", "--keyfile", ENV['KEY'] || "#{ENV['HOME']}/.ssh/id_rsa") end end task :check_env do raise(USAGE) unless sha end namespace :test do USAGE = <<-EOS Requires commit SHA to be put under test as environment variable: SHA=''. Also must set BEAKER_HOSTS=config/nodes/foo.yaml or include it in an options.rb for Beaker, or specify TEST_TARGET in a form beaker-hostgenerator accepts, e.g. ubuntu1504-64a. You may set TESTS=path/to/test,and/more/tests. You may set additional Beaker OPTIONS='--more --options' If testing from git checkouts, you may optionally set the github fork to checkout from using FORK='other-puppet-fork'. You may also optionally set the git server to checkout from using GIT_SERVER='my.host.with.git.daemon', if you have set up a `git daemon` to pull local commits from. (In this case, the FORK should be set to the path to the repository, and you will need to allow the git daemon to serve the repo (see `git help daemon`)). If there is a Beaker options hash in a ./local_options.rb, it will be included. Commandline options set through the above environment variables will override settings in this file. EOS desc <<-EOS Run the acceptance tests through Beaker and install packages as part of the AIO puppet-agent installation. #{USAGE} EOS task :aio => 'ci:check_env' do beaker_test(:aio) end desc <<-EOS Run the acceptance tests through Beaker and install from git on the configuration targets. #{USAGE} EOS task :git => 'ci:check_env' do beaker_test(:git) end end desc "Capture the master and agent hostname from the latest log and construct a preserved_config.yaml for re-running against preserved hosts without provisioning." task :extract_preserved_config do generate_config_for_latest_hosts end desc <<-EOS Run an acceptance test for a given node configuration and preserve the hosts. Defaults to an aio run, but you can set it to 'git' with TYPE='git'. #{USAGE} EOS task :test_and_preserve_hosts => 'ci:check_env' do beaker_test(beaker_run_type, :preserve_hosts => 'always', :__preserve_config__ => true) end desc "List acceptance runs from the past day which had hosts preserved." task :list_preserved do preserved = list_preserved_configurations print_preserved(preserved) end desc <<-EOS Shutdown and destroy any hosts that we have preserved for testing. These should be reaped daily by scripts, but this will free up resources immediately. Specify a list of comma separated HOST_NAMES if you have a set of dynamic vcloud host names you want to purge outside of what can be grepped from the logs. You can go back through the last SECS_AGO logs. Default is one day ago in secs. EOS task :destroy_preserved_hosts do host_names = ENV['HOST_NAMES'].split(',') if ENV['HOST_NAMES'] secs_ago = ENV['SECS_AGO'] destroy_preserved_hosts(host_names, secs_ago) end desc <<-EOS Rerun an acceptance test using the last captured preserved_config.yaml to skip provisioning. Or specify a CONFIG_NUMBER from `rake ci:list_preserved`. Uses the setup/rsync/pre-suite to rsync the local puppet source onto master and agent. You may specify an RSYNC_FILTER_FILE as well. You may skip purging and reinstalling puppet packages by including SKIP_PACKAGE_REINSTALL. You may skip rsyncing local puppet files over to the tests hosts by including SKIP_RSYNC. Defaults to an aio run, but you can set it to 'git' with TYPE='git'. EOS task :test_against_preserved_hosts do config_number = (ENV['CONFIG_NUMBER'] || 0).to_i preserved = list_preserved_configurations print_preserved(preserved) config_path = preserved[config_number][0] puts "Using ##{config_number}: #{config_path}" beaker_test(beaker_run_type, :hosts_file => "#{config_path}/preserved_config.yaml", :no_provision => true, :preserve_hosts => 'always', :pre_suite => ['setup/rsync/pre-suite'] ) end end task :default do sh('rake -T') end hiera-3.2.0/acceptance/config/000077500000000000000000000000001271216112200161325ustar00rootroot00000000000000hiera-3.2.0/acceptance/config/aio/000077500000000000000000000000001271216112200167025ustar00rootroot00000000000000hiera-3.2.0/acceptance/config/aio/options.rb000066400000000000000000000003121271216112200207160ustar00rootroot00000000000000{ :type => 'aio', :pre_suite => [ 'setup/common/pre-suite/000-delete-puppet-when-none.rb', 'setup/aio/pre-suite/010_Install.rb', 'setup/aio/pre-suite/021_InstallAristaModule.rb', ], } hiera-3.2.0/acceptance/config/git/000077500000000000000000000000001271216112200167155ustar00rootroot00000000000000hiera-3.2.0/acceptance/config/git/options.rb000066400000000000000000000003251271216112200207350ustar00rootroot00000000000000{ :install => ['PUPPET/master', 'FACTER/2.x'], :pre_suite => [ 'setup/common/pre-suite/000-delete-puppet-when-none.rb', 'setup/common/00_EnvSetup.rb', 'setup/git/pre-suite/01_TestSetup.rb', ], } hiera-3.2.0/acceptance/config/nodes/000077500000000000000000000000001271216112200172425ustar00rootroot00000000000000hiera-3.2.0/acceptance/config/nodes/aix-53-power.yaml000066400000000000000000000001551271216112200222670ustar00rootroot00000000000000--- HOSTS: pe-aix-53-acceptance: roles: - agent platform: aix-5.3-power hypervisor: none hiera-3.2.0/acceptance/config/nodes/aix-61-power.yaml000066400000000000000000000001551271216112200222660ustar00rootroot00000000000000--- HOSTS: pe-aix-61-acceptance: roles: - agent platform: aix-6.1-power hypervisor: none hiera-3.2.0/acceptance/config/nodes/aix-71-power.yaml000066400000000000000000000001551271216112200222670ustar00rootroot00000000000000--- HOSTS: pe-aix-71-acceptance: roles: - agent platform: aix-7.1-power hypervisor: none hiera-3.2.0/acceptance/config/nodes/huaweios-6-powerpc.yaml000066400000000000000000000002171271216112200235720ustar00rootroot00000000000000--- HOSTS: huawei-ce6850-2-debian-vm-eth0.ops.puppetlabs.net: roles: - agent platform: huaweios-6-powerpc hypervisor: none hiera-3.2.0/acceptance/config/nodes/solaris-10-sparc.yaml000066400000000000000000000002571271216112200231320ustar00rootroot00000000000000--- HOSTS: solaris-10-sparc: roles: - agent platform: solaris-10-sparc hypervisor: none ip: 10.32.77.107 vmhostname: sol10.delivery.puppetlabs.net hiera-3.2.0/acceptance/config/nodes/solaris-11-sparc.yaml000066400000000000000000000002571271216112200231330ustar00rootroot00000000000000--- HOSTS: solaris-11-sparc: roles: - agent platform: solaris-11-sparc hypervisor: none ip: 10.32.77.138 vmhostname: sol11.delivery.puppetlabs.net hiera-3.2.0/acceptance/lib/000077500000000000000000000000001271216112200154335ustar00rootroot00000000000000hiera-3.2.0/acceptance/lib/helper.rb000066400000000000000000000000671271216112200172420ustar00rootroot00000000000000$LOAD_PATH << File.expand_path(File.dirname(__FILE__)) hiera-3.2.0/acceptance/lib/puppet/000077500000000000000000000000001271216112200167505ustar00rootroot00000000000000hiera-3.2.0/acceptance/lib/puppet/acceptance/000077500000000000000000000000001271216112200210365ustar00rootroot00000000000000hiera-3.2.0/acceptance/lib/puppet/acceptance/common_utils.rb000066400000000000000000000007431271216112200240770ustar00rootroot00000000000000module Puppet module Acceptance module CommandUtils def ruby_command(host) "env PATH=\"#{host['privatebindir']}:${PATH}\" ruby" end module_function :ruby_command def gem_command(host) if host['platform'] =~ /windows/ "env PATH=\"#{host['privatebindir']}:${PATH}\" cmd /c gem" else "env PATH=\"#{host['privatebindir']}:${PATH}\" gem" end end module_function :gem_command end end end hiera-3.2.0/acceptance/lib/puppet/acceptance/install_utils.rb000066400000000000000000000043431271216112200242550ustar00rootroot00000000000000require 'open-uri' require 'open3' require 'uri' require 'puppet/acceptance/common_utils' module Puppet module Acceptance module InstallUtils PLATFORM_PATTERNS = { :redhat => /fedora|el-|centos/, :debian => /debian|ubuntu/, :debian_ruby18 => /debian|ubuntu-lucid|ubuntu-precise/, :solaris => /solaris/, :windows => /windows/, }.freeze # Installs packages on the hosts. # # @param hosts [Array] Array of hosts to install packages to. # @param package_hash [Hash{Symbol=>Array>}] # Keys should be a symbol for a platform in PLATFORM_PATTERNS. Values # should be an array of package names to install, or of two element # arrays where a[0] is the command we expect to find on the platform # and a[1] is the package name (when they are different). # @param options [Hash{Symbol=>Boolean}] # @option options [Boolean] :check_if_exists First check to see if # command is present before installing package. (Default false) # @return true def install_packages_on(hosts, package_hash, options = {}) check_if_exists = options[:check_if_exists] hosts = [hosts] unless hosts.kind_of?(Array) hosts.each do |host| package_hash.each do |platform_key,package_list| if pattern = PLATFORM_PATTERNS[platform_key] if pattern.match(host['platform']) package_list.each do |cmd_pkg| if cmd_pkg.kind_of?(Array) command, package = cmd_pkg else command = package = cmd_pkg end if !check_if_exists || !host.check_for_package(command) host.logger.notify("Installing #{package}") additional_switches = '--allow-unauthenticated' if platform_key == :debian host.install_package(package, additional_switches) end end end else raise("Unknown platform '#{platform_key}' in package_hash") end end end return true end end end end hiera-3.2.0/acceptance/setup/000077500000000000000000000000001271216112200160255ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/aio/000077500000000000000000000000001271216112200165755ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/aio/pre-suite/000077500000000000000000000000001271216112200205125ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/aio/pre-suite/010_Install.rb000066400000000000000000000011761271216112200230320ustar00rootroot00000000000000require 'puppet/acceptance/common_utils' require 'beaker/dsl/install_utils' extend Beaker::DSL::InstallUtils test_name "Install Packages" step "Install puppet-agent..." do opts = { :puppet_collection => 'PC1', :puppet_agent_sha => ENV['SHA'], :puppet_agent_version => ENV['SUITE_VERSION'] || ENV['SHA'] } install_puppet_agent_dev_repo_on(hosts, opts) end # make sure install is sane, beaker has already added puppet and ruby # to PATH in ~/.ssh/environment agents.each do |agent| on agent, puppet('--version') ruby = Puppet::Acceptance::CommandUtils.ruby_command(agent) on agent, "#{ruby} --version" end hiera-3.2.0/acceptance/setup/aio/pre-suite/021_InstallAristaModule.rb000066400000000000000000000007771271216112200253540ustar00rootroot00000000000000platforms = hosts.map{|val| val[:platform]} skip_test "No arista hosts present" unless platforms.any? { |val| /^eos-/ =~ val } test_name 'Arista Switch Pre-suite' do switchs = select_hosts({:platform => ['eos-4-i386']}) step 'add puppet user to switch' do switchs.each do |switch| on(switch, "/opt/puppetlabs/bin/puppet config --confdir /etc/puppetlabs/puppet set user root") on(switch, "/opt/puppetlabs/bin/puppet config --confdir /etc/puppetlabs/puppet set group root") end end end hiera-3.2.0/acceptance/setup/common/000077500000000000000000000000001271216112200173155ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/common/00_EnvSetup.rb000066400000000000000000000024631271216112200217170ustar00rootroot00000000000000test_name "Setup environment" step "Ensure Git and Ruby" require 'puppet/acceptance/install_utils' extend Puppet::Acceptance::InstallUtils require 'beaker/dsl/install_utils' extend Beaker::DSL::InstallUtils PACKAGES = { :redhat => [ 'git', 'ruby', 'rubygem-json', ], :debian => [ ['git', 'git-core'], 'ruby', ], :debian_ruby18 => [ 'libjson-ruby', ], :solaris => [ ['git', 'developer/versioning/git'], ['ruby', 'runtime/ruby-18'], # there isn't a package for json, so it is installed later via gems ], :windows => [ 'git', # there isn't a need for json on windows because it is bundled in ruby 1.9 ], } install_packages_on(hosts, PACKAGES, :check_if_exists => true) hosts.each do |host| case host['platform'] when /windows/ step "#{host} Install ruby from git" install_from_git(host, "/opt/puppet-git-repos", :name => 'puppet-win32-ruby', :path => 'git://github.com/puppetlabs/puppet-win32-ruby') on host, 'cd /opt/puppet-git-repos/puppet-win32-ruby; cp -r ruby/* /' on host, 'cd /lib; icacls ruby /grant "Everyone:(OI)(CI)(RX)"' on host, 'cd /lib; icacls ruby /reset /T' on host, 'ruby --version' on host, 'cmd /c gem list' when /solaris/ step "#{host} Install json from rubygems" on host, 'gem install json' end end hiera-3.2.0/acceptance/setup/common/pre-suite/000077500000000000000000000000001271216112200212325ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/common/pre-suite/000-delete-puppet-when-none.rb000066400000000000000000000004331271216112200265250ustar00rootroot00000000000000test_name "Expunge puppet bits if hypervisor is none" # Ensure that the any previous installations of puppet # are removed from the host if it is not managed by a # provisioning hypervisor. hosts.each do |host| if host[:hypervisor] == "none" remove_puppet_on(host) end end hiera-3.2.0/acceptance/setup/git/000077500000000000000000000000001271216112200166105ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/git/pre-suite/000077500000000000000000000000001271216112200205255ustar00rootroot00000000000000hiera-3.2.0/acceptance/setup/git/pre-suite/00_EnvSetup.rb000066400000000000000000000024631271216112200231270ustar00rootroot00000000000000test_name "Setup environment" step "Ensure Git and Ruby" require 'puppet/acceptance/install_utils' extend Puppet::Acceptance::InstallUtils require 'beaker/dsl/install_utils' extend Beaker::DSL::InstallUtils PACKAGES = { :redhat => [ 'git', 'ruby', 'rubygem-json', ], :debian => [ ['git', 'git-core'], 'ruby', ], :debian_ruby18 => [ 'libjson-ruby', ], :solaris => [ ['git', 'developer/versioning/git'], ['ruby', 'runtime/ruby-18'], # there isn't a package for json, so it is installed later via gems ], :windows => [ 'git', # there isn't a need for json on windows because it is bundled in ruby 1.9 ], } install_packages_on(hosts, PACKAGES, :check_if_exists => true) hosts.each do |host| case host['platform'] when /windows/ step "#{host} Install ruby from git" install_from_git(host, "/opt/puppet-git-repos", :name => 'puppet-win32-ruby', :path => 'git://github.com/puppetlabs/puppet-win32-ruby') on host, 'cd /opt/puppet-git-repos/puppet-win32-ruby; cp -r ruby/* /' on host, 'cd /lib; icacls ruby /grant "Everyone:(OI)(CI)(RX)"' on host, 'cd /lib; icacls ruby /reset /T' on host, 'ruby --version' on host, 'cmd /c gem list' when /solaris/ step "#{host} Install json from rubygems" on host, 'gem install json' end end hiera-3.2.0/acceptance/setup/git/pre-suite/01_TestSetup.rb000077500000000000000000000017741271216112200233260ustar00rootroot00000000000000test_name "Install packages and repositories on target machines..." do extend Beaker::DSL::InstallUtils SourcePath = Beaker::DSL::InstallUtils::SourcePath GitURI = Beaker::DSL::InstallUtils::GitURI GitHubSig = Beaker::DSL::InstallUtils::GitHubSig tmp_repositories = [] options[:install].each do |uri| raise(ArgumentError, "#{uri} is not recognized.") unless(uri =~ GitURI) tmp_repositories << extract_repo_info_from(uri) end repositories = order_packages(tmp_repositories) versions = {} hosts.each_with_index do |host, index| on host, "echo #{GitHubSig} >> $HOME/.ssh/known_hosts" repositories.each do |repository| step "Install #{repository[:name]}" install_from_git host, SourcePath, repository if index == 1 versions[repository[:name]] = find_git_repo_versions(host, SourcePath, repository) end end end end hiera-3.2.0/acceptance/tests/000077500000000000000000000000001271216112200160275ustar00rootroot00000000000000hiera-3.2.0/acceptance/tests/yaml_backend/000077500000000000000000000000001271216112200204405ustar00rootroot00000000000000hiera-3.2.0/acceptance/tests/yaml_backend/00-setup.rb000066400000000000000000000010511271216112200223370ustar00rootroot00000000000000test_name "Hiera setup for YAML backend" agents.each do |agent| codedir = agent.puppet['codedir'] hieradatadir = File.join(codedir, 'hieradata') apply_manifest_on agent, <<-PP file { '#{codedir}/hiera.yaml': ensure => present, content => '--- :backends: - "yaml" :logger: "console" :hierarchy: - "%{fqdn}" - "%{environment}" - "global" :yaml: :datadir: "#{hieradatadir}" ' } file { '#{hieradatadir}': ensure => directory, recurse => true, purge => true, force => true, } PP end hiera-3.2.0/acceptance/tests/yaml_backend/01-lookup_data_without_a_key.rb000066400000000000000000000004041271216112200264360ustar00rootroot00000000000000test_name "Lookup data without a key" step "Try to lookup data without specifying a key" agents.each do |agent| on agent, hiera(""), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDERR> Please supply a data item to look up OUTPUT end end hiera-3.2.0/acceptance/tests/yaml_backend/02-lookup_data_with_no_options.rb000066400000000000000000000026341271216112200270150ustar00rootroot00000000000000begin test_name "Lookup data using the default options" agents.each do |agent| codedir = agent.puppet['codedir'] hieradatadir = File.join(codedir, 'hieradata') step 'Setup' apply_manifest_on agent, <<-PP file { '#{hieradatadir}': ensure => directory, recurse => true, purge => true, force => true, } PP apply_manifest_on agent, <<-PP file { '#{hieradatadir}/global.yaml': ensure => present, content => "--- http_port: 8080 ntp_servers: ['0.ntp.puppetlabs.com', '1.ntp.puppetlabs.com'] users: pete: uid: 2000 tom: uid: 2001 " } PP step "Try to lookup string data" on agent, hiera("http_port"), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> 8080 OUTPUT end step "Try to lookup array data" on agent, hiera("ntp_servers"), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> ["0.ntp.puppetlabs.com", "1.ntp.puppetlabs.com"] OUTPUT end step "Try to lookup hash data" on agent, hiera("users"), :acceptable_exit_codes => [0] do assert_match /tom[^}]+"uid"=>2001}/, result.output assert_match /pete[^}]+"uid"=>2000}/, result.output end end end hiera-3.2.0/acceptance/tests/yaml_backend/03-lookup_data_with_a_scope.rb000066400000000000000000000034451271216112200262410ustar00rootroot00000000000000begin test_name "Lookup data with a scope" agents.each do |agent| codedir = agent.puppet['codedir'] hieradatadir = File.join(codedir, 'hieradata') teardown do apply_manifest_on agent, <<-PP file { '#{hieradatadir}': ensure => directory, recurse => true, purge => true, force => true, } file { '#{codedir}/scope.yaml': ensure => absent } file { '#{codedir}/scope.json': ensure => absent } PP end step 'Setup' apply_manifest_on agent, <<-PP file { '#{hieradatadir}/global.yaml': ensure => present, content => "--- http_port: 8080 ntp_servers: ['0.ntp.puppetlabs.com', '1.ntp.puppetlabs.com'] users: pete: uid: 2000 gid: 2000 shell: '/bin/bash' tom: uid: 2001 gid: 2001 shell: '/bin/bash' " } file { '#{hieradatadir}/production.yaml': ensure => present, content => "--- http_port: 9090 monitor: enable ntp_servers: ['0.ntp.puppetlabs.com', '1.ntp.puppetlabs.com'] " } file { '#{codedir}/scope.yaml': ensure => present, content => "--- environment: production " } PP step "Try to lookup string data using a scope from a yaml file" on agent, hiera('monitor', '--yaml', "\"#{codedir}/scope.yaml\""), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> enable OUTPUT end # TODO: Add a test for supplying scope from a json file. # We need to workout the requirement on the json gem. step "Try to lookup string data using a scope from a yaml file" end end hiera-3.2.0/acceptance/tests/yaml_backend/04-lookup_data_with_array_search.rb000066400000000000000000000025311271216112200272670ustar00rootroot00000000000000begin test_name "Lookup data with Array search" agents.each do |agent| codedir = agent.puppet['codedir'] hieradatadir = File.join(codedir, 'hieradata') teardown do apply_manifest_on agent, <<-PP file { '#{hieradatadir}': ensure => directory, recurse => true, purge => true, force => true, } file { '#{codedir}/scope.yaml': ensure => absent } file { '#{codedir}/scope.json': ensure => absent } PP end step 'Setup' apply_manifest_on agent, <<-PP file { '#{hieradatadir}/production.yaml': ensure => present, content => "--- ntpservers: ['production.ntp.puppetlabs.com'] " } file { '#{hieradatadir}/global.yaml': ensure => present, content => "--- ntpservers: ['global.ntp.puppetlabs.com'] " } file { '#{codedir}/scope.yaml': ensure => present, content => "--- environment: production " } PP step "Try to lookup data using array search" on agent, hiera('ntpservers', '--yaml', "\"#{codedir}/scope.yaml\"", '--array'), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> ["production.ntp.puppetlabs.com", "global.ntp.puppetlabs.com"] OUTPUT end end end hiera-3.2.0/acceptance/tests/yaml_backend/05-lookup_data_with_hash_search.rb000066400000000000000000000025211271216112200270740ustar00rootroot00000000000000begin test_name "Lookup data with Hash search" agents.each do |agent| codedir = agent.puppet['codedir'] hieradatadir = File.join(codedir, 'hieradata') teardown do apply_manifest_on agent, <<-PP file { '#{hieradatadir}': ensure => directory, recurse => true, purge => true, force => true, } file { '#{codedir}/scope.yaml': ensure => absent } file { '#{codedir}/scope.json': ensure => absent } PP end step 'Setup' apply_manifest_on agent, <<-PP file { '#{hieradatadir}/production.yaml': ensure => present, content => "--- users: joe: uid: 1000 " } file { '#{hieradatadir}/global.yaml': ensure => present, content => "--- users: pete: uid: 1001 " } file { '#{codedir}/scope.yaml': ensure => present, content => "--- environment: production " } PP step "Try to lookup data using hash search" on agent, hiera('users', '--yaml', "\"#{codedir}/scope.yaml\"", '--hash'), :acceptable_exit_codes => [0] do assert_match /joe[^}]+"uid"=>1000}/, result.output assert_match /pete[^}]+"uid"=>1001}/, result.output end end end hiera-3.2.0/bin/000077500000000000000000000000001271216112200133475ustar00rootroot00000000000000hiera-3.2.0/bin/hiera000077500000000000000000000154501271216112200143720ustar00rootroot00000000000000#!/usr/bin/env ruby # CLI client for Hiera. # # To lookup the 'release' key for a node given Puppet YAML facts: # # $ hiera release 'rel/%{location}' --yaml some.node.yaml # # If the node yaml had a location fact the default would match that # else you can supply scope values on the command line # # $ hiera release 'rel/%{location}' location=dc2 --yaml some.node.yaml # Bundler and rubygems maintain a set of directories from which to # load gems. If Bundler is loaded, let it determine what can be # loaded. If it's not loaded, then use rubygems. But do this before # loading any hiera code, so that our gem loading system is sane. if not defined? ::Bundler begin require 'rubygems' rescue LoadError end end require 'hiera' require 'hiera/util' require 'optparse' require 'pp' options = { :default => nil, :config => nil, :scope => {}, :key => nil, :verbose => false, :resolution_type => :priority, :format => :ruby } initial_scopes = Array.new # Loads the scope from YAML or JSON files def load_scope(source, type=:yaml) case type when :mcollective begin require 'mcollective' include MCollective::RPC util = rpcclient("rpcutil") util.progress = false nodestats = util.custom_request("inventory", {}, source, {"identity" => source}).first raise "Failed to retrieve facts for node #{source}: #{nodestats[:statusmsg]}" unless nodestats[:statuscode] == 0 scope = nodestats[:data][:facts] rescue Exception => e STDERR.puts "MCollective lookup failed: #{e.class}: #{e}" exit 1 end when :yaml raise "Cannot find scope #{type} file #{source}" unless File.exist?(source) require 'yaml' # Attempt to load puppet in case we're going to be fed # Puppet yaml files begin require 'puppet' rescue end scope = YAML.load_file(source) # Puppet makes dumb yaml files that do not promote data reuse. scope = scope.values if scope.is_a?(Puppet::Node::Facts) when :json raise "Cannot find scope #{type} file #{source}" unless File.exist?(source) require 'json' scope = JSON.load(File.read(source)) when :inventory_service # For this to work the machine running the hiera command needs access to # /facts REST endpoint on your inventory server. This access is # controlled in auth.conf and identification is by the certname of the # machine running hiera commands. # # Another caveat is that if your inventory server isn't at the short dns # name of 'puppet' you will need to set the inventory_sever option in # your puppet.conf. Set it in either the master or main sections. It # is fine to have the inventory_server option set even if the config # doesn't have the fact_terminus set to rest. begin require 'puppet/util/run_mode' $puppet_application_mode = Puppet::Util::RunMode[:master] require 'puppet' Puppet.settings.parse Puppet::Node::Facts.indirection.terminus_class = :rest scope = YAML.load(Puppet::Node::Facts.indirection.find(source).to_yaml) # Puppet makes dumb yaml files that do not promote data reuse. scope = scope.values if scope.is_a?(Puppet::Node::Facts) rescue Exception => e STDERR.puts "Puppet inventory service lookup failed: #{e.class}: #{e}" exit 1 end else raise "Don't know how to load data type #{type}" end raise "Scope from #{type} file #{source} should be a Hash" unless scope.is_a?(Hash) scope end def output_answer(ans, format) case format when :json require 'json' puts JSON.dump(ans) when :ruby if ans.is_a?(String) puts ans else pp ans end when :yaml require 'yaml' puts ans.to_yaml else STDERR.puts "Unknown output format: #{v}" exit 1 end end OptionParser.new do |opts| opts.banner = "Usage: hiera [options] key [default value] [variable='text'...]\n\nThe default value will be used if no value is found for the key. Scope variables\nwill be interpolated into %{variable} placeholders in the hierarchy and in\nreturned values.\n\n" opts.on("--version", "-V", "Version information") do puts Hiera.version exit end opts.on("--debug", "-d", "Show debugging information") do options[:verbose] = true end opts.on("--array", "-a", "Return all values as an array") do options[:resolution_type] = :array end opts.on("--hash", "-h", "Return all values as a hash") do options[:resolution_type] = :hash end opts.on("--config CONFIG", "-c", "Configuration file") do |v| if File.exist?(v) options[:config] = v else STDERR.puts "Cannot find config file: #{v}" exit 1 end end opts.on("--json SCOPE", "-j", "JSON format file to load scope from") do |v| initial_scopes << { :type => :json, :value => v, :name => "JSON" } end opts.on("--yaml SCOPE", "-y", "YAML format file to load scope from") do |v| initial_scopes << { :type => :yaml, :value => v, :name => "YAML" } end opts.on("--mcollective IDENTITY", "-m", "Use facts from a node (via mcollective) as scope") do |v| initial_scopes << { :type => :mcollective, :value => v, :name => "Mcollective" } end opts.on("--inventory_service IDENTITY", "-i", "Use facts from a node (via Puppet's inventory service) as scope") do |v| initial_scopes << { :type => :inventory_service, :value => v, :name => "Puppet inventory service" } end opts.on("--format TYPE", "-f", "Output the result in a specific format (ruby, yaml or json); default is 'ruby'") do |v| options[:format] = case v when 'json', 'ruby', 'yaml' v.to_sym else STDERR.puts "Unknown output format: #{v}" exit 1 end end end.parse! unless initial_scopes.empty? initial_scopes.each { |this_scope| # Load initial scope begin options[:scope] = load_scope(this_scope[:value], this_scope[:type]) rescue Exception => e STDERR.puts "Could not load #{this_scope[:name]} scope: #{e.class}: #{e}" exit 1 end } end # arguments can be: # # key default var=val another=val # # The var=val's assign scope unless ARGV.empty? options[:key] = ARGV.delete_at(0) ARGV.each do |arg| if arg =~ /^(.+?)=(.+?)$/ options[:scope][$1] = $2 else unless options[:default] options[:default] = arg.dup else STDERR.puts "Don't know how to parse scope argument: #{arg}" end end end else STDERR.puts "Please supply a data item to look up" exit 1 end begin hiera = Hiera.new(:config => options[:config]) rescue Exception => e if options[:verbose] raise else STDERR.puts "Failed to start Hiera: #{e.class}: #{e}" exit 1 end end unless options[:verbose] Hiera.logger = "noop" end ans = hiera.lookup(options[:key], options[:default], options[:scope], nil, options[:resolution_type]) output_answer(ans, options[:format]) hiera-3.2.0/docs/000077500000000000000000000000001271216112200135275ustar00rootroot00000000000000hiera-3.2.0/docs/images/000077500000000000000000000000001271216112200147745ustar00rootroot00000000000000hiera-3.2.0/docs/images/hiera_hierarchy_resolution.png000066400000000000000000000362071271216112200231230ustar00rootroot00000000000000PNG  IHDR^NFt pHYsaa?i IDATx{\wDRbie44NǬNVZXzԲ2Vㅟi&д4K1K~( .y>>ٙwfa3Jkle]!EH!,D)H  "B$BXR @ a!H!,D)H  "B$BXR @ a!H!,D)H  "B$BXR R^C.o j7j׺B\\ι52< t77k۹B\<J@o ojiZXVBk)(99#H  "B$BXR @ a!RR5YʔRR}ʺ%E)\0*Xܻ+e]Re]Z{Xu\=|#/\=5X \\.|7ק7F %:sn=> @h&o]Yh]])|W1Z/{e u^ZoZg_Ɇ PJ&.>xy1?aUJhf\2Zg*R@cuT?2s R@b أq|.u,t?ֻ.YBOva'ae>o y>kJ?Lgc_uupL:,gYJ0S'uvJ)_Tg c=%+ ckp8Õ^RFk_ǛhZ s=E?0 0s}_z8 E\'sc:cH~k!{gU6o`/ϙ"a?ơdRyc.6v:E{T %X)7?1+16Կ7:~1x0~|aSf_ܳx[0 =0Nv<LE)F)M)f>w)FQfWJ8..< UJ7B)W:WJ45E)F3r aEqKsxE>)4Zs SJ7ρ6RNgE{1d FsqyFK]2$p9q Υx~!o7w5ǘ`kec4+5KXcndug<)NC2ec`TJ8Ja;#.a,kFay8rqYT}cs?1d;ڙo;s1\.1daۇcAw渧]4+ֽyR6F8w͑K{/W@Y"N Zr*a4uNkO\iz)`j.8s@kG)U#Z~W3O^ҧ _un~*>^\R$ތqVUk>S ƶqq>SZ%i;"TiJ'b4*˺yk ֻ˺>Vv-\Sn{Ÿ[yG O d)R *> J)R*@)Ur-qᲮPJ7H)ujr݌W] Z l e7c(W{~\K+ orbX RO)WJ)F#C)RJ.J/RKRO:)*p)gRj0 kN^)R~g_RR5^)R1R /Y^R R+TJ}RJ5r)RJ J/Ru<UJ VJbǔRyR)tqRjRYHTcs\#sי>qWul\X?+QJ ^XZrWZgׁ2%9]LTFGp_]@g^UxBK)Uc؝߼+:\Yu穛\ arNBXȵv-R @ a!H!,D)H  "B$BXR @ a!H!,D)H+=zt/\w}w# u_wJ(Ǥ*H  "B$BXR @ a!H!,D)H  "B$BXR @ a!H!,D)H  "B$BXZuj?| Nk yR0n5ꂛ/J|sss˺.RJ 5j񲮋(iرcdu]\I d X-CYBV |@^@^ |@AiRX~I ݤB)a,$nPJ? y*ƊA@zB)aX$t0V<H+i(%7ƊKYJJ c&,EW QH KYA0 @KC)a2$ASFm5j!A)HU @ a!H!,D)H  "B$BXR @ a!H!,D)TDJ)oO+:Inn+TUV]@YSP J vܶnL=ʺ@TVSE e]R!{1!,D)H  "B$BXR @ a!H!,D)H aINN.jx\EYkA noeРA̝;K}7L!wN~^cǎ1k,^vIdffrҧOΜ9Sթ*L =z47p'O&** m۶DӦML?m49BDDrAƏ͛^vI1c ~W,XPթ~ڶmK߾} nӧ!!!7???5jDtt4 :9si&Zh`ɒ%|wСCyhҤ 4]X dnxyyh֭[Ƿ~ӧY|97o&%%{ٕ(++lΜ9ҥK#(( .qF0`>EԨQO?۷3}tbcc9u~~~hYv-x{{s96mDpp0>,g4hЀ&Oy)wȰ d;ݛ/#Gqʧk[l!##dz}vϙ3gXd ;wFyOMMiӦә1c?35jGu>iy„ ([Xh5kaÆ[3b/_Nll,#==$Ǎ?͛7'**3fɓILL$ .]ObʕҫW/-[FZZ?={tGF&44C2g6mD->>,Y:t?WƤIѣL2!CФIzE\l۶-ժU_$55oƂ ;wegeePE2d?#:t 11UV1e:wի9~87fڵo,\'Os= 7ܹsٶmo6ׯg<4i҄82228wJ)Ο?O,g4hЀ&Oy ]>mXrssWwܓ5daΜ94mڔ=z0~xK郟۶m#))9ڵkQF$&&qF~aKΝfs67|CTTw}fY֯_ٳgٺu+:u*N^dҥ?~g}Hbcc d֬Yy6`cbbbRİh"纈v7DDDŋM6,]Z(hx *WLyhѢ?CI2Qk~ аaCg1))xϟO߾}ٶm6aÆqa6lܹsσ<|0/o?w_F 6lu]@FFw.֭ߟ\gH3<}h߾=`UVs9옯cO:… rQv;Zr.C ]4mڔѣG3j(BCCV+t5e^|E6oΝ;Yj|̚53j(,Xq_tc]l<8lz@n6l؀/u]uYzukǸ]T^^^CDDDYf瑜̔)Sh۶-]v]MVW{e߾}ݻ9t萳ڻwovݻ #00x<Ț5k_ʜ9s4=zpYXWҥK5k .>`o9_;Š+8p/2 *RHNNf۶mlذCGC~G4oޜo+T^#GF3ٳ8q"#F_M6y ߟs9πEÆ yw3f o&`8;E.J7h1biiiL:Ə_^zpBϙ3g$Rnlٱce ?{,UVu 6l&LpNw>L@@84dggs!MHO?Ƞ^zj:βVVI&gxbn&6-IDATrȢ:<:u"88!(O~WHڵ*yW@&kQMLL 19r$:u*몔[n)Me)B$BXR @ a!H!,D)H  "BQ 3l޼ݻ3eʔ>cرlٲ={,a-RKv휏/ .p͛G||j'Z{؎v,X]hٲeYj#аaC6mĖ-[\2WSNOqyڴi|Dޚ5kͥsoٳ޽{;n:vɓ' cǎG>"""hР|wq?f͚q Þ={ " `WNjj*:iӆ)Szj6nȆ eڵ+WC5F΂ =d v =YVVSNO?Cۗ1cň#hР!!!lْݻwoswm6bccf͚QN4iBӦM=H K`ǎҬY3{ڵdff2qD#hҤeԮ] 60}tͥCSeY1H Kzddd/'OqTR{իI򣃙3gO?1f4h9~skO8E!ǐ%Pvmnf.\֚uVBCC_󄎷7Æ #::rvIxx87x#9Cʎ;HOO'##|UanoQ^~e^{5fϞn'<<СC"55.]зoxy7!33|v_M.]hٲ%)))ѱcGƎK`` b G_t?NÆ T)o[NN ZjXFvv6'O^z:uU֚'ORfMh~222T>>>Y0 (4@kBE F @ a!H!,D)H  "B$BXR @ a!<''SE lOm q xvL!|||֕u<ɭ{H B0h鲮'=BBXR @ a!H!,D)H  "B$BXR @ a!_nug>>>Fםe @z@bbbz;N:La=dB$BXR @ a!H!,D)H  "B$BXR @ a!H!,D)H  "B$BXR @ a!H!,Di^ #GhqRvw[)Jl6]RQF]pE@C:¦ď: XyvQFźTH^xᅜU)mY%?Je]Qrbj(%J c!,!RXH BYRXH RYRX>I ݠC)a,$nRZ0oH7t(%<J c wRXqH =% c" 34ƊGa% b@RXqI KIQC)a$J0 d)+(F2qi(%AnrU>sCu{w˺.$BX4Y "B$BXR @ a!H!,D)H  "B$BXGDBhsʺJ) hBkֺwYWSP J vܶnL=ʺ@TVSE e]R!{1!,D)H  "B$BXR @ a!H!,D)H  %5ke]b1caaalڴ"JHy˗3~xΞ=[U)6GW:yٵc=!%%iӦgj֬IyGQJw^ONrr2>,7nLJ,Y:t?+W$66^zl2Ҹٳ'lٲ?W_}5kӞ={8z(M4ᥗ^VZ,[x:v?7|ƍٳ'wy'7ofΜ9ҴiSxZjÇ:u*$&&ҹsg:w̤I?ӧݺuL:]vuVn6^|E*W|+e֬YT^;3zJ*aꫯXv-hтW^yb]mP@lD}ڶmޫo& {Oo:88Xl6ݼys]re㣛4iQ44h[nݳgO]jU}-hooo ^ᇺnݺ{SUV*Uh}wk___} 6脄VOWRE7h@oٲEYFl6]F @7nܨΝ]JݨQ#m4kԨ4i111:22Y UY!Ch@GDDZKs>}46mk֬=h [hݸqc믿W-;q6kmҾ}{{=&NH]v͖-[?y饗b̙dgg;?A`` /&226mڰtRRSS 0 0fbbbxi׮#Gvy4o<2330a3f`ذa$%%|rx뭷Ƞ_~dee{˩S8p 3gdȑ4oޜ<,;<?p@Rr8@׮]i۶-7o桇e˖ԯ_gK/qq粸9_=5 (( .8uhӪU+bcc9ydXWaaaxyymͥM6Tdl*۷g۷nul >믿N-Xx1)))>>DDD`]lԬY;+.|? ]v/DZcǨ]6_5gO?eŚRzǎGpu;I(UFfppt~E(e&XݺufـW棏>b 2$g;vHJJ +V 4H!t8!! """ի7o;oĉL<{o֚ddd[ovXqg8gC]75k0|ƍǮ] sС޽^z1i$h߾=ׯgL>QF}v֭KXX &Mb޼yDEEQreKTwQ8!of^Yb?8`9gL4ٓ'N87bҘ:u*!!!?+ηs?Yf$%%{n֭XpÆ (xh۶-SL駟g!!!׳`z͒%K׿EHHwuݻ&M{tЁɓ';s4u۶m޽M0h vɈ#I<`R| :> 0~!5k?(vE}|?l:DkѣG]vftq<Ӽ+ 8qZjZ^Qz:Е㰠AҔDxx9N\2Çwpp乴߆UT㦛n*VyJ)֭[\f]v8?EkJ Z| SZ5"""Xz5ݛpqSy#HxPۯ\aviŬ['xŋ̣>Jn7n4oޜ(f̘Arr2'O&11t“O>|0itt4/Ϗ;3FMJ9r$/[Ĵiؿ?aaatڕ֭[3i$8z(SL'))s9޸q#_5ǎ^z|dZjży3gw%77tΝ;LJ6mڰvZ7o͛7g?~0 vZ~7.\X+\JW_}E1i$ϟw)^{5~G.]yg$66@f͚Evv3sa<EÊ+8q `̘1vqAx *WLy/̙3Z3{l &OLnn._}s,ٹs'ǎ+* 4o{ܛgy___ۗgzZjlN[ Zl @݋\8?ۡC"z߾}TZ[o5O뜦F4l뮻@nFf1HSbb"`c矝.5k~~~̛7~Y'///ٶmy޽;;w??0w:'{HSFx뭷زe :u⩧w ;o2{lxwxhݺ5qqqۗe}/`֭ylٵkC !!!!Cмyszӧ>|eׯzbݺu_⧟~'V(ƍ/˖-#335j\s:tj֬yG`IIIFlsqQ֭_qD̙3;vu^UGKՅe(M4)p|:u gٸ OӦMUU&w]{1OjѢC3Ǝ -[سgO",ek.ߧ Յ p#>>Me?Jv; ,`׮]lْK;zZxyGhذ!6mb˖-T\իWөS'~'8-y͚5ҹsg~7C޽Xn;wɓLKq=d }DDDРAbcc"""?~<͚5ĉӇ={D@@T^T^uBCCiӦ SLalܸ 68ܵk+WS5j@5 )%{DGGF{,Nʧ~J۷/cƌ!**#FРABBBhٲ%wPm۶KNTf͚QN4iRĵEY;v 77f͚9k׮&33'DZZZϒ]66l`$%%K;}$KYիW'##,|}}8y$7J*{T^0N3gO?1f4h9~skO8E!ǐ%Pvmnf.\֚uVBCC_󄎷7Æ #::rvIxx87x#9Cʎ;HOO'##|UanoQ^~e^{5fϞn'<<0W3tPHMMK.2~ix bbb믿&!!]_ӥK:ZlIJJeetؑcH~<}_Pfo߾}9~8 6Rm9998pjժU`ٜJttRqI#?gn",̏paXak ]Qy]{~{ˮb3X=11111111111111111111111HghX aMΞȍ;[,?޽@7>i;:u"7(WYsC;/x}\`S: ӃӋ:"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q"Q" Fcǎ_^^O'{xc<FAn-V@Ard111111111111111111111n{7omg۽.NeUlw絸J'_jFoUUU+Y,L.qqq'=]^q˫7P\H1 LR1 LR1. :;JX((c TtR1n ҉\b*J 8;J98+JY ]o(Hn3)pQ*Fϥ kF=AWR1abPn( (Vrfcǎ=11.YE E E E E E E E E E E E RZ,oW+b+=Wr=,AE.P~5EEE{ Á6X,JVk\rjZm[nSq\ziKhРTZZ)D E E E E E E E E E f#77p9O9i|8 .dǎmۖCʕ+YjK/E˖-tf>СCyزe _IKKQFql_^^O---g%77۳xb.\Ȓ%Ko߾xyyd6og}Ə?Ț5kعs'yyy<Q^^ӧYz5iiiQYYf,Y'Ҷm[˜>}:cƌRXXvM67gϞeǎ /dƎKӦM eԩ;v &8w0\w}L:z?6leeel߾r ٻw/ڵs|ڵk9uO>a̙tؑcǎxbJJJ^}U-Zѣ_¼yf///!11ϓ?y׮y|qdмyݻ7Ç)(( ((={ʕ+_sżyX~=cƌaܸqvǙO?ԩSL:zG^v +**" &Md bcccڵ=z!C0x`(..&%%ݻӦMk[,((`͚5>>QUU8ۋ{6Vնo>#c/((n /a6l7of۶mԫWٳg9qv~~~QTTOF.唔`X3go~sm~ƍ;.}4hurrrҥK ѸRHH%1z)ˉqZjuۯ*Fz]UuF7|3bwNهTy:w=C=ĺuhܸ cڶmСC`ʕZ bbbx饗㋊⣏>'NahٲxEAvЁ +ONZ,]E]}YYutÆ / &&~z֭6lɓhтM6_l2INN_ofѢEٳ1cưe֬YÓO>I˖-IKKgbX(--gɓ}9rw}|Yz5iii4k֌C]wEƍIMM???v;6m"55oooΞ=ˎ;^`ɒ%;MԩS9v&L;*woOv_pBZjE^4isΥo߾grrroڴ 5kؾ};?8seѢEXVeO?%))wYl™3g/ڵ/._^XXo_|ի9y$/ , 55@͛wYtcY, ))),_HNNvx b ,X@tt4Wv\-ԭ[&M0j(ڶm /_qß!"""999dǞ={Z1Ǐm6-ZXX> 8ϑ#G㇅M7@qq1=ǏgSUUr~ =}sO%: ?SXc2<<___DZUVVRUUʼn'loq|YYYz|ӪU+ƎK\\<3xrjEJHHW^aΝddd~zRSS={6c˖-űtR ν`+8ƱZx8m۶K`` /3gHH/ ~mW |||HLL6lx%!!:УG뚛\ꆿd7|÷~?LPP>|,ej>}DEEHzz:ǎcƍg…W^zqyϱ{xyy1o<-[ĉ9rs%9_XXv-GeȐ! }l޼ "66:CVĉILLiӦj*4i:u}IPPB~~>F"<G||>>90yʒ Rn1111111111UC'--1E|||6{3$b :9v'JNRDODADADADADADADADADADAD@RRW_}ƙcƙcy 8p n1˷ṢKV(H(H(H(H(H(H(H(H(H(H(H(H(H(H(H(H(H(H(H(HhIk[o vf}}[,kWjԩWKm A&M m;WQ+mgنť:}R#tz ^~&Mb|\b:~TFFkY EkYMDkyb}urWvRNPQ*KA:IMEk7DR1~ \b b E.r=tEtuTGAXuTIAրkR1z.YC6Jd RQd (tG<ʍfϞ흕=11.YE E E E E E E E E E E E -++9 vڠ"[n{ Á6X,JVk\rjZm[nSq\ziKhРTZZ)D E E E E E E E E E ݤS)H'gΜ9WnΝٓjk֬Y7ݻwY, ұcGFU1*++`Ť;ivn.{GmgXt)]v}ׯ'==`x """رcwnݺlذ]k.(--%::Gy7RUUEn̤Oޛyf222&**ؚK YM'N$11MʪUKLLdҤIDFFr)Kff&AAALHH5pIHH`Æ l߾m۶9z9ԩSrrrҥK hD E E E E E E E E E E w{TTTjh@?.wڀ"??.6?*b)b)b)b)b)b)b)b)b)b)b)b)b)b)b)b)bY=n*,IENDB`hiera-3.2.0/docs/tutorials/000077500000000000000000000000001271216112200155555ustar00rootroot00000000000000hiera-3.2.0/docs/tutorials/getting_started.md000066400000000000000000000056451271216112200213000ustar00rootroot00000000000000# Getting Started Hiera is a simple hierarchal database which provides an easy to use interface for looking up data using a key. $ hiera puppetlabs_home_page http://puppetlabs.com ## Installation We are going to install Hiera using Rubygems, so now is a good time to make sure you meet the prerequisites. ### Prerequisites * Ruby 1.8.5+ * Rubygems 1.3.0+ ### Installing Hiera via Rubygems $ gem install hiera Successfully installed hiera-0.3.0 1 gem installed Installing ri documentation for hiera-0.3.0... Installing RDoc documentation for hiera-0.3.0... Make sure we can run the hiera command: $ hiera -v 0.3.0 - **Note:** Some Linux distributions such as Debian squeeze do not put the gem bin directory (`/var/lib/gems/1.8/bin`) in your PATH by default. You may have to call hiera using the full path: $ /var/lib/gems/1.8/bin/hiera -v 0.3.0 ## Configuration Before using Hiera we need to create a configuration file. By default Hiera attempts to load `hiera.yaml` from the `/etc/` directory. Lets create that file now: $ vim /etc/hiera.yaml --- :backends: - yaml :hierarchy: - global :yaml: :datadir: /var/lib/hiera/data - **Note:** If Hiera cannot locate `/etc/hiera.yaml` you will receive the follow error when trying to lookup a value: $ hiera key Failed to start Hiera: RuntimeError: Config file /etc/hiera.yaml not found You can specify a different configuration file using the `--config` option: $ hiera --config ~/hiera.yaml key ## Adding data With configuration out of the way, lets add some data. The yaml backend expects to find data files under the `datadir` we configured earlier. Create the `/var/lib/hiera/data` data directory: $ mkdir -p /var/lib/hiera/data For each source in the `hierarchy`, the yaml backend will search for a corresponding YAML file under the `datadir`. For example, our `hierarchy` consists of a single source named `global`. The yaml backend will look for `/var/lib/hiera/data/global.yaml`, and if missing skips it and move on to the next source in the hierarchy. Lets add some data to the `global` source: $ vim /var/lib/hiera/data/global.yaml --- driftfile: '/etc/ntp/drift' ntpservers: - '0.north-america.pool.ntp.org' - '1.north-america.pool.ntp.org' ## Looking up data Now that we have our configuration setup and some data, lets lookup the 'driftfile' key: $ /var/lib/gems/1.8/bin/hiera driftfile /etc/ntp/drift We get exactly what we expected, '/etc/ntp/drift'. Running the lookup command with the `--debug` flag, we can see the details of how Hiera lookups data: $ /var/lib/gems/1.8/bin/hiera driftfile --debug DEBUG: Thu Jun 28 09:54:04 -0400 2012: Hiera YAML backend starting DEBUG: Thu Jun 28 09:54:04 -0400 2012: Looking up driftfile in YAML backend DEBUG: Thu Jun 28 09:54:04 -0400 2012: Looking for data source global /etc/ntp/drift hiera-3.2.0/docs/tutorials/hierarchies_sources_and_scope.md000066400000000000000000000070541271216112200241510ustar00rootroot00000000000000# Hierarchies, Sources, and Scope The key to mastering Hiera is understanding the following concepts: * Hierarchies * Sources * Scope ## Hierarchies At the very core of Hiera are the data hierarchies, which are made up of sources. Hierarchies are specified in Hiera configuration file `hiera.yaml` via the `:hierarchy:` array. :hierarchy: - "%{certname}" - "%{environment}" - default There are three sources in the above hierarchy, `%{certname}`, `%{environment}`, and `default`. The first two sources, `%{certname}` and `%{environment}`, represent dynamic sources which will be resolved at runtime. The third source `default` is static. When looking up a key Hiera iterates through each source in the hierarchy starting with the first one in the list. In our case `%{certname}`. There is no limit to the number of sources you can have. But lets not go crazy; try and keep your hierarchy below 5 - 6 levels deep. Any more than this you should start thinking about custom facts or how your data is organized. ### Order is important Hiera uses the priority resolution type by default. This means Hiera stops at the first source in the hierarchy that provides a non `nil` answer. The behavior is a slightly different for the array and hash resolution types. Every scope in the hierarchy will be searched, but data is appended not overridden! ## Sources Each level of the hierarchy is represented by a source which comes in two flavors, static and dynamic. ### Static sources A source is considered static when it appears in the hierarchy as a simple string. :hierarchy: - default - You should consider using a static source when you want a certain level in the hierarchy to apply to all nodes. ### Dynamic sources A source is considered dynamic when it appears in the hierarchy as a string enclosed between `%{}` like this: :hierarchy: - %{certname} Dynamic sources are interpolated by Hiera at runtime. - You should consider using a dynamic source when you want to provide different data based on Facter Facts. ## Scope A scope is a collection of key/value pairs: certname: agent.puppetlabs.com environment: production operatingsystem: Debian If you are thinking scopes look a lot like Facter Facts you are on to something. Hiera was designed around Facter Facts being the primary input for scope. ### Source interpolation Hiera uses the scope when interpolating sources in the hierarchy. Scopes can be empty, and when they are, dynamic sources are excluded from the hierarchy at run time. ### Feeding Hiera your scope You can provide Hiera a scope via the command line using the `--yaml` or `--json` options. For example: If we had the following scope: $ cat /tmp/scope.yaml --- certname: agent.example.com environment: production You can feed it to Hiera like this: $ hiera --yaml /tmp/scope.yaml driftfile /etc/ntp/drift - **Note:** If you run into the follow error, you need to make sure Puppet is installed: Could not load YAML scope: LoadError: no such file to load -- puppet The reason for this is that the scope yaml file could have been produced by Puppet, and contained serialized objects. Since it would be desirable to use Hiera without Puppet, this restrict will be removed in the future. hiera-3.2.0/ext/000077500000000000000000000000001271216112200133775ustar00rootroot00000000000000hiera-3.2.0/ext/build_defaults.yaml000066400000000000000000000013251271216112200172520ustar00rootroot00000000000000--- packaging_url: 'git://github.com/puppetlabs/packaging.git --branch=master' packaging_repo: 'packaging' packager: 'puppetlabs' gpg_key: '4BD6EC30' # These are the build targets used by the packaging repo. Uncomment to allow use. #final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-el-7-x86_64' #default_cow: 'base-trusty-i386.cow' #cows: 'base-precise-i386.cow base-squeeze-i386.cow base-trusty-i386.cow base-wheezy-i386.cow' #pbuild_conf: '/etc/pbuilderrc' build_gem: TRUE build_dmg: FALSE sign_tar: FALSE yum_host: 'yum.puppetlabs.com' yum_repo_path: '/opt/repository/yum/' apt_host: 'apt.puppetlabs.com' apt_repo_url: 'http://apt.puppetlabs.com' apt_repo_path: '/opt/repository/incoming' tar_host: 'downloads.puppetlabs.com' hiera-3.2.0/ext/debian/000077500000000000000000000000001271216112200146215ustar00rootroot00000000000000hiera-3.2.0/ext/debian/changelog.erb000066400000000000000000000014131271216112200172410ustar00rootroot00000000000000hiera (<%= @debversion %>) lucid squeeze precise quantal wheezy sid unstable; urgency=low * update to version <%= @debversion %> -- Puppet Labs Release <%= Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") %> hiera (0.99+1.0.0rc2-1puppet1) unstable; urgency=low * Release of 1.0.0rc2 -- Matthaus Litteken Tue, 15 May 2012 04:45:08 +0000 hiera (0.99+1.0.0rc1-1puppet1) unstable; urgency=low * Release of 1.0.0rc1 -- Puppet Labs Release Key (Puppet Labs Release Key) Mon, 14 May 2012 23:55:54 +0000 hiera (0.3.0.24-1) unstable; urgency=low * Initial release of upstream 0.3.0.24 -- Puppet Labs Release Key (Puppet Labs Release Key) Fri, 04 May 2012 20:43:22 +0000 hiera-3.2.0/ext/debian/compat000066400000000000000000000000021271216112200160170ustar00rootroot000000000000007 hiera-3.2.0/ext/debian/control000066400000000000000000000010331271216112200162210ustar00rootroot00000000000000Source: hiera Section: utils Priority: extra Maintainer: Puppet Labs Build-Depends: debhelper (>= 7.0.0), cdbs, ruby | ruby-interpreter Standards-Version: 3.9.2 Homepage: http://projects.puppetlabs.com/projects/hiera Package: hiera Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter, libjson-ruby | ruby-json Conflicts: ruby-hiera Provides: ruby-hiera Replaces: ruby-hiera Description: A simple pluggable Hierarchical Database. Hiera is a simple pluggable Hierarchical Database. hiera-3.2.0/ext/debian/copyright000066400000000000000000000000461271216112200165540ustar00rootroot00000000000000/usr/share/common-licenses/Apache-2.0 hiera-3.2.0/ext/debian/rules000077500000000000000000000016421271216112200157040ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 include /usr/share/cdbs/1/rules/debhelper.mk RUBY=/usr/bin/ruby LIBDIR=$(shell $(RUBY) -rrbconfig -e 'puts RbConfig::CONFIG["vendordir"]') BINDIR=$(shell $(RUBY) -rrbconfig -e 'puts RbConfig::CONFIG["bindir"]') DATADIR=$(CURDIR)/debian/$(cdbs_curpkg)/var/lib/hiera install/hiera:: mkdir -p $(DATADIR) $(RUBY) $(CURDIR)/install.rb \ --destdir=$(CURDIR)/debian/$(cdbs_curpkg) \ --sitelibdir=$(LIBDIR) \ --bindir=$(BINDIR) \ --configdir=/etc \ --configs \ --ruby=/usr/bin/ruby clean:: hiera-3.2.0/ext/debian/source/000077500000000000000000000000001271216112200161215ustar00rootroot00000000000000hiera-3.2.0/ext/debian/source/format000066400000000000000000000000141271216112200173270ustar00rootroot000000000000003.0 (quilt) hiera-3.2.0/ext/hiera.yaml000066400000000000000000000005631271216112200153570ustar00rootroot00000000000000--- :backends: - yaml :hierarchy: - "nodes/%{::trusted.certname}" - common :yaml: # datadir is empty here, so hiera uses its defaults: # - /etc/puppetlabs/code/environments/%{environment}/hieradata on *nix # - %CommonAppData%\PuppetLabs\code\environments\%{environment}\hieradata on Windows # When specifying a datadir, make sure the directory exists. :datadir: hiera-3.2.0/ext/ips/000077500000000000000000000000001271216112200141725ustar00rootroot00000000000000hiera-3.2.0/ext/ips/hiera.p5m.erb000066400000000000000000000011021271216112200164460ustar00rootroot00000000000000set name=pkg.fmri value=pkg://puppetlabs.com/application/<%=@name%>@<%=@ipsversion%> set name=pkg.summary value="<%=@summary%>" set name=pkg.human-version value="<%=@version%>" set name=pkg.description value="<%=@description%>" set name=info.classification value="org.opensolaris.category.2008:Applications/System Utilities" set name=org.opensolaris.consolidation value="puppet" set name=description value="<%=@description%>" set name=variant.opensolaris.zone value=global value=nonglobal set name=variant.arch value=sparc value=i386 license hiera.license license="Apache v2.0" hiera-3.2.0/ext/ips/rules000077500000000000000000000010111271216112200152430ustar00rootroot00000000000000#!/usr/bin/make -f LIBDIR=$(shell /usr/bin/ruby18 -rrbconfig -e 'puts RbConfig::CONFIG["rubylibdir"]') DESTDIR=$(CURDIR)/pkg/ips/proto install/hiera:: mkdir -p $(DESTDIR)/$(LIBDIR) mkdir -p $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/share/doc/hiera mkdir -p $(DESTDIR)/var/lib/hiera mkdir -p $(DESTDIR)/etc cp -pr lib/hiera $(DESTDIR)/$(LIBDIR) cp -p lib/hiera.rb $(DESTDIR)/$(LIBDIR) cp -p bin/* $(DESTDIR)/usr/bin cp -pr ext/hiera.yaml $(DESTDIR)/etc cp -p COPYING README.md $(DESTDIR)/usr/share/doc/hiera hiera-3.2.0/ext/ips/transforms000066400000000000000000000013661271216112200163210ustar00rootroot00000000000000 default facet.doc.man true> add restart_fmri svc:/application/man-index:default> # drop user drop> drop> drop> drop> drop> drop> drop> drop> drop> drop> drop> drop> # saner dependencies edit fmri "@[^ \t\n\r\f\v]*" ""> hiera-3.2.0/ext/osx/000077500000000000000000000000001271216112200142105ustar00rootroot00000000000000hiera-3.2.0/ext/osx/file_mapping.yaml000066400000000000000000000010501271216112200175220ustar00rootroot00000000000000directories: lib: path: 'Library/Ruby/Site' owner: 'root' group: 'wheel' perms: '0644' bin: path: 'usr/bin' owner: 'root' group: 'wheel' perms: '0755' hiera: path: 'var/lib/hiera' owner: 'root' group: 'wheel' perms: '0644' etc: path: 'etc' owner: 'root' group: 'wheel' perms: '0644' files: '[A-Z]*': path: 'usr/share/doc/hiera' owner: 'root' group: 'wheel' perms: '0644' 'ext/hiera.yaml': path: 'etc' owner: 'root' group: 'wheel' perms: '0644' hiera-3.2.0/ext/osx/preflight.erb000066400000000000000000000021111271216112200166610ustar00rootroot00000000000000#!/bin/bash # # Make sure that old hiera cruft is removed # This also allows us to downgrade hiera as # it's more likely that installing old versions # over new will cause issues. # # ${3} is the destination volume so that this works correctly # when being installed to volumes other than the current OS. <%- ["@apple_libdir", "@apple_sbindir", "@apple_bindir", "@apple_docdir", "@package_name"].each do |i| -%> <%- val = instance_variable_get(i) -%> <%- raise "Critical variable #{i} is unset!" if val.nil? or val.empty? -%> <%- end -%> # remove ruby library files <%- Dir.chdir("lib") do -%> <%- [@apple_old_libdir, @apple_libdir].compact.each do |libdir| -%> <%- Dir["**/*"].select{ |i| File.file?(i) }.each do |file| -%> /bin/rm -f "${3}<%= libdir %>/<%= file %>" <%- end -%> <%- end -%> <%- end -%> # remove bin files <%- Dir.chdir("bin") do -%> <%- Dir["**/*"].select{ |i| File.file?(i) }.each do |file| -%> /bin/rm -f "${3}<%= @apple_bindir %>/<%= file %>" <%- end -%> <%- end -%> # remove old doc files /bin/rm -Rf "${3}<%= @apple_docdir %>/<%= @package_name %>" hiera-3.2.0/ext/osx/prototype.plist.erb000066400000000000000000000021101271216112200200730ustar00rootroot00000000000000 CFBundleIdentifier <%= @title %> CFBundleShortVersionString <%= @version %> IFMajorVersion <%= @package_major_version %> IFMinorVersion <%= @package_minor_version %> IFPkgBuildDate <%= @build_date %> IFPkgFlagAllowBackRev IFPkgFlagAuthorizationAction RootAuthorization IFPkgFlagDefaultLocation / IFPkgFlagFollowLinks IFPkgFlagInstallFat IFPkgFlagIsRequired IFPkgFlagOverwritePermissions IFPkgFlagRelocatable IFPkgFlagRestartAction <%= @pm_restart %> IFPkgFlagRootVolumeOnly IFPkgFlagUpdateInstalledLanguages hiera-3.2.0/ext/project_data.yaml000066400000000000000000000015011271216112200167170ustar00rootroot00000000000000--- project: 'hiera' author: 'Puppet Labs' email: 'info@puppetlabs.com' homepage: 'https://github.com/puppetlabs/hiera' summary: 'Light weight hierarchical data store' description: 'A pluggable data store for hierarcical data' version_file: 'lib/hiera.rb' # files and gem_files are space separated lists files: '[A-Z]* ext lib bin spec acceptance_tests install.rb' gem_files: '{bin,lib}/**/* COPYING README.md LICENSE' gem_require_path: 'lib' gem_test_files: 'spec/**/*' gem_executables: 'hiera' gem_runtime_dependencies: json_pure: gem_default_executables: 'hiera' gem_platform_dependencies: x86-mingw32: gem_runtime_dependencies: win32console: '1.3.2' win32-dir: '~> 0.4.8' x64-mingw32: gem_runtime_dependencies: win32-dir: '~> 0.4.8' bundle_platforms: x86-mingw32: mingw x64-mingw32: x64_mingw hiera-3.2.0/ext/redhat/000077500000000000000000000000001271216112200146465ustar00rootroot00000000000000hiera-3.2.0/ext/redhat/hiera.spec.erb000066400000000000000000000042541271216112200173660ustar00rootroot00000000000000# Fedora 17 ships with ruby 1.9, RHEL 7 with ruby 2.0, which use vendorlibdir instead # of sitelibdir %if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 || 0%{?amzn} >= 1 %global hiera_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["vendorlibdir"]') %else %global hiera_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["sitelibdir"]') %endif %if 0%{?rhel} == 5 %global _sharedstatedir %{_prefix}/lib %endif %define ruby %{_bindir}/ruby # VERSION is subbed out during rake srpm process %global realversion <%= @version %> %global rpmversion <%= @rpmversion %> Name: hiera Version: %{rpmversion} Release: <%= @rpmrelease -%>%{?dist} Summary: A simple pluggable Hierarchical Database Vendor: %{?_host_vendor} Group: System Environment/Base License: ASL 2.0 URL: http://projects.puppetlabs.com/projects/%{name}/ Source0: http://downloads.puppetlabs.com/%{name}/%{name}-%{realversion}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch BuildRequires: ruby >= 1.8.5 Requires: ruby >= 1.8.5 Requires: rubygem-json %description A simple pluggable Hierarchical Database. %prep %setup -q -n %{name}-%{realversion} %build %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/%{_sharedstatedir}/hiera %{ruby} install.rb \ --destdir=$RPM_BUILD_ROOT \ --sitelibdir=%{hiera_libdir} \ --bindir=%{_bindir} \ --configdir=%{_sysconfdir} \ --configs %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{_bindir}/hiera %{hiera_libdir}/hiera.rb %{hiera_libdir}/hiera %config(noreplace) %{_sysconfdir}/hiera.yaml %{_sharedstatedir}/hiera %doc COPYING README.md %changelog * <%= Time.now.strftime("%a %b %d %Y") %> Puppet Labs Release - <%= @rpmversion %>-<%= @rpmrelease %> - Build for <%= @version %> * Mon May 14 2012 Matthaus Litteken - 1.0.0-0.1rc2 - 1.0.0rc2 release * Mon May 14 2012 Matthaus Litteken - 1.0.0-0.1rc1 - 1.0.0rc1 release * Thu May 03 2012 Matthaus Litteken - 0.3.0.28-1 - Initial Hiera Packaging. Upstream version 0.3.0.28 hiera-3.2.0/install.rb000077500000000000000000000215161271216112200146020ustar00rootroot00000000000000#! /usr/bin/env ruby #-- # Copyright 2004 Austin Ziegler # Install utility. Based on the original installation script for rdoc by the # Pragmatic Programmers. # # This program is free software. It may be redistributed and/or modified under # the terms of the GPL version 2 (or later) or the Ruby licence. # # Usage # ----- # In most cases, if you have a typical project layout, you will need to do # absolutely nothing to make this work for you. This layout is: # # bin/ # executable files -- "commands" # lib/ # the source of the library # # The default behaviour: # 1) Install commands from bin/ into the Ruby bin directory. On Windows, if a # if a corresponding batch file (.bat or .cmd) exists in the bin directory, # it will be copied over as well. Otherwise, a batch file (always .bat) will # be created to run the specified command. # 2) Install all library files ending in .rb from lib/ into Ruby's # site_lib/version directory. # #++ require 'rbconfig' require 'find' require 'fileutils' require 'tempfile' require 'optparse' require 'ostruct' include FileUtils InstallOptions = OpenStruct.new # Returns true if OS is windows (copied from facter/util/config.rb) def is_windows? (defined?(RbConfig) ? RbConfig : Config)::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i end def glob(list) g = list.map { |i| Dir.glob(i) } g.flatten! g.compact! g end def do_configs(configs, target, strip = 'ext/') Dir.mkdir(target) unless File.directory? target configs.each do |cf| ocf = File.join(InstallOptions.config_dir, cf.gsub(/#{strip}/, '')) install(cf, ocf, {:mode => 0644, :preserve => true, :verbose => true}) end end def do_bins(bins, target, strip = 's?bin/') Dir.mkdir(target) unless File.directory? target bins.each do |bf| obf = bf.gsub(/#{strip}/, '') install_binfile(bf, obf, target) end end def do_libs(libs, strip = 'lib/') libs.each do |lf| olf = File.join(InstallOptions.site_dir, lf.gsub(/#{strip}/, '')) op = File.dirname(olf) makedirs(op, {:mode => 0755, :verbose => true}) chmod(0755, op) install(lf, olf, {:mode => 0644, :preserve => true, :verbose => true}) end end def do_man(man, strip = 'man/') man.each do |mf| omf = File.join(InstallOptions.man_dir, mf.gsub(/#{strip}/, '')) om = File.dirname(omf) makedirs(om, {:mode => 0755, :verbose => true}) chmod(0755, om) install(mf, omf, {:mode => 0644, :preserve => true, :verbose => true}) gzip = %x{which gzip} gzip.chomp! %x{#{gzip} -f #{omf}} end end ## # Prepare the file installation. # def prepare_installation InstallOptions.configs = true ARGV.options do |opts| opts.banner = "Usage: #{File.basename($0)} [options]" opts.separator "" opts.on('--[no-]configs', 'Prevents the installation of config files', 'Default off.') do |onconfigs| InstallOptions.configs = onconfigs end opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir| InstallOptions.destdir = destdir end opts.on('--configdir[=OPTIONAL]', 'Installation directory for config files', 'Default /etc') do |configdir| InstallOptions.configdir = configdir end opts.on('--bindir[=OPTIONAL]', 'Installation directory for binaries', 'overrides RbConfig::CONFIG["bindir"]') do |bindir| InstallOptions.bindir = bindir end opts.on('--ruby[=OPTIONAL]', 'Ruby interpreter to use with installation', 'overrides ruby used to call install.rb') do |ruby| InstallOptions.ruby = ruby end opts.on('--sitelibdir[=OPTIONAL]', 'Installation directory for libraries', 'overrides RbConfig::CONFIG["sitelibdir"]') do |sitelibdir| InstallOptions.sitelibdir = sitelibdir end opts.on('--mandir[=OPTIONAL]', 'Installation directory for man pages', 'overrides RbConfig::CONFIG["mandir"]') do |mandir| InstallOptions.mandir = mandir end opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full| InstallOptions.configs = true end opts.separator("") opts.on_tail('--help', "Shows this help text.") do STDERR.puts opts exit end opts.parse! end version = [RbConfig::CONFIG["MAJOR"], RbConfig::CONFIG["MINOR"]].join(".") libdir = File.join(RbConfig::CONFIG["libdir"], "ruby", version) # Mac OS X 10.5 and higher declare bindir # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin # which is not generally where people expect executables to be installed # These settings are appropriate defaults for all OS X versions. if RUBY_PLATFORM =~ /^universal-darwin[\d\.]+$/ RbConfig::CONFIG['bindir'] = "/usr/bin" end if InstallOptions.configdir configdir = InstallOptions.configdir elsif is_windows? begin require 'rubygems' require 'win32/dir' rescue LoadError => e puts "Cannot run on Microsoft Windows without the sys-admin, win32-process, win32-dir & win32-service gems: #{e}" exit(-1) end configdir = File.join(Dir::COMMON_APPDATA, "PuppetLabs", "hiera", "etc") else configdir = "/etc" end if InstallOptions.bindir bindir = InstallOptions.bindir else bindir = RbConfig::CONFIG['bindir'] end if InstallOptions.sitelibdir sitelibdir = InstallOptions.sitelibdir else sitelibdir = RbConfig::CONFIG["sitelibdir"] if sitelibdir.nil? sitelibdir = $LOAD_PATH.find { |x| x =~ /site_ruby/ } if sitelibdir.nil? sitelibdir = File.join(libdir, "site_ruby") elsif sitelibdir !~ Regexp.quote(version) sitelibdir = File.join(sitelibdir, version) end end end if InstallOptions.mandir mandir = InstallOptions.mandir else mandir = RbConfig::CONFIG['mandir'] end # This is the new way forward if InstallOptions.destdir destdir = InstallOptions.destdir else destdir = '' end configdir = join(destdir, configdir) bindir = join(destdir, bindir) mandir = join(destdir, mandir) sitelibdir = join(destdir, sitelibdir) makedirs(configdir) if InstallOptions.configs makedirs(bindir) makedirs(mandir) makedirs(sitelibdir) InstallOptions.site_dir = sitelibdir InstallOptions.config_dir = configdir InstallOptions.bin_dir = bindir InstallOptions.lib_dir = libdir InstallOptions.man_dir = mandir end ## # Join two paths. On Windows, dir must be converted to a relative path, # by stripping the drive letter, but only if the basedir is not empty. # def join(basedir, dir) return "#{basedir}#{dir[2..-1]}" if is_windows? and basedir.length > 0 and dir.length > 2 "#{basedir}#{dir}" end ## # Install file(s) from ./bin to RbConfig::CONFIG['bindir']. Patch it on the way # to insert a #! line; on a Unix install, the command is named as expected # (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under # windows, we add an '.rb' extension and let file associations do their stuff. def install_binfile(from, op_file, target) tmp_file = Tempfile.new('hiera-binfile') if not InstallOptions.ruby.nil? ruby = InstallOptions.ruby else ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) end File.open(from) do |ip| File.open(tmp_file.path, "w") do |op| op.puts "#!#{ruby}" contents = ip.readlines contents.shift if contents[0] =~ /^#!/ op.write contents.join end end if is_windows? installed_wrapper = false if File.exists?("#{from}.bat") install("#{from}.bat", File.join(target, "#{op_file}.bat"), :mode => 0755, :preserve => true, :verbose => true) installed_wrapper = true end if File.exists?("#{from}.cmd") install("#{from}.cmd", File.join(target, "#{op_file}.cmd"), :mode => 0755, :preserve => true, :verbose => true) installed_wrapper = true end if not installed_wrapper tmp_file2 = Tempfile.new('hiera-wrapper') cwv = <<-EOS @echo off SETLOCAL if exist "%~dp0environment.bat" ( call "%~dp0environment.bat" %0 %* ) else ( SET "PATH=%~dp0;%PATH%" ) ruby.exe -S -- hiera %* EOS File.open(tmp_file2.path, "w") { |cw| cw.puts cwv } install(tmp_file2.path, File.join(target, "#{op_file}.bat"), :mode => 0755, :preserve => true, :verbose => true) tmp_file2.unlink installed_wrapper = true end end install(tmp_file.path, File.join(target, op_file), :mode => 0755, :preserve => true, :verbose => true) tmp_file.unlink end # Change directory into the hiera root so we don't get the wrong files for install. cd File.dirname(__FILE__) do # Set these values to what you want installed. configs = glob(%w{ext/hiera.yaml}) bins = glob(%w{bin/*}) man = glob(%w{man/man[0-9]/*}) libs = glob(%w{lib/**/*.rb}) prepare_installation do_configs(configs, InstallOptions.config_dir) if InstallOptions.configs do_bins(bins, InstallOptions.bin_dir) do_libs(libs) do_man(man) unless is_windows? end hiera-3.2.0/lib/000077500000000000000000000000001271216112200133455ustar00rootroot00000000000000hiera-3.2.0/lib/hiera.rb000066400000000000000000000137751271216112200147770ustar00rootroot00000000000000require 'yaml' class Hiera require "hiera/error" require "hiera/version" require "hiera/config" require "hiera/util" require "hiera/backend" require "hiera/console_logger" require "hiera/puppet_logger" require "hiera/noop_logger" require "hiera/fallback_logger" require "hiera/filecache" class << self attr_reader :logger # Loggers are pluggable, just provide a class called # Hiera::Foo_logger and respond to :warn and :debug # # See hiera-puppet for an example that uses the Puppet # loging system instead of our own def logger=(logger) require "hiera/#{logger}_logger" @logger = Hiera::FallbackLogger.new( Hiera.const_get("#{logger.capitalize}_logger"), Hiera::Console_logger) rescue Exception => e @logger = Hiera::Console_logger warn("Failed to load #{logger} logger: #{e.class}: #{e}") end def warn(msg); @logger.warn(msg); end def debug(msg); @logger.debug(msg); end end attr_reader :options, :config # If the config option is a string its assumed to be a filename, # else a hash of what would have been in the YAML config file def initialize(options={}) config = options[:config] if config.nil? # Look in codedir first, then confdir config = File.join(Util.code_dir, 'hiera.yaml') config = File.join(Util.config_dir, 'hiera.yaml') unless File.exist?(config) end @config = Config.load(config) Config.load_backends end # Calls the backends to do the actual lookup. # # The _scope_ can be anything that responds to `[]`, if you have input # data like a Puppet Scope that does not you can wrap that data in a # class that has a `[]` method that fetches the data from your source. # See hiera-puppet for an example of this. # # The order-override will insert as first in the hierarchy a data source # of your choice. # # Possible values for the _resolution_type_ parameter: # # - _:priority_ - This is the default. First found value is returned and no merge is performed # - _:array_ - An array merge lookup assembles a value from every matching level of the hierarchy. It retrieves all # of the (string or array) values for a given key, then flattens them into a single array of unique values. # If _priority_ lookup can be thought of as a “default with overrides” pattern, _array_ merge lookup can be though # of as “default with additions.” # - _:hash_ - A hash merge lookup assembles a value from every matching level of the hierarchy. It retrieves all of # the (hash) values for a given key, then merges the hashes into a single hash. Hash merge lookups will fail with # an error if any of the values found in the data sources are strings or arrays. It only works when every value # found is a hash. The actual merge behavior is determined by looking up the keys `:merge_behavior` and # `:deep_merge_options` in the Hiera config. `:merge_behavior` can be set to `:deep`, :deeper` or `:native` # (explained in detail below). # - _{ deep merge options }_ - Configured values for `:merge_behavior` and `:deep_merge_options`will be completely # ignored. Instead the _resolution_type_ will be a `:hash` merge where the `:merge_behavior` will be the value # keyed by `:behavior` in the given hash and the `:deep_merge_options` will be the remaining top level entries of # that same hash. # # Valid behaviors for the _:hash_ resolution type: # # - _native_ - Performs a simple hash-merge by overwriting keys of lower lookup priority. # - _deeper_ - In a deeper hash merge, Hiera recursively merges keys and values in each source hash. For each key, # if the value is: # - only present in one source hash, it goes into the final hash. # - a string/number/boolean and exists in two or more source hashes, the highest priority value goes into # the final hash. # - an array and exists in two or more source hashes, the values from each source are merged into a single # array and de-duplicated (but not automatically flattened, as in an array merge lookup). # - a hash and exists in two or more source hashes, the values from each source are recursively merged, as # though they were source hashes. # - mismatched between two or more source hashes, we haven’t validated the behavior. It should act as # described in the deep_merge gem documentation. # - _deep_ - In a deep hash merge, Hiera behaves the same as for _deeper_, except that when a string/number/boolean # exists in two or more source hashes, the lowest priority value goes into the final hash. This is considered # largely useless and should be avoided. Use _deeper_ instead. # # The _merge_ can be given as a hash with the mandatory key `:strategy` to denote the actual strategy. This # is useful for the `:deeper` and `:deep` strategy since they can use additional options to control the behavior. # The options can be passed as top level keys in the `merge` parameter when it is a given as a hash. Recognized # options are: # # - 'knockout_prefix' Set to string value to signify prefix which deletes elements from existing element. Defaults is _undef_ # - 'sort_merged_arrays' Set to _true_ to sort all arrays that are merged together. Default is _false_ # - 'merge_hash_arrays' Set to _true_ to merge hashes within arrays. Default is _false_ # # @param key [String] The key to lookup # @param default [Object,nil] The value to return when there is no match for _key_ # @param scope [#[],nil] The scope to use for the lookup # @param order_override [#[]] An override that will considered the first source of lookup # @param resolution_type [String,Hash] Symbolic resolution type or deep merge configuration # @return [Object] The found value or the given _default_ value def lookup(key, default, scope, order_override=nil, resolution_type=:priority) Backend.lookup(key, default, scope, order_override, resolution_type) end end hiera-3.2.0/lib/hiera/000077500000000000000000000000001271216112200144355ustar00rootroot00000000000000hiera-3.2.0/lib/hiera/backend.rb000066400000000000000000000334601271216112200163570ustar00rootroot00000000000000require 'hiera/util' require 'hiera/interpolate' begin require 'deep_merge/rails_compat' rescue LoadError end class Hiera module Backend class Backend1xWrapper def initialize(wrapped) @wrapped = wrapped end def lookup(key, scope, order_override, resolution_type, context) Hiera.debug("Using Hiera 1.x backend API to access instance of class #{@wrapped.class.name}. Lookup recursion will not be detected") value = @wrapped.lookup(key, scope, order_override, resolution_type.is_a?(Hash) ? :hash : resolution_type) # The most likely cause when an old backend returns nil is that the key was not found. In any case, it is # impossible to know the difference between that and a found nil. The throw here preserves the old behavior. throw (:no_such_key) if value.nil? value end end class << self # Data lives in /var/lib/hiera by default. If a backend # supplies a datadir in the config it will be used and # subject to variable expansion based on scope def datadir(backend, scope) backend = backend.to_sym if Config[backend] && Config[backend][:datadir] dir = Config[backend][:datadir] else dir = Hiera::Util.var_dir end if !dir.is_a?(String) raise(Hiera::InvalidConfigurationError, "datadir for #{backend} cannot be an array") end interpolate_config(dir, scope, nil) end # Finds the path to a datafile based on the Backend#datadir # and extension # # If the file is not found nil is returned def datafile(backend, scope, source, extension) datafile_in(datadir(backend, scope), source, extension) end # @api private def datafile_in(datadir, source, extension) file = File.join(datadir, "#{source}.#{extension}") if File.exist?(file) file else Hiera.debug("Cannot find datafile #{file}, skipping") nil end end # Constructs a list of data sources to search # # If you give it a specific hierarchy it will just use that # else it will use the global configured one, failing that # it will just look in the 'common' data source. # # An override can be supplied that will be pre-pended to the # hierarchy. # # The source names will be subject to variable expansion based # on scope def datasources(scope, override=nil, hierarchy=nil) if hierarchy hierarchy = [hierarchy] elsif Config.include?(:hierarchy) hierarchy = [Config[:hierarchy]].flatten else hierarchy = ["common"] end hierarchy.insert(0, override) if override hierarchy.flatten.map do |source| source = interpolate_config(source, scope, override) yield(source) unless source == "" or source =~ /(^\/|\/\/|\/$)/ end end # Constructs a list of data files to search # # If you give it a specific hierarchy it will just use that # else it will use the global configured one, failing that # it will just look in the 'common' data source. # # An override can be supplied that will be pre-pended to the # hierarchy. # # The source names will be subject to variable expansion based # on scope # # Only files that exist will be returned. If the file is missing, then # the block will not receive the file. # # @yield [String, String] the source string and the name of the resulting file # @api public def datasourcefiles(backend, scope, extension, override=nil, hierarchy=nil) datadir = Backend.datadir(backend, scope) Backend.datasources(scope, override, hierarchy) do |source| Hiera.debug("Looking for data source #{source}") file = datafile_in(datadir, source, extension) if file yield source, file end end end # Parse a string like '%{foo}' against a supplied # scope and additional scope. If either scope or # extra_scope includes the variable 'foo', then it will # be replaced else an empty string will be placed. # # If both scope and extra_data has "foo", then the value in scope # will be used. # # @param data [String] The string to perform substitutions on. # This will not be modified, instead a new string will be returned. # @param scope [#[]] The primary source of data for substitutions. # @param extra_data [#[]] The secondary source of data for substitutions. # @param context [#[]] Context can include :recurse_guard and :order_override. # @return [String] A copy of the data with all instances of %{...} replaced. # # @api public def parse_string(data, scope, extra_data={}, context={:recurse_guard => nil, :order_override => nil}) Hiera::Interpolate.interpolate(data, scope, extra_data, context) end # Parses a answer received from data files # # Ultimately it just pass the data through parse_string but # it makes some effort to handle arrays of strings as well def parse_answer(data, scope, extra_data={}, context={:recurse_guard => nil, :order_override => nil}) if data.is_a?(Numeric) or data.is_a?(TrueClass) or data.is_a?(FalseClass) return data elsif data.is_a?(String) return parse_string(data, scope, extra_data, context) elsif data.is_a?(Hash) answer = {} data.each_pair do |key, val| interpolated_key = parse_string(key, scope, extra_data, context) answer[interpolated_key] = parse_answer(val, scope, extra_data, context) end return answer elsif data.is_a?(Array) answer = [] data.each do |item| answer << parse_answer(item, scope, extra_data, context) end return answer end end def resolve_answer(answer, resolution_type) case resolution_type when :array [answer].flatten.uniq.compact when :hash answer # Hash structure should be preserved else answer end end # Merges two hashes answers with the given or configured merge behavior. Behavior can be given # by passing _resolution_type_ as a Hash # # :merge_behavior: {:native|:deep|:deeper} # # Deep merge options use the Hash utility function provided by [deep_merge](https://github.com/danielsdeleo/deep_merge) # It uses the compatibility mode [deep_merge](https://github.com/danielsdeleo/deep_merge#using-deep_merge-in-rails) # # :native => Native Hash.merge # :deep => Use Hash.deeper_merge # :deeper => Use Hash.deeper_merge! # # @param left [Hash] left side of the merge # @param right [Hash] right side of the merge # @param resolution_type [String,Hash] The merge type, or if hash, the merge behavior and options # @return [Hash] The merged result # @see Hiera#lookup # def merge_answer(left,right,resolution_type=nil) behavior, options = if resolution_type.is_a?(Hash) merge = resolution_type.clone [merge.delete(:behavior), merge] else [Config[:merge_behavior], Config[:deep_merge_options] || {}] end case behavior when :deeper,'deeper' left.deeper_merge!(right, options) when :deep,'deep' left.deeper_merge(right, options) else # Native and undefined left.merge(right) end end # Calls out to all configured backends in the order they # were specified. The first one to answer will win. # # This lets you declare multiple backends, a possible # use case might be in Puppet where a Puppet module declares # default data using in-module data while users can override # using JSON/YAML etc. By layering the backends and putting # the Puppet one last you can override module author data # easily. # # Backend instances are cached so if you need to connect to any # databases then do so in your constructor, future calls to your # backend will not create new instances # @param key [String] The key to lookup. May be quoted with single or double quotes to avoid subkey traversal on dot characters # @param scope [#[]] The primary source of data for substitutions. # @param order_override [#[],nil] An override that will be pre-pended to the hierarchy definition. # @param resolution_type [Symbol,Hash,nil] One of :hash, :array,:priority or a Hash with deep merge behavior and options # @param context [#[]] Context used for internal processing # @return [Object] The value that corresponds to the given key or nil if no such value cannot be found # def lookup(key, default, scope, order_override, resolution_type, context = {:recurse_guard => nil}) @backends ||= {} answer = nil # order_override is kept as an explicit argument for backwards compatibility, but should be specified # in the context for internal handling. context ||= {} order_override ||= context[:order_override] context[:order_override] ||= order_override strategy = resolution_type.is_a?(Hash) ? :hash : resolution_type segments = Util.split_key(key) { |problem| ArgumentError.new("#{problem} in key: #{key}") } subsegments = nil if segments.size > 1 unless strategy.nil? || strategy == :priority raise ArgumentError, "Resolution type :#{strategy} is illegal when accessing values using dotted keys. Offending key was '#{key}'" end subsegments = segments.drop(1) end found = false Config[:backends].each do |backend| backend_constant = "#{backend.capitalize}_backend" if constants.include?(backend_constant) || constants.include?(backend_constant.to_sym) backend = (@backends[backend] ||= find_backend(backend_constant)) found_in_backend = false new_answer = catch(:no_such_key) do if subsegments.nil? value = backend.lookup(segments[0], scope, order_override, resolution_type, context) elsif backend.respond_to?(:lookup_with_segments) value = backend.lookup_with_segments(segments, scope, order_override, resolution_type, context) else value = backend.lookup(segments[0], scope, order_override, resolution_type, context) value = qualified_lookup(subsegments, value, key) unless subsegments.nil? end found_in_backend = true value end next unless found_in_backend found = true case strategy when :array raise Exception, "Hiera type mismatch for key '#{key}': expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String answer ||= [] answer << new_answer when :hash raise Exception, "Hiera type mismatch for key '#{key}': expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash answer ||= {} answer = merge_answer(new_answer, answer, resolution_type) else answer = new_answer break end end end answer = resolve_answer(answer, strategy) unless answer.nil? answer = parse_string(default, scope, {}, context) if !found && default.is_a?(String) return default if !found && answer.nil? return answer end def clear! @backends = {} end def qualified_lookup(segments, hash, full_key = nil) value = hash segments.each do |segment| throw :no_such_key if value.nil? if segment =~ /^[0-9]+$/ segment = segment.to_i unless value.instance_of?(Array) suffix = full_key.nil? ? '' : " from key '#{full_key}'" raise Exception, "Hiera type mismatch: Got #{value.class.name} when Array was expected to access value using '#{segment}'#{suffix}" end throw :no_such_key unless segment < value.size else unless value.respond_to?(:'[]') && !(value.instance_of?(Array) || value.instance_of?(String)) suffix = full_key.nil? ? '' : " from key '#{full_key}'" raise Exception, "Hiera type mismatch: Got #{value.class.name} when a hash-like object was expected to access value using '#{segment}'#{suffix}" end throw :no_such_key unless value.include?(segment) end value = value[segment] end value end def find_backend(backend_constant) backend = Backend.const_get(backend_constant).new return backend.method(:lookup).arity == 4 ? Backend1xWrapper.new(backend) : backend end private :find_backend def interpolate_config(entry, scope, override) if @config_lookup_context.nil? @config_lookup_context = { :is_interpolate_config => true, :order_override => override, :recurse_guard => Hiera::RecursiveGuard.new } begin Hiera::Interpolate.interpolate(entry, scope, {}, @config_lookup_context) ensure @config_lookup_context = nil end else # Nested call (will happen when interpolate method 'hiera' is used) Hiera::Interpolate.interpolate(entry, scope, {}, @config_lookup_context.merge(:order_override => override)) end end end end end hiera-3.2.0/lib/hiera/backend/000077500000000000000000000000001271216112200160245ustar00rootroot00000000000000hiera-3.2.0/lib/hiera/backend/json_backend.rb000066400000000000000000000036111271216112200207720ustar00rootroot00000000000000class Hiera module Backend class Json_backend def initialize(cache=nil) require 'json' Hiera.debug("Hiera JSON backend starting") @cache = cache || Filecache.new end def lookup(key, scope, order_override, resolution_type, context) answer = nil found = false Hiera.debug("Looking up #{key} in JSON backend") Backend.datasources(scope, order_override) do |source| Hiera.debug("Looking for data source #{source}") jsonfile = Backend.datafile(:json, scope, source, "json") || next next unless File.exist?(jsonfile) data = @cache.read_file(jsonfile, Hash) do |data| JSON.parse(data) end next if data.empty? next unless data.include?(key) found = true # for array resolution we just append to the array whatever # we find, we then goes onto the next file and keep adding to # the array # # for priority searches we break after the first found data item new_answer = Backend.parse_answer(data[key], scope, {}, context) case resolution_type.is_a?(Hash) ? :hash : resolution_type when :array raise Exception, "Hiera type mismatch for key '#{key}': expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String answer ||= [] answer << new_answer when :hash raise Exception, "Hiera type mismatch for key '#{key}': expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash answer ||= {} answer = Backend.merge_answer(new_answer, answer, resolution_type) else answer = new_answer break end end throw :no_such_key unless found return answer end end end end hiera-3.2.0/lib/hiera/backend/yaml_backend.rb000066400000000000000000000042041271216112200207620ustar00rootroot00000000000000class Hiera module Backend class Yaml_backend def initialize(cache=nil) require 'yaml' Hiera.debug("Hiera YAML backend starting") @cache = cache || Filecache.new end def lookup(key, scope, order_override, resolution_type, context) answer = nil found = false Hiera.debug("Looking up #{key} in YAML backend") Backend.datasourcefiles(:yaml, scope, "yaml", order_override) do |source, yamlfile| data = @cache.read_file(yamlfile, Hash) do |data| YAML.load(data) || {} end next if data.empty? next unless data.include?(key) found = true # Extra logging that we found the key. This can be outputted # multiple times if the resolution type is array or hash but that # should be expected as the logging will then tell the user ALL the # places where the key is found. Hiera.debug("Found #{key} in #{source}") # for array resolution we just append to the array whatever # we find, we then goes onto the next file and keep adding to # the array # # for priority searches we break after the first found data item new_answer = Backend.parse_answer(data[key], scope, {}, context) case resolution_type.is_a?(Hash) ? :hash : resolution_type when :array raise Exception, "Hiera type mismatch for key '#{key}': expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String answer ||= [] answer << new_answer when :hash raise Exception, "Hiera type mismatch for key '#{key}': expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash answer ||= {} answer = Backend.merge_answer(new_answer, answer, resolution_type) else answer = new_answer break end end throw :no_such_key unless found return answer end private def file_exists?(path) File.exist? path end end end end hiera-3.2.0/lib/hiera/config.rb000066400000000000000000000047311271216112200162340ustar00rootroot00000000000000class Hiera::Config class << self ## # load takes a string or hash as input, strings are treated as filenames # hashes are stored as data that would have been in the config file # # Unless specified it will only use YAML as backend with a # hierarchy of 'nodes/%{::trusted.certname}' and 'common', and with a # console logger. # # @return [Hash] representing the configuration. def load(source) @config = {:backends => ["yaml"], :hierarchy => ["nodes/%{::trusted.certname}", "common"], :merge_behavior => :native } if source.is_a?(String) if File.exist?(source) config = begin yaml_load_file(source) rescue TypeError => detail case detail.message when /no implicit conversion from nil to integer/ false else raise detail end end @config.merge! config if config else raise "Config file #{source} not found" end elsif source.is_a?(Hash) @config.merge! source end @config[:backends] = [ @config[:backends] ].flatten if @config.include?(:logger) Hiera.logger = @config[:logger].to_s else @config[:logger] = "console" Hiera.logger = "console" end self.validate! @config end def validate! case @config[:merge_behavior] when :deep,'deep',:deeper,'deeper' begin require "deep_merge" require "deep_merge/rails_compat" rescue LoadError raise Hiera::Error, "Must have 'deep_merge' gem installed for the configured merge_behavior." end end end ## # yaml_load_file directly delegates to YAML.load_file and is intended to be # a private, internal method suitable for stubbing and mocking. # # @return [Object] return value of {YAML.load_file} def yaml_load_file(source) YAML.load_file(source) end private :yaml_load_file def load_backends @config[:backends].each do |backend| begin require "hiera/backend/#{backend.downcase}_backend" rescue LoadError => e raise "Cannot load backend #{backend}: #{e}" end end end def include?(key) @config.include?(key) end def [](key) @config[key] end end end hiera-3.2.0/lib/hiera/console_logger.rb000066400000000000000000000003751271216112200177700ustar00rootroot00000000000000class Hiera module Console_logger class << self def warn(msg) STDERR.puts("WARN: %s: %s" % [Time.now.to_s, msg]) end def debug(msg) STDERR.puts("DEBUG: %s: %s" % [Time.now.to_s, msg]) end end end end hiera-3.2.0/lib/hiera/error.rb000066400000000000000000000001421271216112200161100ustar00rootroot00000000000000class Hiera class Error < StandardError; end class InvalidConfigurationError < Error; end end hiera-3.2.0/lib/hiera/fallback_logger.rb000066400000000000000000000021301271216112200200540ustar00rootroot00000000000000# Select from a given list of loggers the first one that # it suitable and use that as the actual logger # # @api private class Hiera::FallbackLogger # Chooses the first suitable logger. For all of the loggers that are # unsuitable it will issue a warning using the suitable logger stating that # the unsuitable logger is not being used. # # @param implementations [Array] the implementations to choose from # @raises when there are no suitable loggers def initialize(*implementations) warnings = [] @implementation = implementations.find do |impl| if impl.respond_to?(:suitable?) if impl.suitable? true else warnings << "Not using #{impl.name}. It does not report itself to be suitable." false end else true end end if @implementation.nil? raise "No suitable logging implementation found." end warnings.each { |message| warn(message) } end def warn(message) @implementation.warn(message) end def debug(message) @implementation.debug(message) end end hiera-3.2.0/lib/hiera/filecache.rb000066400000000000000000000051651271216112200166740ustar00rootroot00000000000000class Hiera class Filecache def initialize @cache = {} end # Reads a file, optionally parse it in some way check the # output type and set a default # # Simply invoking it this way will return the file contents # # data = read("/some/file") # # But as most cases of file reading in hiera involves some kind # of parsing through a serializer there's some help for those # cases: # # data = read("/some/file", Hash, {}) do |data| # JSON.parse(data) # end # # In this case it will read the file, parse it using JSON then # check that the end result is a Hash, if it's not a hash or if # reading/parsing fails it will return {} instead # # Prior to calling this method you should be sure the file exist def read(path, expected_type = Object, default=nil, &block) read_file(path, expected_type, &block) rescue TypeError => detail Hiera.debug("#{detail.message}, setting defaults") @cache[path][:data] = default rescue => detail error = "Reading data from #{path} failed: #{detail.class}: #{detail}" if default.nil? raise detail else Hiera.debug(error) @cache[path][:data] = default end end # Read a file when it changes. If a file is re-read and has not changed since the last time # then the last, processed, contents will be returned. # # The processed data can also be checked against an expected type. If the # type does not match a TypeError is raised. # # No error handling is done inside this method. Any failed reads or errors # in processing will be propagated to the caller def read_file(path, expected_type = Object) if stale?(path) data = File.read(path) @cache[path][:data] = block_given? ? yield(data) : data if !@cache[path][:data].is_a?(expected_type) raise TypeError, "Data retrieved from #{path} is #{@cache[path][:data].class} not #{expected_type}" end end @cache[path][:data] end private def stale?(path) meta = path_metadata(path) @cache[path] ||= {:data => nil, :meta => nil} if @cache[path][:meta] == meta return false else @cache[path][:meta] = meta return true end end # This is based on the old caching in the YAML backend and has a # resolution of 1 second, changes made within the same second of # a previous read will be ignored def path_metadata(path) stat = File.stat(path) {:inode => stat.ino, :mtime => stat.mtime, :size => stat.size} end end end hiera-3.2.0/lib/hiera/interpolate.rb000066400000000000000000000107161271216112200173150ustar00rootroot00000000000000require 'hiera/backend' require 'hiera/recursive_guard' class Hiera::InterpolationInvalidValue < StandardError; end # @api private class Hiera::Interpolate RX_INTERPOLATION = /%\{([^\}]*)\}/ RX_ONLY_INTERPOLATION = /^%\{([^\}]*)\}$/ RX_METHOD_AND_ARG = /^(\w+)\(([^)]*)\)$/ EMPTY_INTERPOLATIONS = { '' => true, '::' => true, '""' => true, "''" => true, '"::"' => true, "'::'" => true }.freeze INTERPOLATION_METHODS = { 'hiera' => :hiera_interpolate, 'scope' => :scope_interpolate, 'literal' => :literal_interpolate, 'alias' => :alias_interpolate }.freeze class << self # These two patterns are never used but kept here anyway since they used to be public and therefore # must be considered API. The class is now marked @api private and these should be removed in a # future version # # @deprecated INTERPOLATION = /%\{([^\}]*)\}/ # @deprecated METHOD_INTERPOLATION = /%\{(scope|hiera|literal|alias)\(['"]([^"']*)["']\)\}/ def interpolate(data, scope, extra_data, context) if data.is_a?(String) # Wrapping do_interpolation in a gsub block ensures we process # each interpolation site in isolation using separate recursion guards. new_context = context.nil? ? {} : context.clone new_context[:recurse_guard] ||= Hiera::RecursiveGuard.new data.gsub(RX_INTERPOLATION) do |match| (interp_val, interpolate_method) = do_interpolation(match, scope, extra_data, new_context) if (interpolate_method == :alias_interpolate) && !interp_val.is_a?(String) return interp_val if data.match(RX_ONLY_INTERPOLATION) raise Hiera::InterpolationInvalidValue, "Cannot call alias in the string context" else interp_val end end else data end end def do_interpolation(data, scope, extra_data, context) if data.is_a?(String) && (match = data.match(RX_INTERPOLATION)) interpolation_variable = match[1] # HI-494 return ['', nil] if EMPTY_INTERPOLATIONS[interpolation_variable.strip] context[:recurse_guard].check(interpolation_variable) do interpolate_method, key = get_interpolation_method_and_key(interpolation_variable, context) interpolated_data = send(interpolate_method, data, key, scope, extra_data, context) # Halt recursion if we encounter a literal. return [interpolated_data, interpolate_method] if interpolate_method == :literal_interpolate [do_interpolation(interpolated_data, scope, extra_data, context)[0], interpolate_method] end else [data, nil] end end private :do_interpolation def get_interpolation_method_and_key(interpolation_variable, context) if (match = interpolation_variable.match(RX_METHOD_AND_ARG)) Hiera.warn('Use of interpolation methods in hiera configuration file is deprecated') if context[:is_interpolate_config] method = match[1] method_sym = INTERPOLATION_METHODS[method] raise Hiera::InterpolationInvalidValue, "Invalid interpolation method '#{method}'" unless method_sym arg = match[2] match_data = arg.match(Hiera::QUOTED_KEY) raise Hiera::InterpolationInvalidValue, "Argument to interpolation method '#{method}' must be quoted, got '#{arg}'" unless match_data [method_sym, match_data[1] || match_data[2]] else [:scope_interpolate, interpolation_variable] end end private :get_interpolation_method_and_key def scope_interpolate(data, key, scope, extra_data, context) segments = Hiera::Util.split_key(key) { |problem| Hiera::InterpolationInvalidValue.new("#{problem} in interpolation expression: #{data}") } catch(:no_such_key) { return Hiera::Backend.qualified_lookup(segments, scope, key) } catch(:no_such_key) { Hiera::Backend.qualified_lookup(segments, extra_data, key) } end private :scope_interpolate def hiera_interpolate(data, key, scope, extra_data, context) Hiera::Backend.lookup(key, nil, scope, context[:order_override], :priority, context) end private :hiera_interpolate def literal_interpolate(data, key, scope, extra_data, context) key end private :literal_interpolate def alias_interpolate(data, key, scope, extra_data, context) Hiera::Backend.lookup(key, nil, scope, context[:order_override], :priority, context) end private :alias_interpolate end end hiera-3.2.0/lib/hiera/noop_logger.rb000066400000000000000000000001661271216112200172770ustar00rootroot00000000000000class Hiera module Noop_logger class << self def warn(msg);end def debug(msg);end end end end hiera-3.2.0/lib/hiera/puppet_logger.rb000066400000000000000000000004361271216112200176410ustar00rootroot00000000000000class Hiera module Puppet_logger class << self def suitable? defined?(::Puppet) == "constant" end def warn(msg) Puppet.notice("hiera(): #{msg}") end def debug(msg) Puppet.debug("hiera(): #{msg}") end end end end hiera-3.2.0/lib/hiera/recursive_guard.rb000066400000000000000000000006651271216112200201620ustar00rootroot00000000000000# Allow for safe recursive lookup of values during variable interpolation. # # @api private class Hiera::InterpolationLoop < StandardError; end class Hiera::RecursiveGuard def initialize @seen = [] end def check(value, &block) if @seen.include?(value) raise Hiera::InterpolationLoop, "Lookup recursion detected in [#{@seen.join(', ')}]" end @seen.push(value) ret = yield @seen.pop ret end end hiera-3.2.0/lib/hiera/util.rb000066400000000000000000000031211271216112200157340ustar00rootroot00000000000000class Hiera # Matches a key that is quoted using a matching pair of either single or double quotes. QUOTED_KEY = /^(?:"([^"]+)"|'([^']+)')$/ QUOTES = /[",]/ module Util module_function def posix? require 'etc' Etc.getpwuid(0) != nil end def microsoft_windows? return false unless file_alt_separator begin require 'win32/dir' true rescue LoadError => err warn "Cannot run on Microsoft Windows without the win32-dir gem: #{err}" false end end def config_dir if microsoft_windows? File.join(common_appdata, 'PuppetLabs', 'puppet', 'etc') else '/etc/puppetlabs/puppet' end end def code_dir if microsoft_windows? File.join(common_appdata, 'PuppetLabs', 'code') else '/etc/puppetlabs/code' end end def var_dir File.join(code_dir, 'environments' , '%{environment}' , 'hieradata') end def file_alt_separator File::ALT_SEPARATOR end def common_appdata Dir::COMMON_APPDATA end def split_key(key) segments = key.split(/(?:"([^"]+)"|'([^']+)'|([^'".]+))/) if segments.empty? # Only happens if the original key was an empty string '' elsif segments.shift == '' count = segments.size raise yield('Syntax error') unless count > 0 segments.keep_if { |seg| seg != '.' } raise yield('Syntax error') unless segments.size * 2 == count + 1 segments else raise yield('Syntax error') end end end end hiera-3.2.0/lib/hiera/version.rb000066400000000000000000000067711271216112200164620ustar00rootroot00000000000000# The version method and constant are isolated in hiera/version.rb so that a # simple `require 'hiera/version'` allows a rubygems gemspec or bundler # Gemfile to get the hiera version of the gem install. # # The version is programatically settable because we want to allow the # Raketasks and such to set the version based on the output of `git describe` class Hiera VERSION = "3.2.0" ## # version is a public API method intended to always provide a fast and # lightweight way to determine the version of hiera. # # The intent is that software external to hiera be able to determine the # hiera version with no side-effects. The expected use is: # # require 'hiera/version' # version = Hiera.version # # This function has the following ordering precedence. This precedence list # is designed to facilitate automated packaging tasks by simply writing to # the VERSION file in the same directory as this source file. # # 1. If a version has been explicitly assigned using the Hiera.version= # method, return that version. # 2. If there is a VERSION file, read the contents, trim any # trailing whitespace, and return that version string. # 3. Return the value of the Hiera::VERSION constant hard-coded into # the source code. # # If there is no VERSION file, the method must return the version string of # the nearest parent version that is an officially released version. That is # to say, if a branch named 3.1.x contains 25 patches on top of the most # recent official release of 3.1.1, then the version method must return the # string "3.1.1" if no "VERSION" file is present. # # By design the version identifier is _not_ intended to vary during the life # a process. There is no guarantee provided that writing to the VERSION file # while a Hiera process is running will cause the version string to be # updated. On the contrary, the contents of the VERSION are cached to reduce # filesystem accesses. # # The VERSION file is intended to be used by package maintainers who may be # applying patches or otherwise changing the software version in a manner # that warrants a different software version identifier. The VERSION file is # intended to be managed and owned by the release process and packaging # related tasks, and as such should not reside in version control. The # VERSION constant is intended to be version controlled in history. # # Ideally, this behavior will allow package maintainers to precisely specify # the version of the software they're packaging as in the following example: # # $ git describe --match "1.2.*" > lib/hiera/VERSION # $ ruby -r hiera/version -e 'puts Hiera.version' # 1.2.1-9-g9fda440 # # @api public # # @return [String] containing the hiera version, e.g. "1.2.1" def self.version version_file = File.join(File.dirname(__FILE__), 'VERSION') return @hiera_version if @hiera_version if version = read_version_file(version_file) @hiera_version = version end @hiera_version ||= VERSION end def self.version=(version) @hiera_version = version end ## # read_version_file reads the content of the "VERSION" file that lives in the # same directory as this source code file. # # @api private # # @return [String] for example: "1.6.14-6-gea42046" or nil if the VERSION # file does not exist. def self.read_version_file(path) if File.exists?(path) File.read(path).chomp end end private_class_method :read_version_file end hiera-3.2.0/spec/000077500000000000000000000000001271216112200135315ustar00rootroot00000000000000hiera-3.2.0/spec/spec_helper.rb000066400000000000000000000042521271216112200163520ustar00rootroot00000000000000$:.insert(0, File.join([File.dirname(__FILE__), "..", "lib"])) require 'rubygems' require 'rspec' require 'mocha' require 'hiera' require 'tmpdir' RSpec.configure do |config| config.mock_with :mocha if Hiera::Util.microsoft_windows? && RUBY_VERSION =~ /^1\./ require 'win32console' config.output_stream = $stdout config.error_stream = $stderr config.formatters.each { |f| f.instance_variable_set(:@output, $stdout) } end config.after :suite do # Log the spec order to a file, but only if the LOG_SPEC_ORDER environment variable is # set. This should be enabled on Jenkins runs, as it can be used with Nick L.'s bisect # script to help identify and debug order-dependent spec failures. if ENV['LOG_SPEC_ORDER'] File.open("./spec_order.txt", "w") do |logfile| config.instance_variable_get(:@files_to_run).each { |f| logfile.puts f } end end end end # So everyone else doesn't have to include this base constant. module HieraSpec FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), 'unit', 'fixtures') unless defined?(FIXTURE_DIR) end # In ruby 1.8.5 Dir does not have mktmpdir defined, so this monkey patches # Dir to include the 1.8.7 definition of that method if it isn't already defined. # Method definition borrowed from ruby-1.8.7-p357/lib/ruby/1.8/tmpdir.rb unless Dir.respond_to?(:mktmpdir) def Dir.mktmpdir(prefix_suffix=nil, tmpdir=nil) case prefix_suffix when nil prefix = "d" suffix = "" when String prefix = prefix_suffix suffix = "" when Array prefix = prefix_suffix[0] suffix = prefix_suffix[1] else raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" end tmpdir ||= Dir.tmpdir t = Time.now.strftime("%Y%m%d") n = nil begin path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" path << "-#{n}" if n path << suffix Dir.mkdir(path, 0700) rescue Errno::EEXIST n ||= 0 n += 1 retry end if block_given? begin yield path ensure FileUtils.remove_entry_secure path end else path end end end hiera-3.2.0/spec/unit/000077500000000000000000000000001271216112200145105ustar00rootroot00000000000000hiera-3.2.0/spec/unit/backend/000077500000000000000000000000001271216112200160775ustar00rootroot00000000000000hiera-3.2.0/spec/unit/backend/json_backend_spec.rb000066400000000000000000000076641271216112200220730ustar00rootroot00000000000000require 'spec_helper' require 'hiera/backend/json_backend' class Hiera module Backend describe Json_backend do before do Hiera.stubs(:debug) Hiera.stubs(:warn) Hiera::Backend.stubs(:empty_answer).returns(nil) @cache = mock @backend = Json_backend.new(@cache) end describe "#initialize" do it "should announce its creation" do # because other specs checks this Hiera.expects(:debug).with("Hiera JSON backend starting") Json_backend.new end end describe "#lookup" do it "should look for data in all sources" do Backend.expects(:datasources).multiple_yields(["one"], ["two"]) Backend.expects(:datafile).with(:json, {}, "one", "json").returns(nil) Backend.expects(:datafile).with(:json, {}, "two", "json").returns(nil) expect { @backend.lookup("key", {}, nil, :priority, nil) }.to throw_symbol(:no_such_key) end it "should retain the data types found in data files" do Backend.expects(:datasources).yields("one").times(3) Backend.expects(:datafile).with(:json, {}, "one", "json").returns("/nonexisting/one.json").times(3) File.stubs(:exist?).with("/nonexisting/one.json").returns(true) @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"stringval" => "string", "boolval" => true, "numericval" => 1}).times(3) expect(@backend.lookup("stringval", {}, nil, :priority, nil)).to eq("string") expect(@backend.lookup("boolval", {}, nil, :priority, nil)).to eq(true) expect(@backend.lookup("numericval", {}, nil, :priority, nil)).to eq(1) end it "should pick data earliest source that has it for priority searches" do scope = {"rspec" => "test"} Backend.expects(:datasources).multiple_yields(["one"], ["two"]) Backend.expects(:datafile).with(:json, scope, "one", "json").returns("/nonexisting/one.json") Backend.expects(:datafile).with(:json, scope, "two", "json").never File.stubs(:exist?).with("/nonexisting/one.json").returns(true) @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "test_%{rspec}"}) expect(@backend.lookup("key", scope, nil, :priority, nil)).to eq("test_test") end it "should build an array of all data sources for array searches" do Hiera::Backend.stubs(:empty_answer).returns([]) Backend.stubs(:parse_answer).with('answer', {}, {}, anything).returns("answer") Backend.expects(:datafile).with(:json, {}, "one", "json").returns("/nonexisting/one.json") Backend.expects(:datafile).with(:json, {}, "two", "json").returns("/nonexisting/two.json") Backend.expects(:datasources).multiple_yields(["one"], ["two"]) File.expects(:exist?).with("/nonexisting/one.json").returns(true) File.expects(:exist?).with("/nonexisting/two.json").returns(true) @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "answer"}) @cache.expects(:read_file).with("/nonexisting/two.json", Hash).returns({"key" => "answer"}) expect(@backend.lookup("key", {}, nil, :array, nil)).to eq(["answer", "answer"]) end it "should parse the answer for scope variables" do Backend.stubs(:parse_answer).with('test_%{rspec}', {'rspec' => 'test'}, {}, anything).returns("test_test") Backend.expects(:datasources).yields("one") Backend.expects(:datafile).with(:json, {"rspec" => "test"}, "one", "json").returns("/nonexisting/one.json") File.expects(:exist?).with("/nonexisting/one.json").returns(true) @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "test_%{rspec}"}) expect(@backend.lookup("key", {"rspec" => "test"}, nil, :priority, nil)).to eq("test_test") end end end end end hiera-3.2.0/spec/unit/backend/yaml_backend_spec.rb000066400000000000000000000140501271216112200220470ustar00rootroot00000000000000require 'spec_helper' require 'hiera/backend/yaml_backend' class Hiera module Backend class FakeCache attr_accessor :value def read(path, expected_type, default, &block) read_file(path, expected_type, &block) rescue => e default end def read_file(path, expected_type, &block) output = block.call(@value) if !output.is_a? expected_type raise TypeError end output end end describe Yaml_backend do before do Config.load({}) Hiera.stubs(:debug) Hiera.stubs(:warn) @cache = FakeCache.new @backend = Yaml_backend.new(@cache) end describe "#initialize" do it "should announce its creation" do # because other specs checks this Hiera.expects(:debug).with("Hiera YAML backend starting") Yaml_backend.new end end describe "#lookup" do it "should pick data earliest source that has it for priority searches" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).yields(["one", "/nonexisting/one.yaml"]) @cache.value = "---\nkey: answer" expect(@backend.lookup("key", {}, nil, :priority, nil)).to eq("answer") end describe "handling unexpected YAML values" do before do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).yields(["one", "/nonexisting/one.yaml"]) end it "throws :no_such_key when key is missing in YAML" do @cache.value = "---\n" expect { @backend.lookup("key", {}, nil, :priority, nil) }.to throw_symbol(:no_such_key) end it "returns nil when the YAML value is nil" do @cache.value = "key: ~\n" expect(@backend.lookup("key", {}, nil, :priority, nil)).to be_nil end it "throws :no_such_key when the YAML file is false" do @cache.value = "" expect { @backend.lookup("key", {}, nil, :priority, nil) }.to throw_symbol(:no_such_key) end it "raises a TypeError when the YAML value is not a hash" do @cache.value = "---\n[one, two, three]" expect { @backend.lookup("key", {}, nil, :priority, nil) }.to raise_error(TypeError) end end it "should build an array of all data sources for array searches" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"]) @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"answer"}) @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>"answer"}) expect(@backend.lookup("key", {}, nil, :array, nil)).to eq(["answer", "answer"]) end it "should ignore empty hash of data sources for hash searches" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"]) @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({}) @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"a"=>"answer"}}) expect(@backend.lookup("key", {}, nil, :hash, nil)).to eq({"a" => "answer"}) end it "should build a merged hash of data sources for hash searches" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"]) @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>{"a"=>"answer"}}) @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"b"=>"answer", "a"=>"wrong"}}) expect(@backend.lookup("key", {}, nil, :hash, nil)).to eq({"a" => "answer", "b" => "answer"}) end it "should fail when trying to << a Hash" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"]) @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>["a", "answer"]}) @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"a"=>"answer"}}) expect {@backend.lookup("key", {}, nil, :array, nil)}.to raise_error(Exception, "Hiera type mismatch for key 'key': expected Array and got Hash") end it "should fail when trying to merge an Array" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"]) @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>{"a"=>"answer"}}) @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>["a", "wrong"]}) expect { @backend.lookup("key", {}, nil, :hash, nil) }.to raise_error(Exception, "Hiera type mismatch for key 'key': expected Hash and got Array") end it "should parse the answer for scope variables" do Backend.expects(:datasourcefiles).with(:yaml, {"rspec" => "test"}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"]) @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"test_%{rspec}"}) expect(@backend.lookup("key", {"rspec" => "test"}, nil, :priority, nil)).to eq("test_test") end it "should retain datatypes found in yaml files" do Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"]).times(3) @cache.value = "---\nstringval: 'string'\nboolval: true\nnumericval: 1" expect(@backend.lookup("stringval", {}, nil, :priority, nil)).to eq("string") expect(@backend.lookup("boolval", {}, nil, :priority, nil)).to eq(true) expect(@backend.lookup("numericval", {}, nil, :priority, nil)).to eq(1) end end end end end hiera-3.2.0/spec/unit/backend_spec.rb000066400000000000000000001050501271216112200174370ustar00rootroot00000000000000require 'spec_helper' require 'hiera/util' class Hiera module Backend class Backend1x_backend def lookup(key, scope, order_override, resolution_type) ["a", "b"] end end end describe Backend do describe "loading non existing backend" do it "fails if a backend cannot be loaded" do Config.load({:datadir => "/tmp/%{interpolate}", :backends => ['bogus']}) expect do Config.load_backends end.to raise_error(/Cannot load backend bogus/) end end describe "#datadir" do it "interpolates any values in the configured value" do Config.load({:rspec => {:datadir => "/tmp/%{interpolate}"}}) dir = Backend.datadir(:rspec, { "interpolate" => "my_data" }) expect(dir).to eq("/tmp/my_data") end it "defaults to a directory in var" do Config.load({}) expect(Backend.datadir(:rspec, { "environment" => "foo" })).to eq(Hiera::Util.var_dir % { :environment => "foo"}) Config.load({:rspec => nil}) expect(Backend.datadir(:rspec, { "environment" => "foo" })).to eq(Hiera::Util.var_dir % { :environment => "foo"}) Config.load({:rspec => {}}) expect(Backend.datadir(:rspec, { "environment" => "foo" })).to eq(Hiera::Util.var_dir % { :environment => "foo"}) end it "fails when the datadir is an array" do Config.load({:rspec => {:datadir => []}}) expect do Backend.datadir(:rspec, {}) end.to raise_error(Hiera::InvalidConfigurationError, /datadir for rspec cannot be an array/) end end describe "#datafile" do it "translates a non-existant datafile into nil" do Hiera.expects(:debug).with("Cannot find datafile /nonexisting/test.yaml, skipping") Backend.expects(:datadir).returns("/nonexisting") expect(Backend.datafile(:yaml, {}, "test", "yaml")).to eq(nil) end it "concatenates the datadir and datafile and format to produce the full datafile filename" do Backend.expects(:datadir).returns("/nonexisting") File.expects(:exist?).with("/nonexisting/test.yaml").returns(true) expect(Backend.datafile(:yaml, {}, "test", "yaml")).to eq("/nonexisting/test.yaml") end end describe "#datasources" do it "iterates over the datasources in the order of the given hierarchy" do expected = ["one", "two"] Backend.datasources({}, nil, ["one", "two"]) do |backend| expect(backend).to eq(expected.delete_at(0)) end expect(expected.empty?).to eq(true) end it "uses the configured hierarchy no specific hierarchy is given" do Config.load(:hierarchy => "test") Backend.datasources({}) do |backend| expect(backend).to eq("test") end end it "defaults to a hierarchy of only 'common' if not configured or given" do Config.load({}) Backend.datasources({}) do |backend| expect(backend).to eq("common") end end it "prefixes the hierarchy with the override if an override is provided" do Config.load({}) expected = ["override", "common"] Backend.datasources({}, "override") do |backend| expect(backend).to eq(expected.delete_at(0)) end expect(expected.empty?).to eq(true) end it "parses the names of the hierarchy levels using the given scope" do Backend.expects(:interpolate_config).with('nodes/%{::trusted.certname}', {:rspec => :tests}, nil) Backend.expects(:interpolate_config).with('common', {:rspec => :tests}, nil) Backend.datasources({:rspec => :tests}) { } end it "defaults to 'common' if the hierarchy contains no hierarchies with non-empty names" do Config.load({}) expected = ["common"] Backend.datasources({}, "%{rspec}") do |backend| expect(backend).to eq(expected.delete_at(0)) end expect(expected.empty?).to eq(true) end end describe "#parse_string" do it "passes nil through untouched" do expect(Backend.parse_string(nil, {})).to eq(nil) end it "does not modify the input data" do data = "%{value}" Backend.parse_string(data, { "value" => "replacement" }) expect(data).to eq("%{value}") end it "passes non-string data through untouched" do input = { "not a" => "string" } expect(Backend.parse_string(input, {})).to eq(input) end @scope_interpolation_tests = { "replace %{part1} and %{part2}" => "replace value of part1 and value of part2", "replace %{scope('part1')} and %{scope('part2')}" => "replace value of part1 and value of part2" } @scope_interpolation_tests.each do |input, expected| it "replaces interpolations with data looked up in the scope" do scope = {"part1" => "value of part1", "part2" => "value of part2"} expect(Backend.parse_string(input, scope)).to eq(expected) end end it "replaces interpolations with data looked up in extra_data when scope does not contain the value" do input = "test_%{rspec}_test" expect(Backend.parse_string(input, {}, {"rspec" => "extra"})).to eq("test_extra_test") end it "prefers data from scope over data from extra_data" do input = "test_%{rspec}_test" expect(Backend.parse_string(input, {"rspec" => "test"}, {"rspec" => "fail"})).to eq("test_test_test") end @interprets_nil_in_scope_tests = { "test_%{rspec}_test" => "test__test", "test_%{scope('rspec')}_test" => "test__test" } @interprets_nil_in_scope_tests.each do |input, expected| it "interprets nil in scope as a non-value" do expect(Backend.parse_string(input, {"rspec" => nil})).to eq(expected) end end @interprets_false_in_scope_tests = { "test_%{rspec}_test" => "test_false_test", "test_%{scope('rspec')}_test" => "test_false_test" } @interprets_false_in_scope_tests.each do |input, expected| it "interprets false in scope as a real value" do input = "test_%{scope('rspec')}_test" expect(Backend.parse_string(input, {"rspec" => false})).to eq(expected) end end it "interprets false in extra_data as a real value" do input = "test_%{rspec}_test" expect(Backend.parse_string(input, {}, {"rspec" => false})).to eq("test_false_test") end it "interprets nil in extra_data as a non-value" do input = "test_%{rspec}_test" expect(Backend.parse_string(input, {}, {"rspec" => nil})).to eq("test__test") end @interprets_undefined_in_scope_tests = { "test_%{rspec}_test" => "test__test", "test_%{scope('rspec')}_test" => "test__test" } @exact_lookup_tests = { "test_%{::rspec::data}_test" => "test_value_test", "test_%{scope('::rspec::data')}_test" => "test_value_test" } @exact_lookup_tests.each do |input, expected| it "looks up the interpolated value exactly as it appears in the input" do expect(Backend.parse_string(input, {"::rspec::data" => "value"})).to eq(expected) end end @surrounding_whitespace_tests = { "test_%{\trspec::data }_test" => "test_value_test", "test_%{scope('\trspec::data ')}_test" => "test_value_test" } @surrounding_whitespace_tests.each do |input, expected| it "does not remove any surrounding whitespace when parsing the key to lookup" do expect(Backend.parse_string(input, {"\trspec::data " => "value"})).to eq(expected) end end @leading_double_colon_tests = { "test_%{::rspec::data}_test" => "test__test", "test_%{scope('::rspec::data')}_test" => "test__test" } @leading_double_colon_tests.each do |input, expected| it "does not try removing leading :: when a full lookup fails (#17434)" do expect(Backend.parse_string(input, {"rspec::data" => "value"})).to eq(expected) end end @double_colon_key_tests = { "test_%{::rspec::data}_test" => "test__test", "test_%{scope('::rspec::data')}_test" => "test__test" } @double_colon_key_tests.each do |input, expected| it "does not try removing leading sections separated by :: when a full lookup fails (#17434)" do expect(Backend.parse_string(input, {"data" => "value"})).to eq(expected) end end it "does not try removing unknown, preceeding characters when looking up values" do input = "test_%{$var}_test" expect(Backend.parse_string(input, {"$var" => "value"})).to eq("test_value_test") end it "looks up recursively" do scope = {"rspec" => "%{first}", "first" => "%{last}", "last" => "final"} input = "test_%{rspec}_test" expect(Backend.parse_string(input, scope)).to eq("test_final_test") end it "raises an error if the recursive lookup results in an infinite loop" do scope = {"first" => "%{second}", "second" => "%{first}"} input = "test_%{first}_test" expect do Backend.parse_string(input, scope) end.to raise_error Hiera::InterpolationLoop, "Lookup recursion detected in [first, second]" end it "replaces repeated occurances of the same lookup" do scope = {"rspec" => "value"} input = "it replaces %{rspec} and %{rspec}" expect(Backend.parse_string(input, scope)).to eq("it replaces value and value") end it "replaces hiera interpolations with data looked up in hiera" do input = "%{hiera('key1')}" scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("key1", scope, nil, :priority, instance_of(Hash)).returns("answer") expect(Backend.parse_string(input, scope)).to eq("answer") end it "interpolation passes the order_override back into the backend" do Backend.expects(:lookup).with("lookup::key", nil, {}, "order_override_datasource", :priority, instance_of(Hash)) Backend.parse_string("%{hiera('lookup::key')}", {}, {}, {:order_override => "order_override_datasource"}) end it "replaces literal interpolations with their argument" do scope = {} input = "%{literal('%')}{rspec::data}" expect(Backend.parse_string(input, scope)).to eq("%{rspec::data}") end end describe "#parse_answer" do it "interpolates values in strings" do input = "test_%{rspec}_test" expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq("test_test_test") end it "interpolates each string in an array" do input = ["test_%{rspec}_test", "test_%{rspec}_test", ["test_%{rspec}_test"]] expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq(["test_test_test", "test_test_test", ["test_test_test"]]) end it "interpolates each string in a hash" do input = {"foo" => "test_%{rspec}_test", "bar" => "test_%{rspec}_test"} expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq({"foo"=>"test_test_test", "bar"=>"test_test_test"}) end it "interpolates string in hash keys" do input = {"%{rspec}" => "test"} expect(Backend.parse_answer(input, {"rspec" => "foo"})).to eq({"foo"=>"test"}) end it "interpolates strings in nested hash keys" do input = {"topkey" => {"%{rspec}" => "test"}} expect(Backend.parse_answer(input, {"rspec" => "foo"})).to eq({"topkey"=>{"foo" => "test"}}) end it "interpolates strings in a mixed structure of arrays and hashes" do input = {"foo" => "test_%{rspec}_test", "bar" => ["test_%{rspec}_test", "test_%{rspec}_test"]} expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq({"foo"=>"test_test_test", "bar"=>["test_test_test", "test_test_test"]}) end it "interpolates hiera lookups values in strings" do input = "test_%{hiera('rspec')}_test" scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test") expect(Backend.parse_answer(input, scope)).to eq("test_test_test") end it "interpolates alias lookups with non-string types" do input = "%{alias('rspec')}" scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns(['test', 'test']) expect(Backend.parse_answer(input, scope)).to eq(['test', 'test']) end it 'fails if alias interpolation is attempted in a string context with a prefix' do input = "stuff_before%{alias('rspec')}" scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns(['test', 'test']) expect do expect(Backend.parse_answer(input, scope)).to eq(['test', 'test']) end.to raise_error(Hiera::InterpolationInvalidValue, 'Cannot call alias in the string context') end it 'fails if alias interpolation is attempted in a string context with a postfix' do input = "%{alias('rspec')}_stiff after" scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns(['test', 'test']) expect do expect(Backend.parse_answer(input, scope)).to eq(['test', 'test']) end.to raise_error(Hiera::InterpolationInvalidValue, 'Cannot call alias in the string context') end it "interpolates hiera lookups in each string in an array" do input = ["test_%{hiera('rspec')}_test", "test_%{hiera('rspec')}_test", ["test_%{hiera('rspec')}_test"]] scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test") expect(Backend.parse_answer(input, scope)).to eq(["test_test_test", "test_test_test", ["test_test_test"]]) end it "interpolates hiera lookups in each string in a hash" do input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => "test_%{hiera('rspec')}_test"} scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test") expect(Backend.parse_answer(input, scope)).to eq({"foo"=>"test_test_test", "bar"=>"test_test_test"}) end it "interpolates hiera lookups in string in hash keys" do input = {"%{hiera('rspec')}" => "test"} scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("foo") expect(Backend.parse_answer(input, scope)).to eq({"foo"=>"test"}) end it "interpolates hiera lookups in strings in nested hash keys" do input = {"topkey" => {"%{hiera('rspec')}" => "test"}} scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("foo") expect(Backend.parse_answer(input, scope)).to eq({"topkey"=>{"foo" => "test"}}) end it "interpolates hiera lookups in strings in a mixed structure of arrays and hashes" do input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => ["test_%{hiera('rspec')}_test", "test_%{hiera('rspec')}_test"]} scope = {} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test") expect(Backend.parse_answer(input, scope)).to eq({"foo"=>"test_test_test", "bar"=>["test_test_test", "test_test_test"]}) end it "interpolates hiera lookups and scope lookups in the same string" do input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => "test_%{rspec2}_test"} scope = {"rspec2" => "scope_rspec"} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("hiera_rspec") expect(Backend.parse_answer(input, scope)).to eq({"foo"=>"test_hiera_rspec_test", "bar"=>"test_scope_rspec_test"}) end it "interpolates hiera and scope lookups with the same lookup query in a single string" do input = "test_%{hiera('rspec')}_test_%{rspec}" scope = {"rspec" => "scope_rspec"} Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("hiera_rspec") expect(Backend.parse_answer(input, scope)).to eq("test_hiera_rspec_test_scope_rspec") end it "passes integers unchanged" do input = 1 expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq(1) end it "passes floats unchanged" do input = 0.233 expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq(0.233) end it "passes the boolean true unchanged" do input = true expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq(true) end it "passes the boolean false unchanged" do input = false expect(Backend.parse_answer(input, {"rspec" => "test"})).to eq(false) end it "interpolates lookups using single or double quotes" do input = "test_%{scope(\"rspec\")}_test_%{scope('rspec')}" scope = {"rspec" => "scope_rspec"} expect(Backend.parse_answer(input, scope)).to eq("test_scope_rspec_test_scope_rspec") end end describe "#resolve_answer" do it "flattens and removes duplicate values from arrays during an array lookup" do expect(Backend.resolve_answer(["foo", ["foo", "foo"], "bar"], :array)).to eq(["foo", "bar"]) end it "returns the data unchanged during a priority lookup" do expect(Backend.resolve_answer(["foo", ["foo", "foo"], "bar"], :priority)).to eq(["foo", ["foo", "foo"], "bar"]) end end describe "#lookup" do before do Hiera.stubs(:debug) Hiera.stubs(:warn) end it "caches loaded backends" do Backend.clear! Hiera.expects(:debug).with(regexp_matches(/Hiera YAML backend starting/)).once Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend.lookup("key", "default", {}, nil, nil) Backend.lookup("key", "default", {}, nil, nil) end it "returns the answer from the backend" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, nil, instance_of(Hash)).returns("answer") expect(Backend.lookup("key", "default", {}, nil, nil)).to eq("answer") end it "retains the datatypes as returned by the backend" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("stringval", {}, nil, nil, instance_of(Hash)).returns("string") Backend::Yaml_backend.any_instance.expects(:lookup).with("boolval", {}, nil, nil, instance_of(Hash)).returns(false) Backend::Yaml_backend.any_instance.expects(:lookup).with("numericval", {}, nil, nil, instance_of(Hash)).returns(1) expect(Backend.lookup("stringval", "default", {}, nil, nil)).to eq("string") expect(Backend.lookup("boolval", "default", {}, nil, nil)).to eq(false) expect(Backend.lookup("numericval", "default", {}, nil, nil)).to eq(1) end it "calls to all backends till an answer is found" do backend = mock backend.expects(:lookup).returns("answer") Config.load({}) Config.instance_variable_set("@config", {:backends => ["yaml", "rspec"]}) Backend.instance_variable_set("@backends", {"rspec" => backend}) #Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, nil) Backend.expects(:constants).returns(["Yaml_backend", "Rspec_backend"]).twice expect(Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, nil)).to eq("answer") end it "calls to all backends till an answer is found when doing array lookups" do backend = mock backend.expects(:lookup).returns(["answer"]) Config.load({}) Config.instance_variable_set("@config", {:backends => ["yaml", "rspec"]}) Backend.instance_variable_set("@backends", {"rspec" => backend}) Backend.expects(:constants).returns(["Yaml_backend", "Rspec_backend"]).twice expect(Backend.lookup("key", "notfound", {"rspec" => "test"}, nil, :array)).to eq(["answer"]) end it "calls to all backends till an answer is found when doing hash lookups" do thehash = {:answer => "value"} backend = mock backend.expects(:lookup).returns(thehash) Config.load({}) Config.instance_variable_set("@config", {:backends => ["yaml", "rspec"]}) Backend.instance_variable_set("@backends", {"rspec" => backend}) Backend.expects(:constants).returns(["Yaml_backend", "Rspec_backend"]).twice expect(Backend.lookup("key", "notfound", {"rspec" => "test"}, nil, :hash)).to eq(thehash) end it "builds a merged hash from all backends for hash searches" do backend1 = mock :lookup => {"a" => "answer"} backend2 = mock :lookup => {"b" => "bnswer"} Config.load({}) Config.instance_variable_set("@config", {:backends => ["first", "second"]}) Backend.instance_variable_set("@backends", {"first" => backend1, "second" => backend2}) Backend.stubs(:constants).returns(["First_backend", "Second_backend"]) expect(Backend.lookup("key", {}, {"rspec" => "test"}, nil, :hash)).to eq({"a" => "answer", "b" => "bnswer"}) end it "builds an array from all backends for array searches" do backend1 = mock :lookup => ["a", "b"] backend2 = mock :lookup => ["c", "d"] Config.load({}) Config.instance_variable_set("@config", {:backends => ["first", "second"]}) Backend.instance_variable_set("@backends", {"first" => backend1, "second" => backend2}) Backend.stubs(:constants).returns(["First_backend", "Second_backend"]) expect(Backend.lookup("key", {}, {"rspec" => "test"}, nil, :array)).to eq(["a", "b", "c", "d"]) end it "uses the earliest backend result for priority searches" do backend1 = mock backend1.stubs(:lookup).returns(["a", "b"]) backend2 = mock backend2.stubs(:lookup).returns(["c", "d"]) Config.load({}) Config.instance_variable_set("@config", {:backends => ["first", "second"]}) Backend.instance_variable_set("@backends", {"first" => backend1, "second" => backend2}) Backend.stubs(:constants).returns(["First_backend", "Second_backend"]) expect(Backend.lookup("key", {}, {"rspec" => "test"}, nil, :priority)).to eq(["a", "b"]) end it "parses the answers based on resolution_type" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend.expects(:resolve_answer).with("test_test", :priority).returns("parsed") Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, :priority, instance_of(Hash)).returns("test_test") expect(Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, :priority)).to eq("parsed") end it "returns the default with variables parsed if nothing is found" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, nil, instance_of(Hash)).throws(:no_such_key) expect(Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, nil)).to eq("test_test") end it "returns nil instead of the default when key is found with a nil value" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, nil, instance_of(Hash)).returns(nil) expect(Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, nil)).to eq(nil) end it "keeps string default data as a string" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, nil, instance_of(Hash)).throws(:no_such_key) expect(Backend.lookup("key", "test", {}, nil, nil)).to eq("test") end it "keeps array default data as an array" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, :array, instance_of(Hash)).throws(:no_such_key) expect(Backend.lookup("key", ["test"], {}, nil, :array)).to eq(["test"]) end it "keeps hash default data as a hash" do Config.load({:yaml => {:datadir => "/tmp"}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, :hash, instance_of(Hash)).throws(:no_such_key) expect(Backend.lookup("key", {"test" => "value"}, {}, nil, :hash)).to eq({"test" => "value"}) end it 'can use qualified key to lookup value in hash' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns({ 'test' => 'value'}) expect(Backend.lookup('key.test', 'dflt', {}, nil, nil)).to eq('value') end it 'can use qualified key to lookup value in array' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns([ 'first', 'second']) expect(Backend.lookup('key.1', 'dflt', {}, nil, nil)).to eq('second') end it 'will fail when qualified key is partially found but not expected hash' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns(['value 1', 'value 2']) expect do Backend.lookup('key.test', 'dflt', {}, nil, nil) end.to raise_error(Exception, /^Hiera type mismatch:/) end it 'will fail when qualified key used with resolution_type :hash' do expect do Backend.lookup('key.test', 'dflt', {}, nil, :hash) end.to raise_error(ArgumentError, /^Resolution type :hash is illegal/) end it 'will fail when qualified key used with resolution_type :array' do expect do Backend.lookup('key.test', 'dflt', {}, nil, :array) end.to raise_error(ArgumentError, /^Resolution type :array is illegal/) end it 'will succeed when qualified key used with resolution_type :priority' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, :priority, instance_of(Hash)).returns({ 'test' => 'value'}) expect(Backend.lookup('key.test', 'dflt', {}, nil, :priority)).to eq('value') end it 'will fail when qualified key is partially found but not expected array' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns({ 'test' => 'value'}) expect do Backend.lookup('key.2', 'dflt', {}, nil, nil) end.to raise_error(Exception, /^Hiera type mismatch:/) end it 'will not fail when qualified key is partially not found' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns(nil) expect(Backend.lookup('key.test', 'dflt', {}, nil, nil)).to eq('dflt') end it 'will not fail when qualified key is array index out of bounds' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns(['value 1', 'value 2']) expect(Backend.lookup('key.33', 'dflt', {}, nil, nil)).to eq('dflt') end it 'will fail when dotted key access is made using a numeric index and value is not array' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns( {'one' => 'value 1', 'two' => 'value 2'}) expect {Backend.lookup('key.33', 'dflt', {}, nil, nil)}.to raise_error(Exception, /Got Hash when Array was expected to access value using '33' from key 'key.33'/) end it 'will fail when dotted key access is made using a string and value is not hash' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns( ['value 1', 'value 2']) expect {Backend.lookup('key.one', 'dflt', {}, nil, nil)}.to raise_error(Exception, /Got Array when a hash-like object was expected to access value using 'one' from key 'key.one'/) end it 'will fail when dotted key access is made using resolution type :hash' do expect {Backend.lookup('key.one', 'dflt', {}, nil, :hash)}.to raise_error(Exception, /Resolution type :hash is illegal when accessing values using dotted keys. Offending key was 'key.one'/) end it 'will fail when dotted key access is made using resolution type :array' do expect {Backend.lookup('key.one', 'dflt', {}, nil, :array)}.to raise_error(Exception, /Resolution type :array is illegal when accessing values using dotted keys. Offending key was 'key.one'/) end it 'can use qualified key in interpolation to lookup value in hash' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends Hiera::Backend.stubs(:datasourcefiles).yields('foo', 'bar') Hiera::Filecache.any_instance.expects(:read_file).at_most(2).returns({'key' => '%{hiera(\'some.subkey\')}', 'some' => { 'subkey' => 'value' }}) expect(Backend.lookup('key', 'dflt', {}, nil, nil)).to eq('value') end it 'can use qualified key in interpolated default and scope' do Config.load({:yaml => {:datadir => '/tmp'}}) Config.load_backends scope = { 'some' => { 'test' => 'value'}} Backend::Yaml_backend.any_instance.expects(:lookup).with('key', scope, nil, nil, instance_of(Hash)) expect(Backend.lookup('key.notfound', '%{some.test}', scope, nil, nil)).to eq('value') end it "handles older backend with 4 argument lookup" do Config.load({}) Config.instance_variable_set("@config", {:backends => ["Backend1x"]}) Hiera.expects(:debug).at_least_once.with(regexp_matches /Using Hiera 1.x backend/) expect(Backend.lookup("key", {}, {"rspec" => "test"}, nil, :priority)).to eq(["a", "b"]) end end describe '#merge_answer' do before do Hiera.stubs(:debug) Hiera.stubs(:warn) Config.stubs(:validate!) end it "uses Hash.merge when configured with :merge_behavior => :native" do Config.load({:merge_behavior => :native}) Hash.any_instance.expects(:merge).with({"b" => "bnswer"}).returns({"a" => "answer", "b" => "bnswer"}) expect(Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"})).to eq({"a" => "answer", "b" => "bnswer"}) end it "uses deep_merge! when configured with :merge_behavior => :deeper" do Config.load({:merge_behavior => :deeper}) Hash.any_instance.expects('deeper_merge!').with({"b" => "bnswer"}, {}).returns({"a" => "answer", "b" => "bnswer"}) expect(Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"})).to eq({"a" => "answer", "b" => "bnswer"}) end it "uses deep_merge when configured with :merge_behavior => :deep" do Config.load({:merge_behavior => :deep}) Hash.any_instance.expects('deeper_merge').with({"b" => "bnswer"}, {}).returns({"a" => "answer", "b" => "bnswer"}) expect(Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"})).to eq({"a" => "answer", "b" => "bnswer"}) end it "disregards configuration when 'merge' parameter is given as a Hash" do Config.load({:merge_behavior => :deep}) Hash.any_instance.expects('deeper_merge!').with({"b" => "bnswer"}, {}).returns({"a" => "answer", "b" => "bnswer"}) expect(Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}, {:behavior => 'deeper' })).to eq({"a" => "answer", "b" => "bnswer"}) end it "propagates deep merge options when given Hash 'merge' parameter" do Hash.any_instance.expects('deeper_merge!').with({"b" => "bnswer"}, { :knockout_prefix => '-' }).returns({"a" => "answer", "b" => "bnswer"}) expect(Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}, {:behavior => 'deeper', :knockout_prefix => '-'})).to eq({"a" => "answer", "b" => "bnswer"}) end it "passes Config[:deep_merge_options] into calls to deep_merge" do Config.load({:merge_behavior => :deep, :deep_merge_options => { :knockout_prefix => '-' } }) Hash.any_instance.expects('deeper_merge').with({"b" => "bnswer"}, {:knockout_prefix => '-'}).returns({"a" => "answer", "b" => "bnswer"}) expect(Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"})).to eq({"a" => "answer", "b" => "bnswer"}) end end end end hiera-3.2.0/spec/unit/config_spec.rb000066400000000000000000000077151271216112200173260ustar00rootroot00000000000000require 'spec_helper' class Hiera describe Config do describe "#load" do let(:default_config) do { :backends => ["yaml"], :hierarchy => ['nodes/%{::trusted.certname}', 'common'], :logger => "console", :merge_behavior=>:native } end it "should raise an error for missing config files" do File.expects(:exist?).with("/nonexisting").returns(false) YAML.expects(:load_file).with("/nonexisting").never expect { Config.load("/nonexisting") }.to raise_error "Config file /nonexisting not found" end it "should attempt to YAML load config files" do File.expects(:exist?).with("/nonexisting").returns(true) YAML.expects(:load_file).with("/nonexisting").returns(YAML.load("---\n")) Config.load("/nonexisting") end it "should use defaults on empty YAML config file" do File.expects(:exist?).with("/nonexisting").returns(true) YAML.expects(:load_file).with("/nonexisting").returns(YAML.load("")) expect(Config.load("/nonexisting")).to eq(default_config) end it "should use hash data as source if supplied" do config = Config.load({"rspec" => "test"}) expect(config["rspec"]).to eq("test") end it "should merge defaults with the loaded or supplied config" do config = Config.load({}) expect(config).to eq({:backends => ["yaml"], :hierarchy => ['nodes/%{::trusted.certname}', 'common'], :logger => "console", :merge_behavior=>:native}) end it "should force :backends to be a flattened array" do expect(Config.load({:backends => [["foo", ["bar"]]]})).to eq({:backends => ["foo", "bar"], :hierarchy => ['nodes/%{::trusted.certname}', 'common'], :logger => "console", :merge_behavior=>:native}) end it "should load the supplied logger" do Hiera.expects(:logger=).with("foo") Config.load({:logger => "foo"}) end it "should default to the console logger" do Hiera.expects(:logger=).with("console") Config.load({}) end context "loading '/dev/null' as spec tests do", :unless => Hiera::Util.microsoft_windows? do before :each do # Simulate the behavior of YAML.load_file('/dev/null') in MRI 1.9.3p194 Config.stubs(:yaml_load_file). raises(TypeError, "no implicit conversion from nil to integer") end it "is not exceptional behavior" do Config.load('/dev/null') end end describe "if deep_merge can't be loaded" do let(:error_message) { "Must have 'deep_merge' gem installed for the configured merge_behavior." } before(:each) do Config.expects(:require).with("deep_merge").raises(LoadError, "unable to load") end it "should error if merge_behavior is 'deep'" do expect { Config.load(:merge_behavior => :deep) }.to raise_error(Hiera::Error, error_message) end it "should error if merge_behavior is 'deeper'" do expect { Config.load(:merge_behavior => :deeper) }.to raise_error(Hiera::Error, error_message) end end end describe "#load_backends" do it "should load each backend" do Config.load(:backends => ["One", "Two"]) Config.expects(:require).with("hiera/backend/one_backend") Config.expects(:require).with("hiera/backend/two_backend") Config.load_backends end it "should warn if it cant load a backend" do Config.load(:backends => ["one"]) Config.expects(:require).with("hiera/backend/one_backend").raises("fail") expect { Config.load_backends }.to raise_error("fail") end end describe "#include?" do it "should correctly report inclusion" do Config.load({}) expect(Config.include?(:foo)).to eq(false) expect(Config.include?(:logger)).to eq(true) end end end end hiera-3.2.0/spec/unit/console_logger_spec.rb000066400000000000000000000007021271216112200210470ustar00rootroot00000000000000require 'spec_helper' class Hiera describe Console_logger do describe "#warn" do it "should warn to STDERR" do STDERR.expects(:puts).with("WARN: 0: foo") Time.expects(:now).returns(0) Console_logger.warn("foo") end it "should debug to STDERR" do STDERR.expects(:puts).with("DEBUG: 0: foo") Time.expects(:now).returns(0) Console_logger.debug("foo") end end end end hiera-3.2.0/spec/unit/fallback_logger_spec.rb000066400000000000000000000036411271216112200211510ustar00rootroot00000000000000require 'hiera/fallback_logger' describe Hiera::FallbackLogger do before :each do InMemoryLogger.reset SuitableLogger.reset end it "delegates #warn to the logger implemenation" do logger = Hiera::FallbackLogger.new(InMemoryLogger) logger.warn("the message") expect(InMemoryLogger.warnings).to eq(["the message"]) end it "delegates #debug to the logger implemenation" do logger = Hiera::FallbackLogger.new(InMemoryLogger) logger.debug("the message") expect(InMemoryLogger.debugs).to eq(["the message"]) end it "chooses the first logger that is suitable" do logger = Hiera::FallbackLogger.new(UnsuitableLogger, SuitableLogger) logger.warn("for the suitable logger") expect(SuitableLogger.warnings).to include("for the suitable logger") end it "raises an error if no implementation is suitable" do expect do Hiera::FallbackLogger.new(UnsuitableLogger) end.to raise_error "No suitable logging implementation found." end it "issues a warning for each implementation that is not suitable" do Hiera::FallbackLogger.new(UnsuitableLogger, UnsuitableLogger, SuitableLogger) expect(SuitableLogger.warnings).to eq([ "Not using UnsuitableLogger. It does not report itself to be suitable.", "Not using UnsuitableLogger. It does not report itself to be suitable."]) end # Preserves log messages in memory # and also serves as a "legacy" logger that has no # suitable? method class InMemoryLogger class << self attr_accessor :warnings, :debugs end def self.reset self.warnings = [] self.debugs = [] end def self.warn(message) self.warnings << message end def self.debug(message) self.debugs << message end end class UnsuitableLogger def self.suitable? false end end class SuitableLogger < InMemoryLogger def self.suitable? true end end end hiera-3.2.0/spec/unit/filecache_spec.rb000066400000000000000000000077271271216112200177670ustar00rootroot00000000000000require 'spec_helper' require 'tmpdir' class Hiera describe Filecache do before do @cache = Filecache.new end def write_file(file, contents) File.open(file, 'w') do |f| f.write(contents) end end describe "#read" do it "reads data from a file" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect(@cache.read(file)).to eq("my data") end end it "rereads data when the file changes" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect(@cache.read(file)).to eq("my data") write_file(file, "changed data") expect(@cache.read(file)).to eq("changed data") end end it "uses the provided default when the type does not match the expected type" do Hiera.expects(:debug).with(regexp_matches(/String.*not.*Hash, setting defaults/)) Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") data = @cache.read(file, Hash, { :testing => "hash" }) do |data| "a string" end expect(data).to eq({ :testing => "hash" }) end end it "traps any errors from the block and uses the default value" do Hiera.expects(:debug).with(regexp_matches(/Reading data.*failed:.*testing error/)) Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") data = @cache.read(file, Hash, { :testing => "hash" }) do |data| raise ArgumentError, "testing error" end expect(data).to eq({ :testing => "hash" }) end end it "raises an error when there is no default given and there is a problem" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect do @cache.read(file, Hash) do |data| raise ArgumentError, "testing error" end end.to raise_error(ArgumentError, "testing error") end end end describe "#read_file" do it "reads data from a file" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect(@cache.read_file(file)).to eq("my data") end end it "rereads data when the file changes" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect(@cache.read_file(file)).to eq("my data") write_file(file, "changed data") expect(@cache.read_file(file)).to eq("changed data") end end it "errors when the type does not match the expected type" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect do @cache.read_file(file, Hash) do |data| "a string" end end.to raise_error(TypeError) end end it "converts the read data using the block" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect(@cache.read_file(file, Hash) do |data| { :data => data } end).to eq({ :data => "my data" }) end end it "errors when the file does not exist" do expect do @cache.read_file("/notexist") end.to raise_error(Errno::ENOENT) end it "propogates any errors from the block" do Dir.mktmpdir do |dir| file = File.join(dir, "testing") write_file(file, "my data") expect do @cache.read_file(file) do |data| raise ArgumentError, "testing error" end end.to raise_error(ArgumentError, "testing error") end end end end end hiera-3.2.0/spec/unit/fixtures/000077500000000000000000000000001271216112200163615ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/interpolate/000077500000000000000000000000001271216112200207075ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/interpolate/config/000077500000000000000000000000001271216112200221545ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/interpolate/config/hiera.yaml000066400000000000000000000002341271216112200241270ustar00rootroot00000000000000:backends: - yaml :hierarchy: - recursive - niltest - complex - empty_%{}inter%{::}polation - dotted_keys - weird_keys - bad_interpolation hiera-3.2.0/spec/unit/fixtures/interpolate/config/hiera_iplm_hiera.yaml000066400000000000000000000001011271216112200263110ustar00rootroot00000000000000:backends: - json :hierarchy: - role - "%{hiera('role')}" hiera-3.2.0/spec/unit/fixtures/interpolate/config/hiera_iplm_hiera_bad.yaml000066400000000000000000000000701271216112200271240ustar00rootroot00000000000000:backends: - yaml :hierarchy: - "%{hiera('role')}" hiera-3.2.0/spec/unit/fixtures/interpolate/data/000077500000000000000000000000001271216112200216205ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/interpolate/data/bad_interpolation.yaml000066400000000000000000000010401271216112200261740ustar00rootroot00000000000000--- quote_mismatch: 'Key delimited with singe quote on one side aand double qoute on the other: %{''the.key"}' quote_mismatch_arg: 'Arg delimited with singe quote on one side and double qoute on the other: %{hiera(''the.key")}' non_existing_method: 'The method flubber does not exist: %{flubber("hello")}' one_quote: 'Key with only one quote: %{the.''key}' empty_segment: 'Key with only one quote: %{the..key}' empty_quoted_segment: 'Key with only one quote: %{the.''''.key}' partly_quoted_segment: 'Key with only one quote: %{the.''pa''key}' hiera-3.2.0/spec/unit/fixtures/interpolate/data/complex.yaml000066400000000000000000000001651271216112200241550ustar00rootroot00000000000000--- root: a: aa: "%{alias('aaa')}" aaa: b: bb: "%{alias('bbb')}" bbb: [ "%{alias('ccc')}" ] ccc: text hiera-3.2.0/spec/unit/fixtures/interpolate/data/dotted_keys.yaml000066400000000000000000000021551271216112200250250ustar00rootroot00000000000000--- a.b: '(hiera) a dot b' a.c.scope: "a dot c: %{'a.b'}" a.c.hiera: 'a dot c: %{hiera("''a.b''")}' a.c.scope: "a dot c: %{'a.b'}" a.c.hiera: 'a dot c: %{hiera("''a.b''")}' a.c.alias: '%{alias("''a.b''")}' a: d: '(hiera) a dot d is a hash entry' d.x: '(hiera) a dot d.x is a hash entry' d.z: g: '(hiera) a dot d.z dot g is a hash entry' a.x: d: '(hiera) a.x dot d is a hash entry' d.x: '(hiera) a.x dot d.x is a hash entry' d.z: g: '(hiera) a.x dot d.z dot g is a hash entry' a.e.scope: "a dot e: %{a.d}" a.e.hiera: "a dot e: %{hiera('a.d')}" a.ex.scope: "a dot ex: %{a.'d.x'}" a.ex.hiera: 'a dot ex: %{hiera("a.''d.x''")}' a.xe.scope: "a dot xe: %{'a.x'.d}" a.xe.hiera: 'a dot xe: %{hiera("''a.x''.d")}' a.xm.scope: "a dot xm: %{a.'d.z'.g}" a.xm.hiera: 'a dot xm: %{hiera("a.''d.z''.g")}' a.xx.scope: "a dot xx: %{'a.x'.'d.z'.g}" a.xx.hiera: 'a dot xx: %{hiera("''a.x''.''d.z''.g")}' a.f.scope: "a dot f: %{'a.d'}" a.f.hiera: 'a dot f: %{hiera("''a.d''")}' x.1: '(hiera) x dot 1' x.2.scope: "x dot 2: %{'x.1'}" x.2.hiera: 'x dot 2: %{hiera("''x.1''")}' key: subkey ipl_key: '- %{hiera("key.subkey")} -' hiera-3.2.0/spec/unit/fixtures/interpolate/data/empty_interpolation.yaml000066400000000000000000000006521271216112200266140ustar00rootroot00000000000000empty_interpolation: 'clown%{}shoe' escaped_empty_interpolation: 'clown%%{}{shoe}s' only_empty_interpolation: '%{}' empty_namespace: '%{::}' whitespace1: '%{ :: }' whitespace2: '%{ }' quoted_empty_interpolation: 'clown%{""}shoe' quoted_escaped_empty_interpolation: 'clown%%{""}{shoe}s' quoted_only_empty_interpolation: '%{""}' quoted_empty_namespace: '%{"::"}' quoted_whitespace1: '%{ "::" }' quoted_whitespace2: '%{ "" }' hiera-3.2.0/spec/unit/fixtures/interpolate/data/frontend.json000066400000000000000000000000201271216112200243220ustar00rootroot00000000000000{ "foo": "Foo" }hiera-3.2.0/spec/unit/fixtures/interpolate/data/niltest.yaml000066400000000000000000000001331271216112200241630ustar00rootroot00000000000000niltest: 'Missing key #%{hiera("knotfound")}#. Key with nil #%{hiera("knil")}#' knil: null hiera-3.2.0/spec/unit/fixtures/interpolate/data/recursive.yaml000066400000000000000000000000571271216112200245150ustar00rootroot00000000000000foo: '%{hiera("bar")}' bar: '%{hiera("foo")}' hiera-3.2.0/spec/unit/fixtures/interpolate/data/role.json000066400000000000000000000000261271216112200234520ustar00rootroot00000000000000{ "role": "frontend" }hiera-3.2.0/spec/unit/fixtures/interpolate/data/weird_keys.yaml000066400000000000000000000003531271216112200246520ustar00rootroot00000000000000--- 'a key with whitespace': 'value for a ws key' ws_key: '%{alias("a key with whitespace")}' '#@!&%|§': 'not happy' angry: '%{alias("#@!&%|§")}' '!$\%!': '#@!&%|§': 'not happy at all' very_angry: '%{alias("!$\%!.#@!&%|§")}' hiera-3.2.0/spec/unit/fixtures/override/000077500000000000000000000000001271216112200202005ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/override/config/000077500000000000000000000000001271216112200214455ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/override/config/hiera.yaml000066400000000000000000000000541271216112200234200ustar00rootroot00000000000000:backends: - yaml :hierarchy: - common hiera-3.2.0/spec/unit/fixtures/override/data/000077500000000000000000000000001271216112200211115ustar00rootroot00000000000000hiera-3.2.0/spec/unit/fixtures/override/data/alternate.yaml000066400000000000000000000000211271216112200237450ustar00rootroot00000000000000bar: 'alternate' hiera-3.2.0/spec/unit/fixtures/override/data/common.yaml000066400000000000000000000000451271216112200232640ustar00rootroot00000000000000foo: '%{hiera("bar")}' bar: 'common' hiera-3.2.0/spec/unit/hiera_spec.rb000066400000000000000000000055031271216112200171420ustar00rootroot00000000000000require 'spec_helper' require 'hiera/util' # This is only around for the logger setup tests module Hiera::Foo_logger end describe "Hiera" do describe "#logger=" do it "loads the given logger" do Hiera.expects(:require).with("hiera/foo_logger") Hiera.logger = "foo" end it "falls back to the Console logger when the logger could not be loaded" do Hiera.expects(:warn) Hiera.logger = "no_such_logger" expect(Hiera.logger).to be Hiera::Console_logger end it "falls back to the Console logger when the logger class could not be found" do Hiera.expects(:warn) Hiera.expects(:require).with("hiera/no_constant_logger") Hiera.logger = "no_constant" expect(Hiera.logger).to be Hiera::Console_logger end end describe "#warn" do it "delegates to the configured logger" do Hiera.logger = 'console' Hiera::Console_logger.expects(:warn).with("rspec") Hiera.warn("rspec") end end describe "#debug" do it "delegates to the configured logger" do Hiera.logger = 'console' Hiera::Console_logger.expects(:debug).with("rspec") Hiera.debug("rspec") end end describe "#initialize" do before(:each) { Hiera::Config.expects(:load_backends) } context 'when loading the config' do let!(:code_config) { File.join(Hiera::Util.code_dir, 'hiera.yaml') } let!(:conf_config) { File.join(Hiera::Util.config_dir, 'hiera.yaml') } let!(:cli_config) { File.join('/home/bob', 'hiera.yaml') } it 'attempts to load from code_dir first and config_dir second' do File.expects(:exist?).with(code_config).returns(false) Hiera::Config.expects(:load).with(conf_config) Hiera.new end it 'does not load from config_dir when file is found in code_dir' do File.expects(:exist?).with(code_config).returns(true) Hiera::Config.expects(:load).with(code_config) Hiera.new end it 'makes no attempt to load from code_dir or config_dir when :config is given as an option' do File.expects(:exist?).with(code_config).never Hiera::Config.expects(:load).with(cli_config) Hiera.new(:config => cli_config) end end it "passes the supplied config to the config class" do Hiera::Config.expects(:load).with({"test" => "rspec"}) Hiera.new(:config => {"test" => "rspec"}) end it "loads all backends on start" do Hiera::Config.stubs(:load) Hiera.new end end describe "#lookup" do it "delegates to the Backend#lookup method" do Hiera::Config.stubs(:load) Hiera::Config.stubs(:load_backends) Hiera::Backend.expects(:lookup).with(:key, :default, :scope, :order_override, :resolution_type) Hiera.new.lookup(:key, :default, :scope, :order_override, :resolution_type) end end end hiera-3.2.0/spec/unit/interpolate_spec.rb000066400000000000000000000271401271216112200204010ustar00rootroot00000000000000require 'spec_helper' require 'hiera/util' describe "Hiera" do let!(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'interpolate') } let!(:fixture_data) { File.join(fixtures, 'data') } let(:hiera) { Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml')) } before(:each) do Hiera::Util.expects(:var_dir).at_most(3).returns(fixture_data) end context "when doing interpolation" do it 'should prevent endless recursion' do hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml')) expect do hiera.lookup('foo', nil, {}) end.to raise_error Hiera::InterpolationLoop, 'Lookup recursion detected in [hiera("bar"), hiera("foo")]' end it 'produces a nested hash with arrays from nested aliases with hashes and arrays' do Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data')) hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml')) expect(hiera.lookup('root', nil, {}, nil, :hash)).to eq({'a'=>{'aa'=>{'b'=>{'bb'=>['text']}}}}) end it 'allows keys with white space' do expect(hiera.lookup('ws_key', nil, {})).to eq('value for a ws key') end it 'allows keys with non alphanumeric characters' do expect(hiera.lookup('angry', nil, {})).to eq('not happy') end end context "when not finding value for interpolated key" do it 'should resolve the interpolation to an empty string' do expect(hiera.lookup('niltest', nil, {})).to eq('Missing key ##. Key with nil ##') end end context "when there are empty interpolations %{} in data" do it 'should should produce an empty string for the interpolation' do expect(hiera.lookup('empty_interpolation', nil, {})).to eq('clownshoe') end it 'the empty interpolation can be escaped' do expect(hiera.lookup('escaped_empty_interpolation', nil, {})).to eq('clown%{shoe}s') end it 'the value can consist of only an empty escape' do expect(hiera.lookup('only_empty_interpolation', nil, {})).to eq('') end it 'the value can consist of an empty namespace %{::}' do expect(hiera.lookup('empty_namespace', nil, {})).to eq('') end it 'the value can consist of whitespace %{ :: }' do expect(hiera.lookup('whitespace1', nil, {})).to eq('') end it 'the value can consist of whitespace %{ }' do expect(hiera.lookup('whitespace2', nil, {})).to eq('') end end context 'when there are quoted empty interpolations %{} in data' do it 'should should produce an empty string for the interpolation' do expect(hiera.lookup('quoted_empty_interpolation', nil, {})).to eq('clownshoe') end it 'the empty interpolation can be escaped' do expect(hiera.lookup('quoted_escaped_empty_interpolation', nil, {})).to eq('clown%{shoe}s') end it 'the value can consist of only an empty escape' do expect(hiera.lookup('quoted_only_empty_interpolation', nil, {})).to eq('') end it 'the value can consist of an empty namespace %{::}' do expect(hiera.lookup('quoted_empty_namespace', nil, {})).to eq('') end it 'the value can consist of whitespace %{ :: }' do expect(hiera.lookup('quoted_whitespace1', nil, {})).to eq('') end it 'the value can consist of whitespace %{ }' do expect(hiera.lookup('quoted_whitespace2', nil, {})).to eq('') end end context 'when using dotted keys' do it 'should find an entry using a quoted interpolation' do expect(hiera.lookup('"a.c.scope"', nil, {'a.b' => '(scope) a dot b'})).to eq('a dot c: (scope) a dot b') end it 'should find an entry using a quoted interpolation with method hiera' do expect(hiera.lookup('"a.c.hiera"', nil, {'a.b' => '(scope) a dot b'})).to eq('a dot c: (hiera) a dot b') end it 'should find an entry using a quoted interpolation with method alias' do expect(hiera.lookup('"a.c.alias"', nil, {'a.b' => '(scope) a dot b'})).to eq('(hiera) a dot b') end it 'should use a dotted key to navigate into a structure when it is not quoted' do expect(hiera.lookup('"a.e.scope"', nil, {'a' => { 'd' => '(scope) a dot d is a hash entry'}})).to eq('a dot e: (scope) a dot d is a hash entry') end it 'should use a dotted key to navigate into a structure when when it is not quoted with method hiera' do expect(hiera.lookup('"a.e.hiera"', nil, {'a' => { 'd' => '(scope) a dot d is a hash entry'}})).to eq('a dot e: (hiera) a dot d is a hash entry') end it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is last' do expect(hiera.lookup('"a.ex.scope"', nil, {'a' => { 'd.x' => '(scope) a dot d.x is a hash entry'}})).to eq('a dot ex: (scope) a dot d.x is a hash entry') end it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is last and method is hiera' do expect(hiera.lookup('"a.ex.hiera"', nil, {'a' => { 'd.x' => '(scope) a dot d.x is a hash entry'}})).to eq('a dot ex: (hiera) a dot d.x is a hash entry') end it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is first' do expect(hiera.lookup('"a.xe.scope"', nil, {'a.x' => { 'd' => '(scope) a.x dot d is a hash entry'}})).to eq('a dot xe: (scope) a.x dot d is a hash entry') end it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is first and method is hiera' do expect(hiera.lookup('"a.xe.hiera"', nil, {'a.x' => { 'd' => '(scope) a.x dot d is a hash entry'}})).to eq('a dot xe: (hiera) a.x dot d is a hash entry') end it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle' do expect(hiera.lookup('"a.xm.scope"', nil, {'a' => { 'd.z' => { 'g' => '(scope) a dot d.z dot g is a hash entry'}}})).to eq('a dot xm: (scope) a dot d.z dot g is a hash entry') end it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle and method is hiera' do expect(hiera.lookup('"a.xm.hiera"', nil, {'a' => { 'd.z' => { 'g' => '(scope) a dot d.z dot g is a hash entry'}}})).to eq('a dot xm: (hiera) a dot d.z dot g is a hash entry') end it 'should use a mix of several quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle' do expect(hiera.lookup('"a.xx.scope"', nil, {'a.x' => { 'd.z' => { 'g' => '(scope) a.x dot d.z dot g is a hash entry'}}})).to eq('a dot xx: (scope) a.x dot d.z dot g is a hash entry') end it 'should use a mix of several quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle and method is hiera' do expect(hiera.lookup('"a.xx.hiera"', nil, {'a.x' => { 'd.z' => { 'g' => '(scope) a.x dot d.z dot g is a hash entry'}}})).to eq('a dot xx: (hiera) a.x dot d.z dot g is a hash entry') end it 'should find an entry using using a quoted interpolation on dotted key containing numbers' do expect(hiera.lookup('"x.2.scope"', nil, {'x.1' => '(scope) x dot 1'})).to eq('x dot 2: (scope) x dot 1') end it 'should find an entry using using a quoted interpolation on dotted key containing numbers using method hiera' do expect(hiera.lookup('"x.2.hiera"', nil, {'x.1' => '(scope) x dot 1'})).to eq('x dot 2: (hiera) x dot 1') end it 'will allow strange characters in the key' do expect(hiera.lookup('very_angry', nil, {})).to eq('not happy at all') end it 'should not find a subkey when the dotted key is quoted' do expect(hiera.lookup('"a.f.scope"', nil, {'a' => { 'f' => '(scope) a dot f is a hash entry'}})).to eq('a dot f: ') end it 'should not find a subkey when the dotted key is quoted with method hiera' do expect(hiera.lookup('"a.f.hiera"', nil, {'a' => { 'f' => '(scope) a dot f is a hash entry'}})).to eq('a dot f: ') end it 'should not find a subkey that is matched within a string' do expect{ hiera.lookup('ipl_key', nil, {}) }.to raise_error(/Got String when a hash-like object was expected to access value using 'subkey' from key 'key.subkey'/) end it 'should not find a subkey that is matched within a string' do expect{ hiera.lookup('key.subkey', nil, {}) }.to raise_error(/Got String when a hash-like object was expected to access value using 'subkey' from key 'key.subkey'/) end end context 'when bad interpolation expressions are encountered' do it 'should produce an error when different quotes are used on either side' do expect { hiera.lookup('quote_mismatch', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{'the\.key"\}/) end it 'should produce an if there is only one quote' do expect { hiera.lookup('one_quote', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.'key\}/) end it 'should produce an error for an empty segment' do expect { hiera.lookup('empty_segment', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.\.key\}/) end it 'should produce an error for an empty quoted segment' do expect { hiera.lookup('empty_quoted_segment', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.''\.key\}/) end it 'should produce an error for an partly quoted segment' do expect { hiera.lookup('partly_quoted_segment', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.'pa'key\}/) end it 'should produce an error when different quotes are used on either side in a method argument' do expect { hiera.lookup('quote_mismatch_arg', nil, {}) }.to raise_error(/Argument to interpolation method 'hiera' must be quoted, got ''the.key"'/) end it 'should produce an error unless a known interpolation method is used' do expect { hiera.lookup('non_existing_method', nil, {}) }.to raise_error(/Invalid interpolation method 'flubber'/) end it 'should produce an error if there is only one quote' do expect { hiera.lookup('one_quote', nil, {}) }.to raise_error(/Syntax error/) end it 'should produce an error when different quotes are used on either side in a top-level key' do expect { hiera.lookup("'the.key\"", nil, {}) }.to raise_error(/Syntax error in key: 'the.key"/) end end context 'when doing interpolation with override' do let!(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'override') } it 'should resolve interpolation using the override' do expect(hiera.lookup('foo', nil, {}, 'alternate')).to eq('alternate') end end context 'when doing interpolation in config file' do let(:hiera) { Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera.yaml')) } it 'should allow and resolve a correctly configured interpolation using "hiera" method' do expect(hiera.lookup('foo', nil, {})).to eq('Foo') end it 'should issue warning when interpolation methods are used' do Hiera.expects(:warn).with('Use of interpolation methods in hiera configuration file is deprecated').at_least_once expect(hiera.lookup('foo', nil, {})).to eq('Foo') end end context 'when doing interpolation in bad config file' do let(:hiera) { Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera_bad.yaml')) } it 'should detect interpolation recursion when using "hiera" method' do expect{ hiera.lookup('foo', nil, {}) }.to raise_error(Hiera::InterpolationLoop, "Lookup recursion detected in [hiera('role')]") end end end hiera-3.2.0/spec/unit/puppet_logger_spec.rb000066400000000000000000000012201271216112200207160ustar00rootroot00000000000000require 'hiera/puppet_logger' describe Hiera::Puppet_logger do it "is not suitable when Puppet is not defined" do ensure_puppet_not_defined expect(Hiera::Puppet_logger.suitable?).to eq(false) end it "is suitable when Puppet is defined" do ensure_puppet_defined expect(Hiera::Puppet_logger.suitable?).to eq(true) end after :each do ensure_puppet_not_defined end def ensure_puppet_defined if !Kernel.const_defined? :Puppet Kernel.const_set(:Puppet, "Fake Puppet") end end def ensure_puppet_not_defined if Kernel.const_defined? :Puppet Kernel.send(:remove_const, :Puppet) end end end hiera-3.2.0/spec/unit/util_spec.rb000066400000000000000000000043751271216112200170350ustar00rootroot00000000000000require 'spec_helper' describe Hiera::Util do describe 'Hiera::Util.posix?' do it 'should return true on posix systems' do Etc.expects(:getpwuid).with(0).returns(true) expect(Hiera::Util.posix?).to be_truthy end it 'should return false on non posix systems' do Etc.expects(:getpwuid).with(0).returns(nil) expect(Hiera::Util.posix?).to be_falsey end end describe 'Hiera::Util.microsoft_windows?' do it 'should return false on posix systems' do Hiera::Util.expects(:file_alt_separator).returns(nil) expect(Hiera::Util.microsoft_windows?).to be_falsey end end describe 'Hiera::Util.config_dir' do it 'should return the correct path for posix systems' do Hiera::Util.expects(:file_alt_separator).returns(nil) expect(Hiera::Util.config_dir).to eq('/etc/puppetlabs/puppet') end it 'should return the correct path for microsoft windows systems' do Hiera::Util.expects(:microsoft_windows?).returns(true) Hiera::Util.expects(:common_appdata).returns('C:\\ProgramData') expect(Hiera::Util.config_dir).to eq('C:\\ProgramData/PuppetLabs/puppet/etc') end end describe 'Hiera::Util.code_dir' do it 'should return the correct path for posix systems' do Hiera::Util.expects(:file_alt_separator).returns(nil) expect(Hiera::Util.code_dir).to eq('/etc/puppetlabs/code') end it 'should return the correct path for microsoft windows systems' do Hiera::Util.expects(:microsoft_windows?).returns(true) Hiera::Util.expects(:common_appdata).returns('C:\\ProgramData') expect(Hiera::Util.code_dir).to eq('C:\\ProgramData/PuppetLabs/code') end end describe 'Hiera::Util.var_dir' do it 'should return the correct path for posix systems' do Hiera::Util.expects(:file_alt_separator).returns(nil) expect(Hiera::Util.var_dir).to eq('/etc/puppetlabs/code/environments/%{environment}/hieradata') end it 'should return the correct path for microsoft windows systems' do Hiera::Util.expects(:microsoft_windows?).returns(true) Hiera::Util.expects(:common_appdata).returns('C:\\ProgramData') expect(Hiera::Util.var_dir).to eq('C:\\ProgramData/PuppetLabs/code/environments/%{environment}/hieradata') end end end hiera-3.2.0/spec/unit/version_spec.rb000066400000000000000000000020301271216112200175270ustar00rootroot00000000000000require "spec_helper" require "hiera/version" require 'pathname' describe "Hiera.version Public API" do subject() { Hiera } before :each do Hiera.instance_eval do if @hiera_version @hiera_version = nil end end end context "without a VERSION file" do before :each do subject.stubs(:read_version_file).returns(nil) end it "is Hiera::VERSION" do expect(subject.version).to eq(Hiera::VERSION) end it "respects the version= setter" do subject.version = '1.2.3' expect(subject.version).to eq('1.2.3') end end context "with a VERSION file" do it "is the content of the file" do subject.expects(:read_version_file).with() do |path| pathname = Pathname.new(path) pathname.basename.to_s == "VERSION" end.returns('1.2.1-9-g9fda440') expect(subject.version).to eq('1.2.1-9-g9fda440') end it "respects the version= setter" do subject.version = '1.2.3' expect(subject.version).to eq('1.2.3') end end end hiera-3.2.0/tasks/000077500000000000000000000000001271216112200137245ustar00rootroot00000000000000hiera-3.2.0/tasks/ci.rake000066400000000000000000000016111271216112200151620ustar00rootroot00000000000000require 'yaml' require 'time' namespace "ci" do task :spec do ENV["LOG_SPEC_ORDER"] = "true" sh %{rspec --require rspec/legacy_formatters -r yarjuf -f JUnit -o result.xml -fp spec} # sh %{rspec -r yarjuf -f JUnit -o result.xml -fp spec} end desc "Tar up the acceptance/ directory so that package test runs have tests to run against." task :acceptance_artifacts => :tag_creator do rm_f "acceptance/acceptance-artifacts.tar.gz" sh "tar -czv --exclude acceptance/.bundle -f acceptance-artifacts.tar.gz acceptance schema" end task :tag_creator do Dir.chdir("acceptance") do File.open('creator.txt', 'w') do |fh| YAML.dump({ 'creator_id' => ENV['CREATOR'] || ENV['BUILD_URL'] || 'unknown', 'created_on' => Time.now.iso8601, 'commit' => (`git log -1 --oneline` rescue "unknown: #{$!}") }, fh) end end end end