From 9e662bd63ca684b3001c2a215149966b2a10fb04 Mon Sep 17 00:00:00 2001 From: Matthias Baur Date: Sat, 25 Jan 2020 23:59:56 +0100 Subject: [PATCH] Allow deferred for private_key --- README.md | 214 +++++++++++++++++- files/interface.conf.epp | 84 +++++++ lib/puppet/functions/wireguard/genkey.rb | 34 +-- .../functions/wireguard/genprivatekey.rb | 33 +++ lib/puppet/functions/wireguard/genpsk.rb | 2 +- .../functions/wireguard/genpublickey.rb | 32 +++ manifests/interface.pp | 43 +++- spec/functions/genkey_spec.rb | 57 +++++ spec/functions/genprivatekey_spec.rb | 42 ++++ spec/functions/genpublickey_spec.rb | 43 ++++ spec/spec_helper.rb | 1 + templates/interface.conf.erb | 72 ------ 12 files changed, 552 insertions(+), 105 deletions(-) create mode 100644 files/interface.conf.epp create mode 100644 lib/puppet/functions/wireguard/genprivatekey.rb create mode 100644 lib/puppet/functions/wireguard/genpublickey.rb create mode 100644 spec/functions/genkey_spec.rb create mode 100644 spec/functions/genprivatekey_spec.rb create mode 100644 spec/functions/genpublickey_spec.rb delete mode 100644 templates/interface.conf.erb diff --git a/README.md b/README.md index ec312fe..728c4ee 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,13 @@ secure point-to-point connections in routed or bridged configurations. * [`wireguard::interface`](#wireguardinterface): Defines wireguard tunnel interfaces +**Functions** + +* [`wireguard::genkey`](#wireguardgenkey): Returns an array containing the wireguard private and public (in this order) key for a certain interface. +* [`wireguard::genprivatekey`](#wireguardgenprivatekey): Returns the private key. Will be generated and saved to disk if it doesn't already exist. +* [`wireguard::genpsk`](#wireguardgenpsk): Returns string containing the wireguard psk for a certain interface. +* [`wireguard::genpublickey`](#wireguardgenpublickey): Returns a public key derived from a private key. Will be generated and saved to disk if it doesn't already exist. + ## Classes ### wireguard @@ -95,6 +102,14 @@ Define wireguard interfaces Default value: {} +##### `config_dir_purge` + +Data type: `Boolean` + + + +Default value: $wireguard::params::config_dir_purge + ### wireguard::config Class configures files and directories for wireguard @@ -115,6 +130,12 @@ Data type: `String` The config_dir access mode bits +##### `config_dir_purge` + +Data type: `Boolean` + + + ### wireguard::install Class installs wireguard packages and sets yum repository @@ -169,7 +190,7 @@ The following parameters are available in the `wireguard::interface` defined typ ##### `private_key` -Data type: `String` +Data type: `Any` Private key for data encryption @@ -193,6 +214,8 @@ Data type: `Optional[Variant[Array,String]]` List of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned to the interface. +Data type isn't 100% correct but needs to be 'Any' to allow 'Deferred' +on Puppet 6 systems. epp will enforce Optional[Variant[Array,String]]. Default value: `undef` @@ -282,3 +305,192 @@ Data type: `Optional[Variant[Array,String]]` Default value: `undef` + +## Functions + +### wireguard::genkey + +Type: Ruby 4.x API + +Returns an array containing the wireguard private and public (in this order) key for a certain interface. + +#### Examples + +##### Creating private and public key for the interface wg0. + +```puppet +wireguard::genkey('wg0', '/etc/wireguard') => [ + '2N0YBID3tnptapO/V5x3GG78KloA8xkLz1QtX6OVRW8=', + 'Pz4sRKhRMSet7IYVXXeZrAguBSs+q8oAVMfAAXHJ7S8=', +] +``` + +#### `wireguard::genkey(String $name, Optional[String] $path)` + +Returns an array containing the wireguard private and public (in this order) key for a certain interface. + +Returns: `Array` Returns [$private_key, $public_key]. + +##### Examples + +###### Creating private and public key for the interface wg0. + +```puppet +wireguard::genkey('wg0', '/etc/wireguard') => [ + '2N0YBID3tnptapO/V5x3GG78KloA8xkLz1QtX6OVRW8=', + 'Pz4sRKhRMSet7IYVXXeZrAguBSs+q8oAVMfAAXHJ7S8=', +] +``` + +##### `name` + +Data type: `String` + +The interface name. + +##### `path` + +Data type: `Optional[String]` + +Absolut path to the wireguard key files (default '/etc/wireguard'). + +### wireguard::genprivatekey + +Type: Ruby 4.x API + +Returns the private key. Will be generated and saved to disk if it doesn't already exist. + +#### Examples + +##### Creating private key for the interface wg0. + +```puppet +wireguard::genprivatekey('/etc/wireguard/wg0.key') => '2N0YBID3tnptapO/V5x3GG78KloA8xkLz1QtX6OVRW8=' +``` + +##### Using it as a Deferred function + +```puppet +include wireguard +wireguard::interface { 'wg0': + private_key => Deferred('wireguard::genprivatekey', ['/etc/wireguard/wg0.key']), + listen_port => 53098, +} +``` + +#### `wireguard::genprivatekey(String $path)` + +Returns the private key. Will be generated and saved to disk if it doesn't already exist. + +Returns: `String` Returns the private key. + +##### Examples + +###### Creating private key for the interface wg0. + +```puppet +wireguard::genprivatekey('/etc/wireguard/wg0.key') => '2N0YBID3tnptapO/V5x3GG78KloA8xkLz1QtX6OVRW8=' +``` + +###### Using it as a Deferred function + +```puppet +include wireguard +wireguard::interface { 'wg0': + private_key => Deferred('wireguard::genprivatekey', ['/etc/wireguard/wg0.key']), + listen_port => 53098, +} +``` + +##### `path` + +Data type: `String` + +Absolut path to the private key + +### wireguard::genpsk + +Type: Ruby 4.x API + +Returns string containing the wireguard psk for a certain interface. + +#### Examples + +##### Creating psk for the interface wg0. + +```puppet +wireguard::genpsk('wg0') => 'FIVuvMyHvzujQweYa+oJdLDRvrpbHBithvMmNjN5rK4=' +``` + +#### `wireguard::genpsk(String $name, Optional[String] $path)` + +Returns string containing the wireguard psk for a certain interface. + +Returns: `String` Returns psk. + +##### Examples + +###### Creating psk for the interface wg0. + +```puppet +wireguard::genpsk('wg0') => 'FIVuvMyHvzujQweYa+oJdLDRvrpbHBithvMmNjN5rK4=' +``` + +##### `name` + +Data type: `String` + +The interface name. + +##### `path` + +Data type: `Optional[String]` + +Absolut path to the wireguard key files (default '/etc/wireguard'). + +### wireguard::genpublickey + +Type: Ruby 4.x API + +Returns a public key derived from a private key. +Will be generated and saved to disk if it doesn't already exist. + +#### Examples + +##### Creating public key for the interface wg0. + +```puppet +wireguard::genpublickey('/etc/wireguard/wg0.key', + '/etc/wireguard/wg0.pub' + ) => 'gNaMjIpR7LKg019iktKJC74GX/MD3Y35Wo+WRNRQZxA=' +``` + +#### `wireguard::genpublickey(String $private_key_path, String $public_key_path)` + +Returns a public key derived from a private key. +Will be generated and saved to disk if it doesn't already exist. + +Returns: `String` Returns the public key. + +##### Examples + +###### Creating public key for the interface wg0. + +```puppet +wireguard::genpublickey('/etc/wireguard/wg0.key', + '/etc/wireguard/wg0.pub' + ) => 'gNaMjIpR7LKg019iktKJC74GX/MD3Y35Wo+WRNRQZxA=' +``` + +##### `private_key_path` + +Data type: `String` + +Absolut path to the private key + +##### `public_key_path` + +Data type: `String` + +Absolut path to the public key + diff --git a/files/interface.conf.epp b/files/interface.conf.epp new file mode 100644 index 0000000..b0227b5 --- /dev/null +++ b/files/interface.conf.epp @@ -0,0 +1,84 @@ +<%- | Optional[Variant[Array,String]] $address, + Boolean $saveconfig, + String $private_key, + Integer[1,65535] $listen_port, + Optional[Integer[1,9202]] $mtu, + Optional[String] $dns, + Optional[Variant[Array,String]] $preup, + Optional[Variant[Array,String]] $postup, + Optional[Variant[Array,String]] $predown, + Optional[Variant[Array,String]] $postdown, + Array $peers, +| -%> +# This file is managed by puppet +[Interface] +<%- if $address { -%> + <%- if $address =~ Array { -%> + <%- $address.flatten.each |$adr| { -%> +Address = <%= $adr %> + <%- } -%> + <%- } else {-%> +Address = <%= $address %> + <%- } -%> +<%- } -%> +<% if $saveconfig { -%> +SaveConfig = true +<% } -%> +PrivateKey = <%= $private_key %> +ListenPort = <%= $listen_port %> +<%- if $mtu { -%> +MTU = <%= $mtu %> +<% } -%> +<%- if $dns { -%> +DNS = <%= $dns %> +<% } -%> +<%- if $preup { -%> + <%- if $preup =~ Array { -%> + <%- $preup.flatten.each |$p| { -%> +PreUp = <%= $p %> + <%- } -%> + <%- } else { -%> +PreUp = <%= $preup %> + <%- } -%> +<%- } -%> +<%- if $postup { -%> + <%- if $postup =~ Array { -%> + <%- $postup.flatten.each |$p| { -%> +PostUp = <%= $p %> + <%- } -%> + <%- } else { -%> +PostUp = <%= $postup %> + <%- } -%> +<%- } -%> +<%- if $predown { -%> + <%- if $predown =~ Array { -%> + <%- $predown.flatten.each |$p| { -%> +PreDown = <%= $p %> + <%- } -%> + <%- } else { -%> +PreDown = <%= $predown %> + <%- } -%> +<%- } -%> +<%- if $postdown { -%> + <%- if $postdown =~ Array { -%> + <%- $postdown.flatten.each |$p| { -%> +PostDown = <%= $p %> + <%- } -%> + <%- } else { -%> +PostDown = <%= $postdown %> + <%- } -%> +<%- } -%> +<%- if $peers { -%> + +# Peers + <%- $peers.each |$peer| { -%> +[Peer] + <%- $peer.each |$key,$value| { -%> + <%- if $key == 'Comment' { -%> +# <%= $value -%> + <%- } else { -%> +<%= $key %> = <%= $value -%> + <%- } %> + <%- } %> + <%- } -%> +<%- } -%> diff --git a/lib/puppet/functions/wireguard/genkey.rb b/lib/puppet/functions/wireguard/genkey.rb index c384984..2e669e1 100644 --- a/lib/puppet/functions/wireguard/genkey.rb +++ b/lib/puppet/functions/wireguard/genkey.rb @@ -1,6 +1,5 @@ +# Returns an array containing the wireguard private and public (in this order) key for a certain interface. Puppet::Functions.create_function(:'wireguard::genkey') do - # Returns an array containing the wireguard private and public (in this order) key - # for a certain interface. # @param name The interface name. # @param path Absolut path to the wireguard key files (default '/etc/wireguard'). # @return [Array] Returns [$private_key, $public_key]. @@ -11,33 +10,10 @@ # ] dispatch :genkey do required_param 'String', :name + optional_param 'String', :path return_type 'Array' end - def gen_privkey(private_key_path, public_key_path) - unless File.exists?(private_key_path) - private_key = Puppet::Util::Execution.execute( - ['/usr/bin/wg', 'genkey'], - ) - File.open(private_key_path, 'w') do |f| - f << private_key - end - File.delete(public_key_path) if File.exist?(public_key_path) - end - end - - def gen_pubkey(private_key_path, public_key_path) - unless File.exists?(public_key_path) - public_key = Puppet::Util::Execution.execute( - ['/usr/bin/wg', 'pubkey'], - {:stdinfile => private_key_path}, - ) - File.open(public_key_path, 'w') do |f| - f << public_key - end - end - end - def genkey(name, path='/etc/wireguard') private_key_path = File.join(path, "#{name}.key") public_key_path = File.join(path, "#{name}.pub") @@ -47,9 +23,9 @@ def genkey(name, path='/etc/wireguard') raise Puppet::ParseError, "#{dir} is not writable" if not File.writable?(dir) end - gen_privkey(private_key_path, public_key_path) - gen_pubkey(private_key_path, public_key_path) - [File.read(private_key_path),File.read(public_key_path)] + private_key = call_function('wireguard::genprivatekey', private_key_path) + public_key = call_function('wireguard::genpublickey', private_key_path, public_key_path) + [private_key, public_key] end end diff --git a/lib/puppet/functions/wireguard/genprivatekey.rb b/lib/puppet/functions/wireguard/genprivatekey.rb new file mode 100644 index 0000000..48815be --- /dev/null +++ b/lib/puppet/functions/wireguard/genprivatekey.rb @@ -0,0 +1,33 @@ +# Returns the private key. Will be generated and saved to disk if it doesn't already exist. +Puppet::Functions.create_function(:'wireguard::genprivatekey') do + # @param path Absolut path to the private key + # @return [String] Returns the private key. + # + # @example Creating private key for the interface wg0. + # wireguard::genprivatekey('/etc/wireguard/wg0.key') => '2N0YBID3tnptapO/V5x3GG78KloA8xkLz1QtX6OVRW8=' + # + # @example Using it as a Deferred function + # include wireguard + # wireguard::interface { 'wg0': + # private_key => Deferred('wireguard::genprivatekey', ['/etc/wireguard/wg0.key']), + # listen_port => 53098, + # } + # + dispatch :genprivatekey do + required_param 'String', :path + return_type 'String' + end + + def genprivatekey(path) + if File.exists?(path) + private_key = File.read(path).strip + else + private_key = Puppet::Util::Execution.execute( + ['/usr/bin/wg', 'genkey'], + ) + File.write(path, private_key) + end + + private_key + end +end diff --git a/lib/puppet/functions/wireguard/genpsk.rb b/lib/puppet/functions/wireguard/genpsk.rb index 0e9df88..8686f27 100644 --- a/lib/puppet/functions/wireguard/genpsk.rb +++ b/lib/puppet/functions/wireguard/genpsk.rb @@ -1,5 +1,5 @@ +# Returns string containing the wireguard psk for a certain interface. Puppet::Functions.create_function(:'wireguard::genpsk') do - # Returns string containing the wireguard psk for a certain interface. # @param name The interface name. # @param path Absolut path to the wireguard key files (default '/etc/wireguard'). # @return [String] Returns psk. diff --git a/lib/puppet/functions/wireguard/genpublickey.rb b/lib/puppet/functions/wireguard/genpublickey.rb new file mode 100644 index 0000000..a433c85 --- /dev/null +++ b/lib/puppet/functions/wireguard/genpublickey.rb @@ -0,0 +1,32 @@ +# Returns a public key derived from a private key. +# Will be generated and saved to disk if it doesn't already exist. +Puppet::Functions.create_function(:'wireguard::genpublickey') do + # @param private_key_path Absolut path to the private key + # @param public_key_path Absolut path to the public key + # @return [String] Returns the public key. + # + # @example Creating public key for the interface wg0. + # wireguard::genpublickey('/etc/wireguard/wg0.key', + # '/etc/wireguard/wg0.pub' + # ) => 'gNaMjIpR7LKg019iktKJC74GX/MD3Y35Wo+WRNRQZxA=' + # + dispatch :genprivkey do + required_param 'String', :private_key_path + required_param 'String', :public_key_path + return_type 'String' + end + + def genprivkey(private_key_path, public_key_path) + if File.exists?(public_key_path) + public_key = File.read(public_key_path).strip + else + public_key = Puppet::Util::Execution.execute( + ['/usr/bin/wg', 'pubkey'], + {:stdinfile => private_key_path}, + ) + File.write(public_key_path, public_key) + end + + public_key + end +end diff --git a/manifests/interface.pp b/manifests/interface.pp index a51522f..dff49b4 100644 --- a/manifests/interface.pp +++ b/manifests/interface.pp @@ -9,6 +9,8 @@ # @param address # List of IP (v4 or v6) addresses (optionally with CIDR masks) to # be assigned to the interface. +# Data type isn't 100% correct but needs to be 'Any' to allow 'Deferred' +# on Puppet 6 systems. epp will enforce Optional[Variant[Array,String]]. # @param mtu # Set MTU for the wireguard interface # @param preup @@ -28,7 +30,7 @@ # @param config_dir # Path to wireguard configuration files define wireguard::interface ( - String $private_key, + Any $private_key, Integer[1,65535] $listen_port, Enum['present','absent'] $ensure = 'present', Optional[Variant[Array,String]] $address = undef, @@ -51,6 +53,43 @@ Boolean $saveconfig = true, Stdlib::Absolutepath $config_dir = '/etc/wireguard', ) { + $config = { + address => $address, + saveconfig => $saveconfig, + private_key => $private_key, + listen_port => $listen_port, + mtu => $mtu, + dns => $dns, + preup => $preup, + postup => $postup, + predown => $predown, + postdown => $postdown, + peers => $peers, + } + + # $serverversion is empty on 'puppet apply' runs. Just use clientversion. + $_serverversion = getvar('serverversion') ? { + undef => $clientversion, + default => $serverversion, + } + + # We explicitly put the template in the files directory to be able + # use it in a Deferred function. This can later be changed into the + # 'find_template' function which was introduced with Puppet 6.12.0. + # At time of writing there was no Puppet Agent 6.12 release. + + # Puppet < 6 doesn't include the Deferred type and will therefore + # fail with an compilation error while trying to load the type + if versioncmp($clientversion, '6.0') >= 0 and versioncmp($_serverversion, '6.0') >= 0 { + if $private_key =~ Deferred { + $content = Deferred('inline_epp', [file("${module_name}/interface.conf.epp"), $config]) + } else { + assert_type(String[1], $private_key) + $content = inline_epp(file("${module_name}/interface.conf.epp"), $config) + } + } else { + $content = inline_epp(file("${module_name}/interface.conf.epp"), $config) + } file {"${config_dir}/${name}.conf": ensure => $ensure, @@ -58,7 +97,7 @@ owner => 'root', group => 'root', show_diff => false, - content => template("${module_name}/interface.conf.erb"), + content => $content, notify => Service["wg-quick@${name}.service"], } diff --git a/spec/functions/genkey_spec.rb b/spec/functions/genkey_spec.rb new file mode 100644 index 0000000..c374b93 --- /dev/null +++ b/spec/functions/genkey_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe 'wireguard::genkey' do + let(:name) { 'wg0' } + let(:private_key_path) { '/etc/wireguard/wg0.key' } + let(:public_key_path) { '/etc/wireguard/wg0.pub' } + let(:privatekey) { 'privatekey' } + let(:publickey) { 'publickey' } + + + context 'fails on invalid params' do + it { is_expected.not_to eq(nil) } + [ + nil, + 1, + true, + false, + [], + {}, + ].each do |value| + it { is_expected.to run.with_params(value).and_raise_error(ArgumentError) } + end + end + + context 'generates a private and public key' do + before do + allow(File).to receive(:writable?).with('/etc/wireguard').and_return(true) + + # Privatekey + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/wg', 'genkey']).and_return(privatekey) + allow(File).to receive(:write).with(private_key_path, privatekey) + + # Publickey + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/wg', 'pubkey'], {:stdinfile => private_key_path}).and_return(publickey) + allow(File).to receive(:write).with(public_key_path, publickey) + end + + it { is_expected.to run.with_params(name).and_return([privatekey, publickey]) } + end + + context 'uses existing private and public key file' do + before do + allow(File).to receive(:writable?).with('/etc/wireguard').and_return(true) + allow(File).to receive(:read).and_call_original + + # Private Key + allow(File).to receive(:exists?).with(private_key_path).and_return(true) + allow(File).to receive(:read).with(private_key_path).and_return(privatekey) + + # Public Key + allow(File).to receive(:exists?).with(public_key_path).and_return(true) + allow(File).to receive(:read).with(public_key_path).and_return(publickey) + end + + it { is_expected.to run.with_params(name).and_return([privatekey, publickey]) } + end +end diff --git a/spec/functions/genprivatekey_spec.rb b/spec/functions/genprivatekey_spec.rb new file mode 100644 index 0000000..651ccda --- /dev/null +++ b/spec/functions/genprivatekey_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe 'wireguard::genprivatekey' do + context 'fails on invalid params' do + it { is_expected.not_to eq(nil) } + [ + nil, + 1, + true, + false, + [], + {}, + ].each do |value| + it { is_expected.to run.with_params(value).and_raise_error(ArgumentError) } + end + end + + context 'generates a private key' do + let(:filename) { '/etc/wireguard/wg0.key' } + let(:privatekey) { '1234567890abcdef' } + + before do + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/wg', 'genkey']).and_return(privatekey) + allow(File).to receive(:write).with(filename, privatekey) + end + + it { is_expected.to run.with_params(filename).and_return(privatekey) } + end + + context 'uses existing private key file' do + let(:filename) { '/etc/wireguard/wg0.key' } + let(:privatekey) { 'abcdef1234567890' } + + before do + allow(File).to receive(:exists?).with(filename).and_return(true) + allow(File).to receive(:read).and_call_original + allow(File).to receive(:read).with(filename).and_return(privatekey) + end + + it { is_expected.to run.with_params(filename).and_return(privatekey) } + end +end diff --git a/spec/functions/genpublickey_spec.rb b/spec/functions/genpublickey_spec.rb new file mode 100644 index 0000000..658701e --- /dev/null +++ b/spec/functions/genpublickey_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe 'wireguard::genpublickey' do + let(:private_key_path) { '/etc/wireguard/wg0.key' } + let(:public_key_path) { '/etc/wireguard/wg0.pub' } + + context 'fails on invalid params' do + it { is_expected.not_to eq(nil) } + [ + nil, + 1, + true, + false, + [], + {}, + ].each do |value| + it { is_expected.to run.with_params(value).and_raise_error(ArgumentError) } + end + end + + context 'generates a public key' do + let(:publickey) { '1234567890abcdef' } + + before do + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/wg', 'pubkey'], {:stdinfile => private_key_path}).and_return(publickey) + allow(File).to receive(:write).with(public_key_path, publickey) + end + + it { is_expected.to run.with_params(private_key_path, public_key_path).and_return(publickey) } + end + + context 'uses existing public key file' do + let(:publickey) { 'abcdef1234567890' } + + before do + allow(File).to receive(:exists?).with(public_key_path).and_return(true) + allow(File).to receive(:read).and_call_original + allow(File).to receive(:read).with(public_key_path).and_return(publickey) + end + + it { is_expected.to run.with_params(private_key_path, public_key_path).and_return(publickey) } + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 990e03f..df83721 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -29,4 +29,5 @@ end c.hiera_config = File.expand_path(File.join(__FILE__, '../fixtures/hiera.yaml')) + c.mock_framework = :rspec end diff --git a/templates/interface.conf.erb b/templates/interface.conf.erb deleted file mode 100644 index 261084b..0000000 --- a/templates/interface.conf.erb +++ /dev/null @@ -1,72 +0,0 @@ -# This file is managed by puppet -[Interface] -<%- if @address -%> - <%- if @address.is_a? Array -%> - <%- @address.flatten.each do |adr| -%> -Address = <%= adr %> - <%- end -%> - <%- else -%> -Address = <%= @address %> - <%- end -%> -<%- end -%> -<% if @saveconfig -%> -SaveConfig = true -<% end -%> -PrivateKey = <%= @private_key %> -ListenPort = <%= @listen_port %> -<%- if @mtu -%> -MTU = <%= @mtu %> -<% end -%> -<%- if @dns -%> -DNS = <%= @dns %> -<% end -%> -<%- if @preup -%> - <%- if @preup.is_a? Array -%> - <%- @preup.flatten.each do |p| -%> -PreUp = <%= p %> - <%- end -%> - <%- else -%> -PreUp = <%= @preup %> - <%- end -%> -<%- end -%> -<%- if @postup -%> - <%- if @postup.is_a? Array -%> - <%- @postup.flatten.each do |p| -%> -PostUp = <%= p %> - <%- end -%> - <%- else -%> -PostUp = <%= @postup %> - <%- end -%> -<%- end -%> -<%- if @predown -%> - <%- if @predown.is_a? Array -%> - <%- @predown.flatten.each do |p| -%> -PreDown = <%= p %> - <%- end -%> - <%- else -%> -PreDown = <%= @predown %> - <%- end -%> -<%- end -%> -<%- if @postdown -%> - <%- if @postdown.is_a? Array -%> - <%- @postdown.flatten.each do |p| -%> -PostDown = <%= p %> - <%- end -%> - <%- else -%> -PostDown = <%= @postdown %> - <%- end -%> -<%- end -%> -<%- if @peers -%> - -# Peers - <%- @peers.each do |peer| -%> -[Peer] - <%- peer.each do |key,value| -%> - <%- if key == 'Comment' -%> -# <%= value %> - <%- else -%> -<%= key %> = <%= value %> - <%- end -%> - <%- end %> - <%- end -%> -<%- end -%>