From 65017847b054ae61a0e635307898e9fb3c367d6e Mon Sep 17 00:00:00 2001 From: Jeff Fraser Date: Wed, 26 Sep 2018 11:01:49 -0400 Subject: [PATCH 1/4] Support multiple file sources for a single template This will concatenate the contents of each file together to form a single template, which will then be rendered once. --- .gitignore | 2 +- lib/consult/template.rb | 21 ++++++++++++++++++++- spec/consult_spec.rb | 5 +++++ spec/support/config/consult.yml | 20 ++++++++++++++++++++ spec/support/expected/elements.txt | 2 ++ spec/support/expected/more_elements.txt | 3 +++ spec/support/templates/elements/air.txt | 1 + spec/support/templates/elements/fire.txt | 1 + spec/support/templates/elements/water.txt | 1 + 9 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 spec/support/expected/elements.txt create mode 100644 spec/support/expected/more_elements.txt create mode 100644 spec/support/templates/elements/air.txt create mode 100644 spec/support/templates/elements/fire.txt create mode 100644 spec/support/templates/elements/water.txt diff --git a/.gitignore b/.gitignore index b4c60c6..29955f4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ /doc/ /pkg/ /spec/reports/ -/spec/support/rendered/*.yml +/spec/support/rendered/* /tmp/ # rspec failure tracking diff --git a/lib/consult/template.rb b/lib/consult/template.rb index 05429e0..6c8d827 100644 --- a/lib/consult/template.rb +++ b/lib/consult/template.rb @@ -15,7 +15,7 @@ def initialize(name, config) end def render(save: true) - renderer = ERB.new(File.read(path, encoding: 'utf-8'), nil, '-') + renderer = ERB.new(contents, nil, '-') result = renderer.result(binding) File.open(dest, 'w') { |f| f << result } if save @@ -26,9 +26,19 @@ def render(save: true) end def path + return unless @config.key?(:path) resolve @config.fetch(:path) end + def paths + return [] unless @config.key?(:paths) + @config.fetch(:paths).map { |path| resolve(path) } + end + + def vars + @config[:vars] + end + def dest resolve @config.fetch(:dest) end @@ -42,5 +52,14 @@ def expired? return true if !config.key?(:ttl) || !dest.exist? dest.mtime < (Time.now - @config[:ttl].to_i) end + + private + + # Concatenate all the source templates together, in the order provided + def contents + [path, paths].compact.flatten.map do |file_path| + File.read file_path, encoding: 'utf-8' + end.join + end end end diff --git a/spec/consult_spec.rb b/spec/consult_spec.rb index feac664..8696dda 100644 --- a/spec/consult_spec.rb +++ b/spec/consult_spec.rb @@ -21,5 +21,10 @@ it 'renders without error' do expect { Consult.render! }.to_not raise_exception + + # Verify text templates rendered correctly + %w[elements.txt more_elements.txt].each do |template| + expect(FileUtils.compare_file("spec/support/expected/#{template}", "spec/support/rendered/#{template}")).to be true + end end end diff --git a/spec/support/config/consult.yml b/spec/support/config/consult.yml index b8b56a4..a71e677 100644 --- a/spec/support/config/consult.yml +++ b/spec/support/config/consult.yml @@ -14,6 +14,26 @@ shared: dest: rendered/database.yml ttl: 10 # seconds + elements: + paths: + - templates/elements/air.txt + - templates/elements/fire.txt + dest: rendered/elements.txt + vars: + air: 1 + fire: 2 + + more_elements: + path: templates/elements/air.txt + paths: + - templates/elements/fire.txt + - templates/elements/water.txt + dest: rendered/more_elements.txt + vars: + air: 1 + fire: 2 + water: 3 + test: templates: secrets: diff --git a/spec/support/expected/elements.txt b/spec/support/expected/elements.txt new file mode 100644 index 0000000..2dd21b4 --- /dev/null +++ b/spec/support/expected/elements.txt @@ -0,0 +1,2 @@ +Air is the 1st element +Fire is the 2nd element diff --git a/spec/support/expected/more_elements.txt b/spec/support/expected/more_elements.txt new file mode 100644 index 0000000..a7af96c --- /dev/null +++ b/spec/support/expected/more_elements.txt @@ -0,0 +1,3 @@ +Air is the 1st element +Fire is the 2nd element +Water is the 3rd element diff --git a/spec/support/templates/elements/air.txt b/spec/support/templates/elements/air.txt new file mode 100644 index 0000000..016dd2b --- /dev/null +++ b/spec/support/templates/elements/air.txt @@ -0,0 +1 @@ +Air is the <%= vars[:air] %>st element diff --git a/spec/support/templates/elements/fire.txt b/spec/support/templates/elements/fire.txt new file mode 100644 index 0000000..34227dc --- /dev/null +++ b/spec/support/templates/elements/fire.txt @@ -0,0 +1 @@ +Fire is the <%= vars[:fire] %>nd element diff --git a/spec/support/templates/elements/water.txt b/spec/support/templates/elements/water.txt new file mode 100644 index 0000000..7b9d40a --- /dev/null +++ b/spec/support/templates/elements/water.txt @@ -0,0 +1 @@ +Water is the <%= vars[:water] %>rd element From 0fdfc874e5f00d3df8aa9d4e31b60154715f5486 Mon Sep 17 00:00:00 2001 From: Michael B Martell Jr Date: Wed, 26 Sep 2018 13:01:32 -0400 Subject: [PATCH 2/4] Support multiple Consul keys as sources for a single template This will concatenate the contents of each Consul key together to form a single template, which will then be rendered once. This will also concatenate the contents of single or multiple files if present. Consul keys are concatenated after files. --- lib/consult/template.rb | 12 ++++++ spec/consult_spec.rb | 2 +- spec/support/config/consult.yml | 38 +++++++++++++++++++ spec/support/expected/consul_elements.txt | 2 + .../support/expected/more_consul_elements.txt | 3 ++ spec/support/expected/multi_pass.txt | 6 +++ spec/support/populate_consul.sh | 20 ++++++++++ 7 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 spec/support/expected/consul_elements.txt create mode 100644 spec/support/expected/more_consul_elements.txt create mode 100644 spec/support/expected/multi_pass.txt diff --git a/lib/consult/template.rb b/lib/consult/template.rb index 6c8d827..3674cc4 100644 --- a/lib/consult/template.rb +++ b/lib/consult/template.rb @@ -56,7 +56,19 @@ def expired? private # Concatenate all the source templates together, in the order provided + # Disk contents go first def contents + disk_contents + consul_contents + end + + def consul_contents + [@config[:consul_key], @config[:consul_keys]].compact.flatten.map do |key| + Diplomat::Kv.get(key, options: nil, not_found: :return, found: :return) + end.join + end + + # Concatenate all the source templates together, in the order provided + def disk_contents [path, paths].compact.flatten.map do |file_path| File.read file_path, encoding: 'utf-8' end.join diff --git a/spec/consult_spec.rb b/spec/consult_spec.rb index 8696dda..450e65c 100644 --- a/spec/consult_spec.rb +++ b/spec/consult_spec.rb @@ -23,7 +23,7 @@ expect { Consult.render! }.to_not raise_exception # Verify text templates rendered correctly - %w[elements.txt more_elements.txt].each do |template| + %w[elements.txt more_elements.txt consul_elements.txt more_consul_elements.txt multi_pass.txt].each do |template| expect(FileUtils.compare_file("spec/support/expected/#{template}", "spec/support/rendered/#{template}")).to be true end end diff --git a/spec/support/config/consult.yml b/spec/support/config/consult.yml index a71e677..5f81f6d 100644 --- a/spec/support/config/consult.yml +++ b/spec/support/config/consult.yml @@ -34,6 +34,44 @@ shared: fire: 2 water: 3 + consul_elements: + consul_keys: + - templates/elements/earth + - templates/elements/love + dest: rendered/consul_elements.txt + vars: + earth: 4 + love: 5 + + more_consul_elements: + consul_key: templates/elements/earth + consul_keys: + - templates/elements/love + - templates/elements/aziz + dest: rendered/more_consul_elements.txt + vars: + earth: 4 + love: 5 + aziz: 'Light!' + + multi_pass: + path: templates/elements/air.txt + paths: + - templates/elements/fire.txt + - templates/elements/water.txt + consul_key: templates/elements/earth + consul_keys: + - templates/elements/love + - templates/elements/aziz + dest: rendered/multi_pass.txt + vars: + air: 1 + fire: 2 + water: 3 + earth: 4 + love: 5 + aziz: 'Light!' + test: templates: secrets: diff --git a/spec/support/expected/consul_elements.txt b/spec/support/expected/consul_elements.txt new file mode 100644 index 0000000..707f8e8 --- /dev/null +++ b/spec/support/expected/consul_elements.txt @@ -0,0 +1,2 @@ +Earth is the 4th element +Love is the 5th element! diff --git a/spec/support/expected/more_consul_elements.txt b/spec/support/expected/more_consul_elements.txt new file mode 100644 index 0000000..18f796f --- /dev/null +++ b/spec/support/expected/more_consul_elements.txt @@ -0,0 +1,3 @@ +Earth is the 4th element +Love is the 5th element! +Aziz! Light! diff --git a/spec/support/expected/multi_pass.txt b/spec/support/expected/multi_pass.txt new file mode 100644 index 0000000..0a10480 --- /dev/null +++ b/spec/support/expected/multi_pass.txt @@ -0,0 +1,6 @@ +Air is the 1st element +Fire is the 2nd element +Water is the 3rd element +Earth is the 4th element +Love is the 5th element! +Aziz! Light! diff --git a/spec/support/populate_consul.sh b/spec/support/populate_consul.sh index 372ba52..3270784 100755 --- a/spec/support/populate_consul.sh +++ b/spec/support/populate_consul.sh @@ -13,3 +13,23 @@ curl \ --request PUT \ --data 'db1.local.net' \ http://0.0.0.0:8500/v1/kv/infrastructure/db1/dns + +curl \ + --request PUT \ + --data 'db1.local.net' \ + http://0.0.0.0:8500/v1/kv/infrastructure/db1/dns + +curl \ + --request PUT \ + --data $'Earth is the <%= vars[:earth] %>th element\n' \ + http://0.0.0.0:8500/v1/kv/templates/elements/earth + +curl \ + --request PUT \ + --data $'Love is the <%= vars[:love] %>th element!\n' \ + http://0.0.0.0:8500/v1/kv/templates/elements/love + +curl \ + --request PUT \ + --data $'Aziz! <%= vars[:aziz] %>\n' \ + http://0.0.0.0:8500/v1/kv/templates/elements/aziz From 229e135ab0e638a25c8140e88576b232f6187081 Mon Sep 17 00:00:00 2001 From: Michael B Martell Jr Date: Wed, 26 Sep 2018 13:14:54 -0400 Subject: [PATCH 3/4] Remove redundant Consul provisioning step --- spec/support/populate_consul.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/spec/support/populate_consul.sh b/spec/support/populate_consul.sh index 3270784..a8b7c22 100755 --- a/spec/support/populate_consul.sh +++ b/spec/support/populate_consul.sh @@ -14,11 +14,6 @@ curl \ --data 'db1.local.net' \ http://0.0.0.0:8500/v1/kv/infrastructure/db1/dns -curl \ - --request PUT \ - --data 'db1.local.net' \ - http://0.0.0.0:8500/v1/kv/infrastructure/db1/dns - curl \ --request PUT \ --data $'Earth is the <%= vars[:earth] %>th element\n' \ From 782fadb6115d8fe84dd400dd01b3fff6d77089e0 Mon Sep 17 00:00:00 2001 From: Michael B Martell Jr Date: Fri, 28 Sep 2018 13:14:37 -0400 Subject: [PATCH 4/4] Support custom error handlers. --- lib/consult.rb | 5 +++++ lib/consult/template.rb | 5 +++-- spec/lib/template_spec.rb | 32 ++++++++++++++++++++++++++++++++ spec/support/config/consult.yml | 8 +++++++- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/lib/consult.rb b/lib/consult.rb index 96732aa..2a10907 100644 --- a/lib/consult.rb +++ b/lib/consult.rb @@ -19,6 +19,7 @@ module Consult class << self attr_reader :config, :templates + attr_writer :exception_handler def load(config_dir: nil) root directory: config_dir @@ -78,6 +79,10 @@ def render! active_templates.each(&:render) end + def exception_handler + @exception_handler ||= ->(e) { puts e.message } + end + # Map more conventional `token` parameter to Diplomat's `acl_token` configuration. # Additionally, we support ~/.consul-token, similar to Vault's support for ~/.vault-token def consul_token diff --git a/lib/consult/template.rb b/lib/consult/template.rb index 3674cc4..477e510 100644 --- a/lib/consult/template.rb +++ b/lib/consult/template.rb @@ -15,14 +15,15 @@ def initialize(name, config) end def render(save: true) + # Attempt to render renderer = ERB.new(contents, nil, '-') result = renderer.result(binding) File.open(dest, 'w') { |f| f << result } if save result rescue StandardError => e - puts "Error rendering template: #{name}" - raise e + Consult.exception_handler.call(e) + nil end def path diff --git a/spec/lib/template_spec.rb b/spec/lib/template_spec.rb index efb1d71..9095ce6 100644 --- a/spec/lib/template_spec.rb +++ b/spec/lib/template_spec.rb @@ -1,5 +1,14 @@ # frozen_string_literal: true +class Handler + class << self + attr_reader :error + def call(error) + @error = error + end + end +end + RSpec.describe Consult::Template do let(:name) { 'database.yml' } let(:config) do @@ -11,6 +20,14 @@ end let(:template) { Consult::Template.new(name, config) } + let(:error_template) { 'Corbin Dallas' } + let(:error_config) do + { + consul_key: 'templates/error_test', + dest: 'rendered/error_test.txt' + } + end + before :all do Consult.load config_dir: 'spec/support' end @@ -75,4 +92,19 @@ expect(template.indent(colon_separated, 1, ':')).to eq ' hello: world' end end + + context 'error handling' do + it 'allows custom error handlers' do + Consult.exception_handler = Handler + Diplomat::Kv.put('templates/error_test', error_template) + template = Consult::Template.new('error_template', error_config) + expect(template.render).to eq error_template + + Diplomat::Kv.delete('templates/error_test') + expect(template.render).to be nil + expect(Handler.error).to be_instance_of Diplomat::KeyNotFound + + expect(File.read(template.dest)).to eq error_template + end + end end diff --git a/spec/support/config/consult.yml b/spec/support/config/consult.yml index 5f81f6d..3d08441 100644 --- a/spec/support/config/consult.yml +++ b/spec/support/config/consult.yml @@ -53,7 +53,7 @@ shared: earth: 4 love: 5 aziz: 'Light!' - + multi_pass: path: templates/elements/air.txt paths: @@ -72,6 +72,12 @@ shared: love: 5 aziz: 'Light!' + dest_fail: + consul_key: templates/elements/aziz + dest: rendered/nope/dest_fail.keep + vars: + aziz: 'Light!' + test: templates: secrets: