From 33391174203ff245fe78b7055d7fcc08e29ee068 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Wed, 5 Jul 2023 12:40:31 +0200 Subject: [PATCH 1/2] Upgrade dependencies. Latest security fixes for rails plus a bunch of minor updates. --- Gemfile.lock | 231 +++++++++++++++++++++++++++------------------------ 1 file changed, 124 insertions(+), 107 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6bc5f5bc..611e2cc6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,96 +2,97 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (2.3.6) - actioncable (7.0.4.3) - actionpack (= 7.0.4.3) - activesupport (= 7.0.4.3) + actioncable (7.0.6) + actionpack (= 7.0.6) + activesupport (= 7.0.6) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.4.3) - actionpack (= 7.0.4.3) - activejob (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + actionmailbox (7.0.6) + actionpack (= 7.0.6) + activejob (= 7.0.6) + activerecord (= 7.0.6) + activestorage (= 7.0.6) + activesupport (= 7.0.6) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.4.3) - actionpack (= 7.0.4.3) - actionview (= 7.0.4.3) - activejob (= 7.0.4.3) - activesupport (= 7.0.4.3) + actionmailer (7.0.6) + actionpack (= 7.0.6) + actionview (= 7.0.6) + activejob (= 7.0.6) + activesupport (= 7.0.6) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.4.3) - actionview (= 7.0.4.3) - activesupport (= 7.0.4.3) - rack (~> 2.0, >= 2.2.0) + actionpack (7.0.6) + actionview (= 7.0.6) + activesupport (= 7.0.6) + rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.4.3) - actionpack (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + actiontext (7.0.6) + actionpack (= 7.0.6) + activerecord (= 7.0.6) + activestorage (= 7.0.6) + activesupport (= 7.0.6) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.4.3) - activesupport (= 7.0.4.3) + actionview (7.0.6) + activesupport (= 7.0.6) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.4.3) - activesupport (= 7.0.4.3) + activejob (7.0.6) + activesupport (= 7.0.6) globalid (>= 0.3.6) - activemodel (7.0.4.3) - activesupport (= 7.0.4.3) - activerecord (7.0.4.3) - activemodel (= 7.0.4.3) - activesupport (= 7.0.4.3) - activestorage (7.0.4.3) - actionpack (= 7.0.4.3) - activejob (= 7.0.4.3) - activerecord (= 7.0.4.3) - activesupport (= 7.0.4.3) + activemodel (7.0.6) + activesupport (= 7.0.6) + activerecord (7.0.6) + activemodel (= 7.0.6) + activesupport (= 7.0.6) + activestorage (7.0.6) + actionpack (= 7.0.6) + activejob (= 7.0.6) + activerecord (= 7.0.6) + activesupport (= 7.0.6) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.4.3) + activesupport (7.0.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.3) + addressable (2.8.4) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) - async (2.5.0) + async (2.6.2) console (~> 1.10) + fiber-annotation io-event (~> 1.1) timers (~> 4.1) - async-http (0.60.1) + async-http (0.60.2) async (>= 1.25) async-io (>= 1.28) async-pool (>= 0.2) protocol-http (~> 0.24.0) protocol-http1 (~> 0.15.0) protocol-http2 (~> 0.15.0) - traces (>= 0.8.0) - async-http-faraday (0.11.0) + traces (>= 0.10.0) + async-http-faraday (0.12.0) async-http (~> 0.42) faraday - async-io (1.34.3) + async-io (1.35.0) async async-pool (0.4.0) async (>= 1.25) autoprefixer-rails (10.4.13.0) execjs (~> 2) - bcrypt (3.1.18) + bcrypt (3.1.19) bindex (0.8.1) bootstrap (4.6.2) autoprefixer-rails (>= 9.1.0) @@ -107,7 +108,7 @@ GEM builder (3.2.4) byebug (11.1.3) cancancan (3.5.0) - capybara (3.39.0) + capybara (3.39.2) addressable matrix mini_mime (>= 0.1.3) @@ -117,12 +118,14 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) concurrent-ruby (1.1.10) - connection_pool (2.4.0) - console (1.16.2) + connection_pool (2.4.1) + console (1.17.2) + fiber-annotation fiber-local crass (1.0.6) - dartsass-rails (0.4.1) + dartsass-rails (0.5.0) railties (>= 6.0.0) + sass-embedded (~> 1.63) date (3.3.3) deep_merge (1.2.2) diffy (3.4.2) @@ -137,18 +140,19 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faker (3.1.1) + faker (3.2.0) i18n (>= 1.8.11, < 2) - faraday (2.7.4) + faraday (2.7.9) faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) - faraday-http-cache (2.4.1) + faraday-http-cache (2.5.0) faraday (>= 0.8) faraday-net_http (3.0.2) - faraday-retry (2.1.0) + faraday-retry (2.2.0) faraday (~> 2.0) fast_gettext (2.3.0) ffi (1.15.5) + fiber-annotation (0.2.0) fiber-local (1.0.0) friendly_id (5.5.0) activerecord (>= 4.0.0) @@ -168,6 +172,9 @@ GEM rake (>= 10.0) globalid (1.1.0) activesupport (>= 5.0) + google-protobuf (3.23.3) + google-protobuf (3.23.3-x86_64-darwin) + google-protobuf (3.23.3-x86_64-linux) hiera (3.12.0) hiera-eyaml (3.3.0) highline @@ -177,27 +184,27 @@ GEM httparty (0.21.0) mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.12.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) - importmap-rails (1.1.5) + importmap-rails (1.2.1) actionpack (>= 6.0.0) railties (>= 6.0.0) - io-event (1.1.7) - jquery-rails (4.5.1) + io-event (1.2.2) + jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (2.6.3) - libv8-node (16.10.0.0) - libv8-node (16.10.0.0-x86_64-darwin) - libv8-node (16.10.0.0-x86_64-linux) + libv8-node (18.16.0.0) + libv8-node (18.16.0.0-x86_64-darwin) + libv8-node (18.16.0.0-x86_64-linux) listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) locale (2.1.3) - loofah (2.20.0) + loofah (2.21.3) crass (~> 1.0.2) - nokogiri (>= 1.5.9) + nokogiri (>= 1.12.0) mail (2.8.1) mini_mime (>= 0.1.1) net-imap @@ -207,13 +214,13 @@ GEM matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.2) - mini_portile2 (2.8.1) - mini_racer (0.6.3) - libv8-node (~> 16.10.0.0) - minitest (5.18.0) + mini_portile2 (2.8.2) + mini_racer (0.8.0) + libv8-node (~> 18.16.0.0) + minitest (5.18.1) multi_json (1.15.0) multi_xml (0.6.0) - net-imap (0.3.4) + net-imap (0.3.6) date net-protocol net-ldap (0.18.0) @@ -224,30 +231,31 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.9) - nokogiri (1.14.3) - mini_portile2 (~> 2.8.0) + nokogiri (1.15.2) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.14.3-x86_64-darwin) + nokogiri (1.15.2-x86_64-darwin) racc (~> 1.4) - nokogiri (1.14.3-x86_64-linux) + nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) sawyer (~> 0.9) optimist (3.0.1) - parallel (1.22.1) - parser (3.2.2.0) + parallel (1.23.0) + parser (3.2.2.3) ast (~> 2.4.1) + racc popper_js (1.16.1) protocol-hpack (1.4.2) - protocol-http (0.24.1) + protocol-http (0.24.4) protocol-http1 (0.15.0) protocol-http (~> 0.22) protocol-http2 (0.15.1) protocol-hpack (~> 1.4) protocol-http (~> 0.18) public_suffix (5.0.1) - puma (6.2.1) + puma (6.3.0) nio4r (~> 2.0) puppet (7.23.0) concurrent-ruby (~> 1.0, < 1.2.0) @@ -276,32 +284,34 @@ GEM hocon (>= 1.0) puppetdb-ruby (1.2.0) httparty - racc (1.6.2) - rack (2.2.6.4) + racc (1.7.1) + rack (2.2.7) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.4.3) - actioncable (= 7.0.4.3) - actionmailbox (= 7.0.4.3) - actionmailer (= 7.0.4.3) - actionpack (= 7.0.4.3) - actiontext (= 7.0.4.3) - actionview (= 7.0.4.3) - activejob (= 7.0.4.3) - activemodel (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + rails (7.0.6) + actioncable (= 7.0.6) + actionmailbox (= 7.0.6) + actionmailer (= 7.0.6) + actionpack (= 7.0.6) + actiontext (= 7.0.6) + actionview (= 7.0.6) + activejob (= 7.0.6) + activemodel (= 7.0.6) + activerecord (= 7.0.6) + activestorage (= 7.0.6) + activesupport (= 7.0.6) bundler (>= 1.15.0) - railties (= 7.0.4.3) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 7.0.6) + rails-dom-testing (2.1.1) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.5.0) - loofah (~> 2.19, >= 2.19.1) - railties (7.0.4.3) - actionpack (= 7.0.4.3) - activesupport (= 7.0.4.3) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.0.6) + actionpack (= 7.0.6) + activesupport (= 7.0.6) method_source rake (>= 12.2) thor (~> 1.0) @@ -316,7 +326,7 @@ GEM redis-client (>= 0.9.0) redis-client (0.14.1) connection_pool - regexp_parser (2.7.0) + regexp_parser (2.8.1) rexml (3.2.5) rubocop (1.49.0) json (~> 2.3) @@ -344,6 +354,13 @@ GEM rexml ruby2_keywords (0.0.5) rubyzip (2.3.2) + sass-embedded (1.63.6) + google-protobuf (~> 3.23) + rake (>= 13.0.0) + sass-embedded (1.63.6-x86_64-darwin) + google-protobuf (~> 3.23) + sass-embedded (1.63.6-x86_64-linux-gnu) + google-protobuf (~> 3.23) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -356,7 +373,7 @@ GEM addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) scanf (1.0.0) - selenium-webdriver (4.8.6) + selenium-webdriver (4.10.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -374,17 +391,17 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sqlite3 (1.6.2) + sqlite3 (1.6.3) mini_portile2 (~> 2.8.0) - sqlite3 (1.6.2-x86_64-darwin) - sqlite3 (1.6.2-x86_64-linux) + sqlite3 (1.6.3-x86_64-darwin) + sqlite3 (1.6.3-x86_64-linux) stimulus-rails (1.2.1) railties (>= 6.0.0) - thor (1.2.1) - tilt (2.1.0) - timeout (0.3.2) + thor (1.2.2) + tilt (2.2.0) + timeout (0.4.0) timers (4.3.5) - traces (0.9.1) + traces (0.11.1) turbo-rails (1.4.0) actionpack (>= 6.0.0) activejob (>= 6.0.0) @@ -407,7 +424,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.7) + zeitwerk (2.6.8) PLATFORMS ruby From 650fdbdda02b2807e3104e0b1b1f145ab31de010 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Wed, 5 Jul 2023 13:32:20 +0200 Subject: [PATCH 2/2] Allow custom lookup functions #162 We do not (and cannot support) custom lookup functions per se, but in case they map cleanly to an existing backing, e.g. eyaml, this allows to specify this mapping, so everything should then work as expected. --- .rubocop_todo.yml | 8 - app/models/hiera_data/data_file.rb | 2 +- app/models/hiera_data/hierarchy.rb | 49 +-- config/hdm.yml.template | 9 + test/models/hiera_data/hierarchy_test.rb | 383 ++++++++++++----------- 5 files changed, 245 insertions(+), 206 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5dcda35b..0273023d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -292,7 +292,6 @@ Rails/FilePath: # Include: **/test/**/* Rails/RefuteMethods: Exclude: - - 'test/models/hiera_data/hierarchy_test.rb' - 'test/models/hiera_data/yaml_file_test.rb' - 'test/models/hierarchy_test.rb' - 'test/models/user_test.rb' @@ -336,7 +335,6 @@ Style/ClassAndModuleChildren: - 'test/models/hiera_data/config_test.rb' - 'test/models/hiera_data/data_file_test.rb' - 'test/models/hiera_data/git_repo_test.rb' - - 'test/models/hiera_data/hierarchy_test.rb' - 'test/models/hiera_data/interpolation_test.rb' - 'test/models/hiera_data/yaml_file_test.rb' - 'test/test_helper.rb' @@ -375,7 +373,6 @@ Style/GuardClause: - 'app/controllers/page_controller.rb' - 'app/controllers/sessions_controller.rb' - 'app/models/hiera_data/data_file.rb' - - 'app/models/hiera_data/hierarchy.rb' # Offense count: 1 # Configuration parameters: MinBranchesCount. @@ -399,7 +396,6 @@ Style/IfUnlessModifier: - 'app/controllers/sessions_controller.rb' - 'app/controllers/users_controller.rb' - 'app/models/hiera_data/config.rb' - - 'app/models/hiera_data/hierarchy.rb' # Offense count: 1 # This cop supports safe auto-correction (--auto-correct). @@ -416,7 +412,6 @@ Style/MutableConstant: Style/NumericLiteralPrefix: Exclude: - 'app/models/hiera_data/yaml_file.rb' - - 'test/models/hiera_data/hierarchy_test.rb' - 'test/models/hiera_data/yaml_file_test.rb' # Offense count: 1 @@ -431,10 +426,8 @@ Style/NumericLiterals: # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - - 'app/models/hiera_data/hierarchy.rb' - 'config/initializers/friendly_id.rb' - 'test/models/environment_test.rb' - - 'test/models/hiera_data/hierarchy_test.rb' - 'test/models/key_test.rb' # Offense count: 5 @@ -444,7 +437,6 @@ Style/PercentLiteralDelimiters: Style/PreferredHashMethods: Exclude: - 'app/models/hiera_data/data_file.rb' - - 'app/models/hiera_data/hierarchy.rb' - 'app/models/hiera_data/yaml_file.rb' # Offense count: 3 diff --git a/app/models/hiera_data/data_file.rb b/app/models/hiera_data/data_file.rb index a5528c61..8f341661 100644 --- a/app/models/hiera_data/data_file.rb +++ b/app/models/hiera_data/data_file.rb @@ -61,7 +61,7 @@ def create_file(type) when :yaml YamlFile.new(path: @path) else - raise HDM::Error, "unsupported data file type #{type}" + raise Hdm::Error, "unsupported data file type #{type}" end end end diff --git a/app/models/hiera_data/hierarchy.rb b/app/models/hiera_data/hierarchy.rb index 003215ad..004a8e03 100644 --- a/app/models/hiera_data/hierarchy.rb +++ b/app/models/hiera_data/hierarchy.rb @@ -1,6 +1,16 @@ class HieraData class Hierarchy - LOOKUP_FUNCTIONS = %w(lookup_key data_hash data_dig hiera3_backend).freeze + LOOKUP_FUNCTIONS = %w[lookup_key data_hash data_dig hiera3_backend].freeze + BACKENDS = { + "data_hash" => { + "json_data" => :json, + "yaml_data" => :yaml + }, + "lookup_key" => { + "eyaml_lookup_key" => :eyaml + } + }.freeze + attr_reader :raw_hash def initialize(raw_hash:, base_path:) @@ -17,17 +27,7 @@ def lookup_function end def backend - @backend ||= - case [lookup_function, raw_hash[lookup_function]] - when ["data_hash", "yaml_data"] - :yaml - when ["data_hash", "json_data"] - :json - when ["lookup_key", "eyaml_lookup_key"] - :eyaml - else - raise HDM::Error, "unknown backend #{raw_hash[lookup_function]}" - end + @backend ||= determine_backend end def datadir(facts: nil) @@ -44,15 +44,15 @@ def datadir(facts: nil) end def private_key - if backend == :eyaml - @base_path.join(raw_hash.dig("options", "pkcs7_private_key")) - end + return unless backend == :eyaml + + @base_path.join(raw_hash.dig("options", "pkcs7_private_key")) end def public_key - if backend == :eyaml - @base_path.join(raw_hash.dig("options", "pkcs7_public_key")) - end + return unless backend == :eyaml + + @base_path.join(raw_hash.dig("options", "pkcs7_public_key")) end def encryptable? @@ -62,7 +62,7 @@ def encryptable? end def uses_globs? - raw_hash.has_key?("glob") || raw_hash.has_key?("globs") + raw_hash.key?("glob") || raw_hash.key?("globs") end def paths @@ -93,5 +93,16 @@ def setup_paths base_key = uses_globs? ? "glob" : "path" Array(raw_hash[base_key] || raw_hash.fetch("#{base_key}s", [])) end + + def determine_backend + key = lookup_function + value = raw_hash[lookup_function] + backends = BACKENDS + custom_mappings = Rails.configuration.hdm[:custom_lookup_function_mapping] + backends = backends.deep_merge({ "lookup_key" => custom_mappings }) if custom_mappings.present? + backends.fetch(key).fetch(value).to_sym + rescue KeyError + raise Hdm::Error, "unknown backend #{value}" + end end end diff --git a/config/hdm.yml.template b/config/hdm.yml.template index cc95923c..0ea3cef9 100644 --- a/config/hdm.yml.template +++ b/config/hdm.yml.template @@ -79,3 +79,12 @@ production: # idp_cert_fingerprint: "aaa" # idp_cert: "cert" # use either fingerprint _or_ cert but not both +# Example for a custom lookup function, called `my_custom_function`, +# mapped to an existing backend, `eyaml` +# production: +# read_only: false +# allow_encryption: true +# puppet_db: +# server: "https://localhost:8081" +# custom_lookup_function_mapping: +# my_custom_function: eyaml diff --git a/test/models/hiera_data/hierarchy_test.rb b/test/models/hiera_data/hierarchy_test.rb index 374ce568..84ce0cd6 100644 --- a/test/models/hiera_data/hierarchy_test.rb +++ b/test/models/hiera_data/hierarchy_test.rb @@ -1,225 +1,252 @@ require 'test_helper' -class HieraData::HierarchyTest < ActiveSupport::TestCase - test "#uses_globs? returns true if `glob` key present" do - glob = "/*/singular/glob" - raw_hash = { "name" => "Singular path", "glob" => glob } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert hierarchy.uses_globs? - end - - test "#uses_globs? returns true if `globs` key present" do - globs = ["/*/array/globs", "/test/**/globs"] - raw_hash = { "name" => "Singular path", "globs" => globs } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert hierarchy.uses_globs? - end - - test "#paths supports the singular `path` setting" do - path = "/test/singular/path" - raw_hash = { "name" => "Singular path", "path" => path } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal [path], hierarchy.paths - end - - test "#paths returns all non-interpolated path names" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - expected_paths = [ - "nodes/%{::facts.fqdn}.yaml", - "role/%{::facts.role}-%{::facts.env}.yaml", - "role/%{::facts.role}.yaml", - "zone/%{::facts.zone}.yaml", - "common.yaml" - ] - assert_equal expected_paths, hierarchy.paths - end - - test "#paths returns an empty array if no data is available" do - hierarchy = HieraData::Hierarchy.new(raw_hash: {}, base_path: ".") - assert hierarchy.paths.empty? - end - - test "#paths supports the singular `glob` setting" do - glob = "/*/singular/glob" - raw_hash = { "name" => "Singular path", "glob" => glob } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal [glob], hierarchy.paths - end - - test "#paths supports the `globs` array setting" do - globs = ["/*/array/globs", "/test/**/globs"] - raw_hash = { "name" => "Singular path", "globs" => globs } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal globs, hierarchy.paths - end - - test "#resolved_paths uses facts to resolve paths" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - facts = { - "fqdn" => "testhost", - "role" => "hdm_test", - "env" => "development", - "zone" => "internal" - } - expected_resolved_paths = [ - "nodes/testhost.yaml", - "role/hdm_test-development.yaml", - "role/hdm_test.yaml", - "zone/internal.yaml", - "common.yaml" - ] - assert_equal expected_resolved_paths, hierarchy.resolved_paths(facts:) - end - - test "#resolved_paths resolves globs" do - base_path = Rails.root.join("test/fixtures/files/puppet/environments/globs") - globs = ["common/*.yaml"] - raw_hash = { "name" => "Common", "datadir" => "data", "globs" => globs } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path:) - facts = { "fqdn" => "testhost" } - expected_resolved_paths = [ - "common/foobar.yaml", - "common/hdm.yaml" - ] - assert_equal expected_resolved_paths, hierarchy.resolved_paths(facts:) - end - - test "#name returns the existing name" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal "Yaml hierarchy", hierarchy.name - end - - test "#candidate_files returns files matching paths with variables replaced by globs" do - base_path = Rails.root.join("test/fixtures/files/puppet/environments/multiple_hierarchies") - hierarchy = HieraData::Hierarchy.new( - raw_hash: raw_hash.merge("datadir" => "data"), - base_path: - ) - expected_candidate_files = [ - "nodes/4msusyei.betadots.training.yaml", - "nodes/60wxmaw5.betadots.training.yaml", - "nodes/test.host.yaml", - "role/hdm_test.yaml", - "zone/internal.yaml", - "common.yaml" - ] - assert_equal expected_candidate_files, hierarchy.candidate_files - end +class HieraData + class HierarchyTest < ActiveSupport::TestCase + test "#uses_globs? returns true if `glob` key present" do + glob = "/*/singular/glob" + raw_hash = { "name" => "Singular path", "glob" => glob } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert hierarchy.uses_globs? + end - test "#datadir uses facts to resolve datadir" do - raw_hash = { - "name" => "dynamic datadir", - "datadir" => "%{facts.datadir}" - } - facts = { "datadir" => "data1" } - base_path = Rails.root.join("test/fixtures/files/puppet/environments/dynamic_datadir") - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path:) + test "#uses_globs? returns true if `globs` key present" do + globs = ["/*/array/globs", "/test/**/globs"] + raw_hash = { "name" => "Singular path", "globs" => globs } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert hierarchy.uses_globs? + end - assert_equal base_path.join("data1"), hierarchy.datadir(facts:) - end + test "#paths supports the singular `path` setting" do + path = "/test/singular/path" + raw_hash = { "name" => "Singular path", "path" => path } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal [path], hierarchy.paths + end - def raw_hash - { - "name" => "Yaml hierarchy", - "paths" => [ + test "#paths returns all non-interpolated path names" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + expected_paths = [ "nodes/%{::facts.fqdn}.yaml", "role/%{::facts.role}-%{::facts.env}.yaml", "role/%{::facts.role}.yaml", "zone/%{::facts.zone}.yaml", "common.yaml" ] - } - end + assert_equal expected_paths, hierarchy.paths + end - class HieraData::HierarchyForYamlDataTest < ActiveSupport::TestCase - test "data_hash specified and yaml" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal :yaml, hierarchy.backend + test "#paths returns an empty array if no data is available" do + hierarchy = HieraData::Hierarchy.new(raw_hash: {}, base_path: ".") + assert hierarchy.paths.empty? end - def raw_hash - { - "name" => "Yaml hierarchy", - "data_hash" => "yaml_data" - } + test "#paths supports the singular `glob` setting" do + glob = "/*/singular/glob" + raw_hash = { "name" => "Singular path", "glob" => glob } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal [glob], hierarchy.paths end - end - class HieraData::HierarchyForJSONDataTest < ActiveSupport::TestCase - test "data_hash specified and json" do + test "#paths supports the `globs` array setting" do + globs = ["/*/array/globs", "/test/**/globs"] + raw_hash = { "name" => "Singular path", "globs" => globs } hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal :json, hierarchy.backend + assert_equal globs, hierarchy.paths end - def raw_hash - { - "name" => "JSON hierarchy", - "data_hash" => "json_data" + test "#resolved_paths uses facts to resolve paths" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + facts = { + "fqdn" => "testhost", + "role" => "hdm_test", + "env" => "development", + "zone" => "internal" } + expected_resolved_paths = [ + "nodes/testhost.yaml", + "role/hdm_test-development.yaml", + "role/hdm_test.yaml", + "zone/internal.yaml", + "common.yaml" + ] + assert_equal expected_resolved_paths, hierarchy.resolved_paths(facts:) end - end - class HieraData::HierarchyForEyamlDataTest < ActiveSupport::TestCase - setup do - @tmpdir = Dir.mktmpdir + test "#resolved_paths resolves globs" do + base_path = Rails.root.join("test/fixtures/files/puppet/environments/globs") + globs = ["common/*.yaml"] + raw_hash = { "name" => "Common", "datadir" => "data", "globs" => globs } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path:) + facts = { "fqdn" => "testhost" } + expected_resolved_paths = [ + "common/foobar.yaml", + "common/hdm.yaml" + ] + assert_equal expected_resolved_paths, hierarchy.resolved_paths(facts:) end - teardown do - FileUtils.remove_entry @tmpdir + test "#name returns the existing name" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal "Yaml hierarchy", hierarchy.name end - test "lookup function is not data_hash" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal :eyaml, hierarchy.backend + test "#candidate_files returns files matching paths with variables replaced by globs" do + base_path = Rails.root.join("test/fixtures/files/puppet/environments/multiple_hierarchies") + hierarchy = HieraData::Hierarchy.new( + raw_hash: raw_hash.merge("datadir" => "data"), + base_path: + ) + expected_candidate_files = [ + "nodes/4msusyei.betadots.training.yaml", + "nodes/60wxmaw5.betadots.training.yaml", + "nodes/test.host.yaml", + "role/hdm_test.yaml", + "zone/internal.yaml", + "common.yaml" + ] + assert_equal expected_candidate_files, hierarchy.candidate_files end - test "#private_key returns path from options" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal "private.key", hierarchy.private_key.to_s + test "#datadir uses facts to resolve datadir" do + raw_hash = { + "name" => "dynamic datadir", + "datadir" => "%{facts.datadir}" + } + facts = { "datadir" => "data1" } + base_path = Rails.root.join("test/fixtures/files/puppet/environments/dynamic_datadir") + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path:) + + assert_equal base_path.join("data1"), hierarchy.datadir(facts:) end - test "#public_key returns path from options" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") - assert_equal "public.key", hierarchy.public_key.to_s + def raw_hash + { + "name" => "Yaml hierarchy", + "paths" => [ + "nodes/%{::facts.fqdn}.yaml", + "role/%{::facts.role}-%{::facts.env}.yaml", + "role/%{::facts.role}.yaml", + "zone/%{::facts.zone}.yaml", + "common.yaml" + ] + } end - test "#encryptable? is true if all keys present and readable" do - tmpdir_path = Pathname.new(@tmpdir) - %w(private.key public.key).each { |f| FileUtils.touch(tmpdir_path.join(f)) } - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: @tmpdir) + class HierarchyForYamlDataTest < ActiveSupport::TestCase + test "data_hash specified and yaml" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal :yaml, hierarchy.backend + end - assert hierarchy.encryptable? + def raw_hash + { + "name" => "Yaml hierarchy", + "data_hash" => "yaml_data" + } + end end - test "#encryptable? is false if a key is missing" do - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: @tmpdir) + class HierarchyForJSONDataTest < ActiveSupport::TestCase + test "data_hash specified and json" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal :json, hierarchy.backend + end - refute hierarchy.encryptable? + def raw_hash + { + "name" => "JSON hierarchy", + "data_hash" => "json_data" + } + end end - test "#encryptable? is false if a key is not readable" do - tmpdir_path = Pathname.new(@tmpdir) - files = %w(private.key public.key).map { |f| tmpdir_path.join(f) } - files.each do |path| - FileUtils.touch(path) - File.chmod(0000, path) + class HierarchyForEyamlDataTest < ActiveSupport::TestCase + setup do + @tmpdir = Dir.mktmpdir end - hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: @tmpdir) - refute hierarchy.encryptable? + teardown do + FileUtils.remove_entry @tmpdir + end - File.chmod(0600, *files) + test "lookup function is not data_hash" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal :eyaml, hierarchy.backend + end + + test "#private_key returns path from options" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal "private.key", hierarchy.private_key.to_s + end + + test "#public_key returns path from options" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal "public.key", hierarchy.public_key.to_s + end + + test "#encryptable? is true if all keys present and readable" do + tmpdir_path = Pathname.new(@tmpdir) + %w[private.key public.key].each { |f| FileUtils.touch(tmpdir_path.join(f)) } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: @tmpdir) + + assert hierarchy.encryptable? + end + + test "#encryptable? is false if a key is missing" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: @tmpdir) + + assert_not hierarchy.encryptable? + end + + test "#encryptable? is false if a key is not readable" do + tmpdir_path = Pathname.new(@tmpdir) + files = %w[private.key public.key].map { |f| tmpdir_path.join(f) } + files.each do |path| + FileUtils.touch(path) + File.chmod(0o000, path) + end + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: @tmpdir) + + assert_not hierarchy.encryptable? + + File.chmod(0o600, *files) + end + + def raw_hash + { + "name" => "EYaml hierarchy", + "lookup_key" => "eyaml_lookup_key", + "options" => { + "pkcs7_private_key" => "private.key", + "pkcs7_public_key" => "public.key" + } + } + end end - def raw_hash - { - "name" => "EYaml hierarchy", - "lookup_key" => "eyaml_lookup_key", - "options" => { - "pkcs7_private_key" => "private.key", - "pkcs7_public_key" => "public.key" + class HierarchyForCustomBackend < ActiveSupport::TestCase + test "custom lookup function specified but no mapping present" do + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_raises Hdm::Error do + hierarchy.backend + end + end + + test "custom lookup function mapped to eyaml" do + Rails.configuration.hdm[:custom_lookup_function_mapping] = { + "custom_eyaml_function" => "eyaml" } - } + hierarchy = HieraData::Hierarchy.new(raw_hash:, base_path: ".") + assert_equal :eyaml, hierarchy.backend + Rails.configuration.hdm.delete(:custom_lookup_function_mapping) + end + + def raw_hash + { + "name" => "JSON hierarchy", + "lookup_key" => "custom_eyaml_function" + } + end end end end