aboutsummaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorJosh Cooper <josh@puppet.com>2018-07-13 09:38:02 -0700
committerGitHub <noreply@github.com>2018-07-13 09:38:02 -0700
commitbd20d920a19e9b011dfd027a802985cea46ae04f (patch)
treef882cd2b7c31ae08cd242882b0f7f1d1ed89cbbd /spec
parent9d96ddecea6367abee1aa77859848b0c158fca80 (diff)
parentc957642d70f4b778736a9b49cf1ef9702e42c4f1 (diff)
downloadpuppet-hosts_core-bd20d920a19e9b011dfd027a802985cea46ae04f.tar.gz
puppet-hosts_core-bd20d920a19e9b011dfd027a802985cea46ae04f.tar.bz2
Merge pull request #1 from puppetlabs/extraction
Initial host module extraction
Diffstat (limited to 'spec')
-rw-r--r--spec/acceptance/nodesets/default.yml13
-rw-r--r--spec/acceptance/tests/create_spec.rb56
-rw-r--r--spec/acceptance/tests/destroy_spec.rb36
-rw-r--r--spec/acceptance/tests/modify_spec.rb35
-rw-r--r--spec/acceptance/tests/query_all_spec.rb37
-rw-r--r--spec/default_facts.yml8
-rw-r--r--spec/fixtures/unit/provider/host/parsed/valid_hosts19
-rw-r--r--spec/lib/puppet_spec/files.rb44
-rw-r--r--spec/shared_behaviours/all_parsedfile_providers.rb21
-rw-r--r--spec/spec_helper.rb45
-rw-r--r--spec/spec_helper_acceptance.rb13
-rw-r--r--spec/spec_helper_local.rb19
-rw-r--r--spec/unit/provider/host/parsed_spec.rb210
-rw-r--r--spec/unit/type/host_spec.rb665
14 files changed, 1221 insertions, 0 deletions
diff --git a/spec/acceptance/nodesets/default.yml b/spec/acceptance/nodesets/default.yml
new file mode 100644
index 0000000..6323005
--- /dev/null
+++ b/spec/acceptance/nodesets/default.yml
@@ -0,0 +1,13 @@
+---
+HOSTS:
+ centos7-64-1:
+ hypervisor: vmpooler
+ platform: el-7-x86_64
+ packaging_platform: el-7-x86_64
+ template: centos-7-x86_64
+ roles:
+ - agent
+ - database
+CONFIG:
+ type: agent
+ pooling_api: http://vmpooler.delivery.puppetlabs.net/
diff --git a/spec/acceptance/tests/create_spec.rb b/spec/acceptance/tests/create_spec.rb
new file mode 100644
index 0000000..40267fd
--- /dev/null
+++ b/spec/acceptance/tests/create_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper_acceptance'
+
+RSpec.context 'when creating host files' do
+ agents.each do |agent|
+ context "on #{agent}" do
+ let(:target) { agent.tmpfile('host-create') }
+
+ after(:each) do
+ on(agent, "test #{target} && rm -f #{target}")
+ end
+
+ it 'creates a host record' do
+ on(agent, puppet_resource('host', 'test', 'ensure=present',
+ 'ip=127.0.0.1', "target=#{target}"))
+ on(agent, "cat #{target}") do |result|
+ fail_test 'record was not present' if result.stdout !~ %r{^127\.0\.0\.1[[:space:]]+test}
+ end
+ end
+
+ it 'creates host aliases' do
+ on(agent, puppet_resource('host', 'test', 'ensure=present',
+ 'ip=127.0.0.7', "target=#{target}", 'host_aliases=alias'))
+
+ on(agent, "cat #{target}") do |result|
+ fail_test 'alias was missing' unless
+ result.stdout =~ %r{^127\.0\.0\.7[[:space:]]+test[[:space:]]alias}
+ end
+ end
+
+ it "doesn't create the entry if it already exists" do
+ on agent, "printf '127.0.0.2 test alias\n' > #{target}"
+
+ step 'tell puppet to ensure the host exists'
+ on(agent, puppet_resource('host', 'test', "target=#{target}",
+ 'ensure=present', 'ip=127.0.0.2', 'host_aliases=alias')) do |result|
+ fail_test 'darn, we created the host record' if
+ result.stdout.include? '/Host[test1]/ensure: created'
+ end
+ end
+
+ it 'requires an ipaddress' do
+ skip_test if agent['locale'] == 'ja'
+
+ on(agent, puppet_resource('host', 'test', "target=#{target}",
+ 'host_aliases=alias')) do |result|
+ fail_test "puppet didn't complain about the missing attribute" unless
+ result.stderr.include? 'ip is a required attribute for hosts'
+ end
+
+ on(agent, "cat #{target}") do |result|
+ fail_test 'the host was apparently added to the file' if result.stdout.include? 'test'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/acceptance/tests/destroy_spec.rb b/spec/acceptance/tests/destroy_spec.rb
new file mode 100644
index 0000000..b00e03a
--- /dev/null
+++ b/spec/acceptance/tests/destroy_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper_acceptance'
+
+RSpec.context 'when managing host files' do
+ agents.each do |agent|
+ context "on #{agent}" do
+ let(:target) { agent.tmpfile('host-destroy') }
+
+ after(:each) do
+ on(agent, "test #{target} && rm -f #{target}")
+ end
+
+ it 'deletes a host record' do
+ line = '127.0.0.7 test1'
+
+ on agent, "printf '#{line}\n' > #{target}"
+ on(agent, puppet_resource('host', 'test1', "target=#{target}",
+ 'ensure=absent', 'ip=127.0.0.7'))
+ on(agent, "cat #{target}") do |result|
+ fail_test 'the content was still present' if result.stdout.include? line
+ end
+ end
+
+ it 'does not purge valid host records if file contains malformed content' do
+ on(agent, "printf '127.0.0.2 existing alias\n' > #{target}")
+ on(agent, "printf '==\n' >> #{target}")
+
+ on(agent, puppet_resource('host', 'test', "target=#{target}",
+ 'ensure=present', 'ip=127.0.0.3', 'host_aliases=foo'))
+
+ on(agent, "cat #{target}") do |result|
+ fail_test 'existing host data was deleted' unless result.stdout.include? 'existing'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/acceptance/tests/modify_spec.rb b/spec/acceptance/tests/modify_spec.rb
new file mode 100644
index 0000000..6498ee0
--- /dev/null
+++ b/spec/acceptance/tests/modify_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper_acceptance'
+
+RSpec.context 'when modifying host files' do
+ agents.each do |agent|
+ context "on #{agent}" do
+ let(:target) { agent.tmpfile('host-modify') }
+
+ after(:each) do
+ on(agent, "test #{target} && rm -f #{target}")
+ end
+
+ it 'modifies a host address' do
+ on agent, "printf '127.0.0.9 test alias\n' > #{target}"
+ on(agent, puppet_resource('host', 'test', "target=#{target}",
+ 'ensure=present', 'ip=127.0.0.10', 'host_aliases=alias'))
+
+ on(agent, "cat #{target}") do |result|
+ fail_test 'the address was not updated' unless
+ result.stdout =~ %r{^127\.0\.0\.10[[:space:]]+test[[:space:]]+alias[[:space:]]*$}
+ end
+ end
+
+ it 'modifies a host alias' do
+ on agent, "printf '127.0.0.8 test alias\n' > #{target}"
+ on(agent, puppet_resource('host', 'test', "target=#{target}",
+ 'ensure=present', 'ip=127.0.0.8', 'host_aliases=banzai'))
+
+ on(agent, "cat #{target}") do |result|
+ fail_test 'the alias was not updated' unless
+ result.stdout =~ %r{^127\.0\.0\.8[[:space:]]+test[[:space:]]+banzai[[:space:]]*$}
+ end
+ end
+ end
+ end
+end
diff --git a/spec/acceptance/tests/query_all_spec.rb b/spec/acceptance/tests/query_all_spec.rb
new file mode 100644
index 0000000..1653ab0
--- /dev/null
+++ b/spec/acceptance/tests/query_all_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper_acceptance'
+
+RSpec.context 'when querying all hosts from a host file' do
+ agents.each do |agent|
+ context "on #{agent}" do
+ let(:backup) { agent.tmpfile('host-query') }
+ let(:content) do
+ <<END
+127.0.0.1 test1 test1.local
+127.0.0.2 test2 test2.local
+127.0.0.3 test3 test3.local
+127.0.0.4 test4 test4.local
+END
+ end
+
+ before(:each) do
+ on(agent, "cp /etc/hosts #{backup}")
+ on agent, 'cat > /etc/hosts', stdin: content
+ end
+
+ after(:each) do
+ on agent, "cat #{backup} > /etc/hosts && rm -f #{backup}"
+ end
+
+ it 'returns 4 host records' do
+ on(agent, puppet_resource('host')) do |result|
+ found = result.stdout.scan(%r{host \{ '([^']+)'}).flatten.sort
+ fail_test "the list of returned hosts was wrong: #{found.join(', ')}" unless
+ found == ['test1', 'test2', 'test3', 'test4']
+
+ count = result.stdout.scan(%r{ensure\s+=>\s+'present'}).length
+ fail_test "found #{count} records, wanted 4" unless count == 4
+ end
+ end
+ end
+ end
+end
diff --git a/spec/default_facts.yml b/spec/default_facts.yml
new file mode 100644
index 0000000..3248be5
--- /dev/null
+++ b/spec/default_facts.yml
@@ -0,0 +1,8 @@
+# Use default_module_facts.yml for module specific facts.
+#
+# Facts specified here will override the values provided by rspec-puppet-facts.
+---
+concat_basedir: "/tmp"
+ipaddress: "172.16.254.254"
+is_pe: false
+macaddress: "AA:AA:AA:AA:AA:AA"
diff --git a/spec/fixtures/unit/provider/host/parsed/valid_hosts b/spec/fixtures/unit/provider/host/parsed/valid_hosts
new file mode 100644
index 0000000..2463629
--- /dev/null
+++ b/spec/fixtures/unit/provider/host/parsed/valid_hosts
@@ -0,0 +1,19 @@
+# Some leading comment, that should be ignored
+# The next line is empty so it should be ignored
+
+::1 localhost
+
+# We now try another delimiter: Several tabs
+127.0.0.1 localhost
+
+# No test trailing spaces
+10.0.0.1 host1
+
+# Ok its time to test aliases
+2001:252:0:1::2008:8 ipv6host alias1
+192.168.0.1 ipv4host alias2 alias3
+
+# Testing inlinecomments now
+192.168.0.2 host3 # This is host3
+192.168.0.3 host4 alias10 # This is host4
+192.168.0.4 host5 alias11 alias12 # This is host5
diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb
new file mode 100644
index 0000000..fa774ef
--- /dev/null
+++ b/spec/lib/puppet_spec/files.rb
@@ -0,0 +1,44 @@
+require 'fileutils'
+require 'tempfile'
+require 'tmpdir'
+
+# A support module for testing files.
+module PuppetSpec::Files
+ @global_tempfiles = []
+
+ def self.cleanup
+ until @global_tempfiles.empty?
+ path = @global_tempfiles.pop
+ Dir.unstub(:entries)
+ FileUtils.rm_rf path, secure: true
+ end
+ end
+
+ module_function
+
+ def tmpfile(name, dir = nil)
+ dir ||= Dir.tmpdir
+ path = Puppet::FileSystem.expand_path(make_tmpname(name, nil).encode(Encoding::UTF_8), dir)
+ PuppetSpec::Files.record_tmp(File.expand_path(path))
+
+ path
+ end
+
+ # Copied from ruby 2.4 source
+ def make_tmpname((prefix, suffix), n)
+ prefix = (String.try_convert(prefix) ||
+ raise(ArgumentError, "unexpected prefix: #{prefix.inspect}"))
+ suffix &&= (String.try_convert(suffix) ||
+ raise(ArgumentError, "unexpected suffix: #{suffix.inspect}"))
+ t = Time.now.strftime('%Y%m%d')
+ path = "#{prefix}#{t}-#{$PROCESS_ID}-#{rand(0x100000000).to_s(36)}".dup
+ path << "-#{n}" if n
+ path << suffix if suffix
+ path
+ end
+
+ def self.record_tmp(tmp)
+ # ...record it for cleanup,
+ @global_tempfiles << tmp
+ end
+end
diff --git a/spec/shared_behaviours/all_parsedfile_providers.rb b/spec/shared_behaviours/all_parsedfile_providers.rb
new file mode 100644
index 0000000..d697a14
--- /dev/null
+++ b/spec/shared_behaviours/all_parsedfile_providers.rb
@@ -0,0 +1,21 @@
+shared_examples_for 'all parsedfile providers' do |provider, *files|
+ if files.empty?
+ files = my_fixtures
+ end
+
+ files.flatten.each do |file|
+ it "should rewrite #{file} reasonably unchanged" do
+ provider.stubs(:default_target).returns(file)
+ provider.prefetch
+
+ text = provider.to_file(provider.target_records(file))
+ text.gsub!(%r{^# HEADER.+\n}, '')
+
+ oldlines = File.readlines(file)
+ newlines = text.chomp.split "\n"
+ oldlines.zip(newlines).each do |old, new|
+ expect(new.gsub(%r{\s+}, '')).to eq(old.chomp.gsub(%r{\s+}, ''))
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..e69d11d
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,45 @@
+
+require 'puppetlabs_spec_helper/module_spec_helper'
+require 'rspec-puppet-facts'
+
+begin
+ require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb'))
+rescue LoadError => loaderror
+ warn "Could not require spec_helper_local: #{loaderror.message}"
+end
+
+include RspecPuppetFacts
+
+default_facts = {
+ puppetversion: Puppet.version,
+ facterversion: Facter.version,
+}
+
+default_facts_path = File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml'))
+default_module_facts_path = File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml'))
+
+if File.exist?(default_facts_path) && File.readable?(default_facts_path)
+ default_facts.merge!(YAML.safe_load(File.read(default_facts_path)))
+end
+
+if File.exist?(default_module_facts_path) && File.readable?(default_module_facts_path)
+ default_facts.merge!(YAML.safe_load(File.read(default_module_facts_path)))
+end
+
+RSpec.configure do |c|
+ c.default_facts = default_facts
+ c.before :each do
+ # set to strictest setting for testing
+ # by default Puppet runs at warning level
+ Puppet.settings[:strict] = :warning
+ end
+end
+
+def ensure_module_defined(module_name)
+ module_name.split('::').reduce(Object) do |last_module, next_module|
+ last_module.const_set(next_module, Module.new) unless last_module.const_defined?(next_module)
+ last_module.const_get(next_module)
+ end
+end
+
+# 'spec_overrides' from sync.yml will appear below this line
diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb
new file mode 100644
index 0000000..848f7d9
--- /dev/null
+++ b/spec/spec_helper_acceptance.rb
@@ -0,0 +1,13 @@
+require 'beaker-rspec'
+require 'beaker/module_install_helper'
+require 'beaker/puppet_install_helper'
+
+RSpec.configure do |c|
+ c.before :suite do
+ unless ENV['BEAKER_provision'] == 'no'
+ run_puppet_install_helper
+ install_module_on(hosts_as('default'))
+ install_module_dependencies_on(hosts)
+ end
+ end
+end
diff --git a/spec/spec_helper_local.rb b/spec/spec_helper_local.rb
new file mode 100644
index 0000000..86c87f0
--- /dev/null
+++ b/spec/spec_helper_local.rb
@@ -0,0 +1,19 @@
+dir = File.expand_path(File.dirname(__FILE__))
+$LOAD_PATH.unshift File.join(dir, 'lib')
+
+# So everyone else doesn't have to include this base constant.
+module PuppetSpec
+ FIXTURE_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'fixtures') unless defined?(FIXTURE_DIR)
+end
+
+require 'puppet_spec/files'
+
+Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour|
+ require behaviour.relative_path_from(Pathname.new(dir))
+end
+
+RSpec.configure do |c|
+ c.after :each do
+ PuppetSpec::Files.cleanup
+ end
+end
diff --git a/spec/unit/provider/host/parsed_spec.rb b/spec/unit/provider/host/parsed_spec.rb
new file mode 100644
index 0000000..e07c83d
--- /dev/null
+++ b/spec/unit/provider/host/parsed_spec.rb
@@ -0,0 +1,210 @@
+require 'spec_helper'
+require 'shared_behaviours/all_parsedfile_providers'
+
+require 'puppet_spec/files'
+
+describe Puppet::Type.type(:host).provider(:parsed) do
+ include PuppetSpec::Files
+
+ let(:provider) { described_class }
+ let(:hostfile) { tmpfile('hosts') }
+
+ after :each do
+ provider.initvars
+ end
+
+ def mkhost(args)
+ hostresource = Puppet::Type::Host.new(name: args[:name], target: hostfile)
+
+ # Using setters of provider to build our testobject
+ # Note: We already proved, that in case of host_aliases
+ # the provider setter "host_aliases=(value)" will be
+ # called with the joined array, so we just simulate that
+ host = provider.new(hostresource)
+ args.each do |property, value|
+ value = value.join(' ') if property == :host_aliases && value.is_a?(Array)
+ host.send("#{property}=", value)
+ end
+ host
+ end
+
+ def genhost(host)
+ provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam)
+ File.stubs(:chown)
+ File.stubs(:chmod)
+ Puppet::Util::SUIDManager.stubs(:asuser).yields
+ host.flush
+ provider.target_object(hostfile).read
+ end
+
+ describe 'when parsing on incomplete line' do
+ it 'works for only ip' do
+ expect(provider.parse_line('127.0.0.1')[:line]).to eq('127.0.0.1')
+ end
+
+ it 'works for only hostname' do
+ expect(provider.parse_line('www.example.com')[:line]).to eq('www.example.com')
+ end
+
+ it 'works for ip and space' do
+ expect(provider.parse_line('127.0.0.1 ')[:line]).to eq('127.0.0.1 ')
+ end
+
+ it 'works for hostname and space' do
+ expect(provider.parse_line('www.example.com ')[:line]).to eq('www.example.com ')
+ end
+
+ it 'works for hostname and host_aliases' do
+ expect(provider.parse_line('www.example.com www xyz')[:line]).to eq('www.example.com www xyz')
+ end
+
+ it 'works for ip and comment' do
+ expect(provider.parse_line('127.0.0.1 #www xyz')[:line]).to eq('127.0.0.1 #www xyz')
+ end
+
+ it 'works for hostname and comment' do
+ expect(provider.parse_line('xyz #www test123')[:line]).to eq('xyz #www test123')
+ end
+
+ it 'works for crazy incomplete lines' do
+ expect(provider.parse_line("%th1s is a\t cr$zy !incompl1t line")[:line]).to eq("%th1s is a\t cr$zy !incompl1t line")
+ end
+ end
+
+ describe 'when parsing a line with ip and hostname' do
+ it 'parses an ipv4 from the first field' do
+ expect(provider.parse_line('127.0.0.1 localhost')[:ip]).to eq('127.0.0.1')
+ end
+
+ it 'parses an ipv6 from the first field' do
+ expect(provider.parse_line('::1 localhost')[:ip]).to eq('::1')
+ end
+
+ it 'parses the name from the second field' do
+ expect(provider.parse_line('::1 localhost')[:name]).to eq('localhost')
+ end
+
+ it 'sets an empty comment' do
+ expect(provider.parse_line('::1 localhost')[:comment]).to eq('')
+ end
+
+ it 'sets host_aliases to :absent' do
+ expect(provider.parse_line('::1 localhost')[:host_aliases]).to eq(:absent)
+ end
+ end
+
+ describe 'when parsing a line with ip, hostname and comment' do
+ let(:testline) { '127.0.0.1 localhost # A comment with a #-char' }
+
+ it 'parses the ip from the first field' do
+ expect(provider.parse_line(testline)[:ip]).to eq('127.0.0.1')
+ end
+
+ it 'parses the hostname from the second field' do
+ expect(provider.parse_line(testline)[:name]).to eq('localhost')
+ end
+
+ it "parses the comment after the first '#' character" do
+ expect(provider.parse_line(testline)[:comment]).to eq('A comment with a #-char')
+ end
+ end
+
+ describe 'when parsing a line with ip, hostname and aliases' do
+ it 'parses alias from the third field' do
+ expect(provider.parse_line('127.0.0.1 localhost localhost.localdomain')[:host_aliases]).to eq('localhost.localdomain')
+ end
+
+ it 'parses multiple aliases' do
+ expect(provider.parse_line('127.0.0.1 host alias1 alias2')[:host_aliases]).to eq('alias1 alias2')
+ expect(provider.parse_line("127.0.0.1 host alias1\talias2")[:host_aliases]).to eq('alias1 alias2')
+ expect(provider.parse_line("127.0.0.1 host alias1\talias2 alias3")[:host_aliases]).to eq('alias1 alias2 alias3')
+ end
+ end
+
+ describe 'when parsing a line with ip, hostname, aliases and comment' do
+ # Just playing with a few different delimiters
+ let(:testline) { "127.0.0.1\t host alias1\talias2 alias3 # A comment with a #-char" }
+
+ it 'parses the ip from the first field' do
+ expect(provider.parse_line(testline)[:ip]).to eq('127.0.0.1')
+ end
+
+ it 'parses the hostname from the second field' do
+ expect(provider.parse_line(testline)[:name]).to eq('host')
+ end
+
+ it 'parses all host_aliases from the third field' do
+ expect(provider.parse_line(testline)[:host_aliases]).to eq('alias1 alias2 alias3')
+ end
+
+ it "parses the comment after the first '#' character" do
+ expect(provider.parse_line(testline)[:comment]).to eq('A comment with a #-char')
+ end
+ end
+
+ describe 'when operating on /etc/hosts like files' do
+ it_behaves_like 'all parsedfile providers',
+ described_class, my_fixtures('valid*')
+
+ it 'is able to generate a simple hostfile entry' do
+ host = mkhost(
+ name: 'localhost',
+ ip: '127.0.0.1',
+ ensure: :present,
+ )
+ expect(genhost(host)).to eq("127.0.0.1\tlocalhost\n")
+ end
+
+ it 'is able to generate an entry with one alias' do
+ host = mkhost(
+ name: 'localhost.localdomain',
+ ip: '127.0.0.1',
+ host_aliases: 'localhost',
+ ensure: :present,
+ )
+ expect(genhost(host)).to eq("127.0.0.1\tlocalhost.localdomain\tlocalhost\n")
+ end
+
+ it 'is able to generate an entry with more than one alias' do
+ host = mkhost(
+ name: 'host',
+ ip: '192.0.0.1',
+ host_aliases: ['a1', 'a2', 'a3', 'a4'],
+ ensure: :present,
+ )
+ expect(genhost(host)).to eq("192.0.0.1\thost\ta1 a2 a3 a4\n")
+ end
+
+ it 'is able to generate a simple hostfile entry with comments' do
+ host = mkhost(
+ name: 'localhost',
+ ip: '127.0.0.1',
+ comment: 'Bazinga!',
+ ensure: :present,
+ )
+ expect(genhost(host)).to eq("127.0.0.1\tlocalhost\t# Bazinga!\n")
+ end
+
+ it 'is able to generate an entry with one alias and a comment' do
+ host = mkhost(
+ name: 'localhost.localdomain',
+ ip: '127.0.0.1',
+ host_aliases: 'localhost',
+ comment: 'Bazinga!',
+ ensure: :present,
+ )
+ expect(genhost(host)).to eq("127.0.0.1\tlocalhost.localdomain\tlocalhost\t# Bazinga!\n")
+ end
+
+ it 'is able to generate an entry with more than one alias and a comment' do
+ host = mkhost(
+ name: 'host',
+ ip: '192.0.0.1',
+ host_aliases: ['a1', 'a2', 'a3', 'a4'],
+ comment: 'Bazinga!',
+ ensure: :present,
+ )
+ expect(genhost(host)).to eq("192.0.0.1\thost\ta1 a2 a3 a4\t# Bazinga!\n")
+ end
+ end
+end
diff --git a/spec/unit/type/host_spec.rb b/spec/unit/type/host_spec.rb
new file mode 100644
index 0000000..426657a
--- /dev/null
+++ b/spec/unit/type/host_spec.rb
@@ -0,0 +1,665 @@
+require 'spec_helper'
+
+FakeHostProvider = Struct.new(:ip, :host_aliases, :comment)
+
+describe Puppet::Type.type(:host) do
+ let(:provider) { FakeHostProvider.new }
+ let(:resource) { stub('resource', resource: nil, provider: provider) }
+
+ it 'has :name be its namevar' do
+ expect(described_class.key_attributes).to eq([:name])
+ end
+
+ describe 'when validating attributes' do
+ [:name, :provider].each do |param|
+ it "should have a #{param} parameter" do
+ expect(described_class.attrtype(param)).to eq(:param)
+ end
+ end
+
+ [:ip, :target, :host_aliases, :comment, :ensure].each do |property|
+ it "should have a #{property} property" do
+ expect(described_class.attrtype(property)).to eq(:property)
+ end
+ end
+
+ it 'has a list host_aliases' do
+ expect(described_class.attrclass(:host_aliases).ancestors).to be_include(Puppet::Property::OrderedList)
+ end
+ end
+
+ describe 'when validating values' do
+ it 'supports present as a value for ensure' do
+ expect { described_class.new(name: 'foo', ensure: :present) }.not_to raise_error
+ end
+
+ it 'supports absent as a value for ensure' do
+ expect { described_class.new(name: 'foo', ensure: :absent) }.not_to raise_error
+ end
+
+ it 'accepts IPv4 addresses' do
+ expect { described_class.new(name: 'foo', ip: '10.96.0.1') }.not_to raise_error
+ end
+
+ it 'accepts long IPv6 addresses' do
+ # Taken from wikipedia article about ipv6
+ expect { described_class.new(name: 'foo', ip: '2001:0db8:85a3:08d3:1319:8a2e:0370:7344') }.not_to raise_error
+ end
+
+ it 'accepts one host_alias' do
+ expect { described_class.new(name: 'foo', host_aliases: 'alias1') }.not_to raise_error
+ end
+
+ it 'accepts multiple host_aliases' do
+ expect { described_class.new(name: 'foo', host_aliases: ['alias1', 'alias2']) }.not_to raise_error
+ end
+
+ it 'accepts shortened IPv6 addresses' do
+ expect { described_class.new(name: 'foo', ip: '2001:db8:0:8d3:0:8a2e:70:7344') }.not_to raise_error
+ expect { described_class.new(name: 'foo', ip: '::ffff:192.0.2.128') }.not_to raise_error
+ expect { described_class.new(name: 'foo', ip: '::1') }.not_to raise_error
+ end
+
+ it 'does not accept malformed IPv4 addresses like 192.168.0.300' do
+ expect { described_class.new(name: 'foo', ip: '192.168.0.300') }.to raise_error(Puppet::ResourceError, %r{Parameter ip failed})
+ end
+
+ it 'rejects over-long IPv4 addresses' do
+ expect { described_class.new(name: 'foo', ip: '10.10.10.10.10') }.to raise_error(Puppet::ResourceError, %r{Parameter ip failed})
+ end
+
+ it 'does not accept malformed IP addresses like 2001:0dg8:85a3:08d3:1319:8a2e:0370:7344' do
+ expect { described_class.new(name: 'foo', ip: '2001:0dg8:85a3:08d3:1319:8a2e:0370:7344') }.to raise_error(Puppet::ResourceError, %r{Parameter ip failed})
+ end
+
+ # Assorted, annotated IPv6 passes.
+ ['::1', # loopback, compressed, non-routable
+ '::', # unspecified, compressed, non-routable
+ '0:0:0:0:0:0:0:1', # loopback, full
+ '0:0:0:0:0:0:0:0', # unspecified, full
+ '2001:DB8:0:0:8:800:200C:417A', # unicast, full
+ 'FF01:0:0:0:0:0:0:101', # multicast, full
+ '2001:DB8::8:800:200C:417A', # unicast, compressed
+ 'FF01::101', # multicast, compressed
+ # Some more test cases that should pass.
+ '2001:0000:1234:0000:0000:C1C0:ABCD:0876',
+ '3ffe:0b00:0000:0000:0001:0000:0000:000a',
+ 'FF02:0000:0000:0000:0000:0000:0000:0001',
+ '0000:0000:0000:0000:0000:0000:0000:0001',
+ '0000:0000:0000:0000:0000:0000:0000:0000',
+ # Assorted valid, compressed IPv6 addresses.
+ '2::10',
+ 'ff02::1',
+ 'fe80::',
+ '2002::',
+ '2001:db8::',
+ '2001:0db8:1234::',
+ '::ffff:0:0',
+ '::1',
+ '1:2:3:4:5:6:7:8',
+ '1:2:3:4:5:6::8',
+ '1:2:3:4:5::8',
+ '1:2:3:4::8',
+ '1:2:3::8',
+ '1:2::8',
+ '1::8',
+ '1::2:3:4:5:6:7',
+ '1::2:3:4:5:6',
+ '1::2:3:4:5',
+ '1::2:3:4',
+ '1::2:3',
+ '1::8',
+ '::2:3:4:5:6:7:8',
+ '::2:3:4:5:6:7',
+ '::2:3:4:5:6',
+ '::2:3:4:5',
+ '::2:3:4',
+ '::2:3',
+ '::8',
+ '1:2:3:4:5:6::',
+ '1:2:3:4:5::',
+ '1:2:3:4::',
+ '1:2:3::',
+ '1:2::',
+ '1::',
+ '1:2:3:4:5::7:8',
+ '1:2:3:4::7:8',
+ '1:2:3::7:8',
+ '1:2::7:8',
+ '1::7:8',
+ # IPv4 addresses as dotted-quads
+ '1:2:3:4:5:6:1.2.3.4',
+ '1:2:3:4:5::1.2.3.4',
+ '1:2:3:4::1.2.3.4',
+ '1:2:3::1.2.3.4',
+ '1:2::1.2.3.4',
+ '1::1.2.3.4',
+ '1:2:3:4::5:1.2.3.4',
+ '1:2:3::5:1.2.3.4',
+ '1:2::5:1.2.3.4',
+ '1::5:1.2.3.4',
+ '1::5:11.22.33.44',
+ 'fe80::217:f2ff:254.7.237.98',
+ '::ffff:192.168.1.26',
+ '::ffff:192.168.1.1',
+ '0:0:0:0:0:0:13.1.68.3', # IPv4-compatible IPv6 address, full, deprecated
+ '0:0:0:0:0:FFFF:129.144.52.38', # IPv4-mapped IPv6 address, full
+ '::13.1.68.3', # IPv4-compatible IPv6 address, compressed, deprecated
+ '::FFFF:129.144.52.38', # IPv4-mapped IPv6 address, compressed
+ 'fe80:0:0:0:204:61ff:254.157.241.86',
+ 'fe80::204:61ff:254.157.241.86',
+ '::ffff:12.34.56.78',
+ '::ffff:192.0.2.128', # this is OK, since there's a single zero digit in IPv4
+ 'fe80:0000:0000:0000:0204:61ff:fe9d:f156',
+ 'fe80:0:0:0:204:61ff:fe9d:f156',
+ 'fe80::204:61ff:fe9d:f156',
+ '::1',
+ 'fe80::',
+ 'fe80::1',
+ '::ffff:c000:280',
+
+ # Additional test cases from http://rt.cpan.org/Public/Bug/Display.html?id=50693
+ '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
+ '2001:db8:85a3:0:0:8a2e:370:7334',
+ '2001:db8:85a3::8a2e:370:7334',
+ '2001:0db8:0000:0000:0000:0000:1428:57ab',
+ '2001:0db8:0000:0000:0000::1428:57ab',
+ '2001:0db8:0:0:0:0:1428:57ab',
+ '2001:0db8:0:0::1428:57ab',
+ '2001:0db8::1428:57ab',
+ '2001:db8::1428:57ab',
+ '0000:0000:0000:0000:0000:0000:0000:0001',
+ '::1',
+ '::ffff:0c22:384e',
+ '2001:0db8:1234:0000:0000:0000:0000:0000',
+ '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff',
+ '2001:db8:a::123',
+ 'fe80::',
+
+ '1111:2222:3333:4444:5555:6666:7777:8888',
+ '1111:2222:3333:4444:5555:6666:7777::',
+ '1111:2222:3333:4444:5555:6666::',
+ '1111:2222:3333:4444:5555::',
+ '1111:2222:3333:4444::',
+ '1111:2222:3333::',
+ '1111:2222::',
+ '1111::',
+ '1111:2222:3333:4444:5555:6666::8888',
+ '1111:2222:3333:4444:5555::8888',
+ '1111:2222:3333:4444::8888',
+ '1111:2222:3333::8888',
+ '1111:2222::8888',
+ '1111::8888',
+ '::8888',
+ '1111:2222:3333:4444:5555::7777:8888',
+ '1111:2222:3333:4444::7777:8888',
+ '1111:2222:3333::7777:8888',
+ '1111:2222::7777:8888',
+ '1111::7777:8888',
+ '::7777:8888',
+ '1111:2222:3333:4444::6666:7777:8888',
+ '1111:2222:3333::6666:7777:8888',
+ '1111:2222::6666:7777:8888',
+ '1111::6666:7777:8888',
+ '::6666:7777:8888',
+ '1111:2222:3333::5555:6666:7777:8888',
+ '1111:2222::5555:6666:7777:8888',
+ '1111::5555:6666:7777:8888',
+ '::5555:6666:7777:8888',
+ '1111:2222::4444:5555:6666:7777:8888',
+ '1111::4444:5555:6666:7777:8888',
+ '::4444:5555:6666:7777:8888',
+ '1111::3333:4444:5555:6666:7777:8888',
+ '::3333:4444:5555:6666:7777:8888',
+ '::2222:3333:4444:5555:6666:7777:8888',
+ '1111:2222:3333:4444:5555:6666:123.123.123.123',
+ '1111:2222:3333:4444:5555::123.123.123.123',
+ '1111:2222:3333:4444::123.123.123.123',
+ '1111:2222:3333::123.123.123.123',
+ '1111:2222::123.123.123.123',
+ '1111::123.123.123.123',
+ '::123.123.123.123',
+ '1111:2222:3333:4444::6666:123.123.123.123',
+ '1111:2222:3333::6666:123.123.123.123',
+ '1111:2222::6666:123.123.123.123',
+ '1111::6666:123.123.123.123',
+ '::6666:123.123.123.123',
+ '1111:2222:3333::5555:6666:123.123.123.123',
+ '1111:2222::5555:6666:123.123.123.123',
+ '1111::5555:6666:123.123.123.123',
+ '::5555:6666:123.123.123.123',
+ '1111:2222::4444:5555:6666:123.123.123.123',
+ '1111::4444:5555:6666:123.123.123.123',
+ '::4444:5555:6666:123.123.123.123',
+ '1111::3333:4444:5555:6666:123.123.123.123',
+ '::2222:3333:4444:5555:6666:123.123.123.123',
+
+ # Playing with combinations of "0" and "::"; these are all sytactically
+ # correct, but are bad form because "0" adjacent to "::" should be
+ # combined into "::"
+ '::0:0:0:0:0:0:0',
+ '::0:0:0:0:0:0',
+ '::0:0:0:0:0',
+ '::0:0:0:0',
+ '::0:0:0',
+ '::0:0',
+ '::0',
+ '0:0:0:0:0:0:0::',
+ '0:0:0:0:0:0::',
+ '0:0:0:0:0::',
+ '0:0:0:0::',
+ '0:0:0::',
+ '0:0::',
+ '0::',
+
+ # Additional cases: http://crisp.tweakblogs.net/blog/2031/ipv6-validation-%28and-caveats%29.html
+ '0:a:b:c:d:e:f::',
+ '::0:a:b:c:d:e:f', # syntactically correct, but bad form (::0:... could be combined)
+ 'a:b:c:d:e:f:0::'].each do |ip|
+ it "should accept #{ip.inspect} as an IPv6 address" do
+ expect { described_class.new(name: 'foo', ip: ip) }.not_to raise_error
+ end
+ end
+
+ # ...aaaand, some failure cases.
+ [':',
+ '02001:0000:1234:0000:0000:C1C0:ABCD:0876', # extra 0 not allowed!
+ '2001:0000:1234:0000:00001:C1C0:ABCD:0876', # extra 0 not allowed!
+ '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0', # junk after valid address
+ '2001:0000:1234: 0000:0000:C1C0:ABCD:0876', # internal space
+ '3ffe:0b00:0000:0001:0000:0000:000a', # seven segments
+ 'FF02:0000:0000:0000:0000:0000:0000:0000:0001', # nine segments
+ '3ffe:b00::1::a', # double "::"
+ '::1111:2222:3333:4444:5555:6666::', # double "::"
+ '1:2:3::4:5::7:8', # Double "::"
+ '12345::6:7:8',
+ # IPv4 embedded, but bad...
+ '1::5:400.2.3.4', '1::5:260.2.3.4', '1::5:256.2.3.4', '1::5:1.256.3.4',
+ '1::5:1.2.256.4', '1::5:1.2.3.256', '1::5:300.2.3.4', '1::5:1.300.3.4',
+ '1::5:1.2.300.4', '1::5:1.2.3.300', '1::5:900.2.3.4', '1::5:1.900.3.4',
+ '1::5:1.2.900.4', '1::5:1.2.3.900', '1::5:300.300.300.300', '1::5:3000.30.30.30',
+ '1::400.2.3.4', '1::260.2.3.4', '1::256.2.3.4', '1::1.256.3.4',
+ '1::1.2.256.4', '1::1.2.3.256', '1::300.2.3.4', '1::1.300.3.4',
+ '1::1.2.300.4', '1::1.2.3.300', '1::900.2.3.4', '1::1.900.3.4',
+ '1::1.2.900.4', '1::1.2.3.900', '1::300.300.300.300', '1::3000.30.30.30',
+ '::400.2.3.4', '::260.2.3.4', '::256.2.3.4', '::1.256.3.4',
+ '::1.2.256.4', '::1.2.3.256', '::300.2.3.4', '::1.300.3.4',
+ '::1.2.300.4', '::1.2.3.300', '::900.2.3.4', '::1.900.3.4',
+ '::1.2.900.4', '::1.2.3.900', '::300.300.300.300', '::3000.30.30.30',
+ '2001:1:1:1:1:1:255Z255X255Y255', # garbage instead of "." in IPv4
+ '::ffff:192x168.1.26', # ditto
+ '::ffff:2.3.4',
+ '::ffff:257.1.2.3',
+ '1.2.3.4:1111:2222:3333:4444::5555',
+ '1.2.3.4:1111:2222:3333::5555',
+ '1.2.3.4:1111:2222::5555',
+ '1.2.3.4:1111::5555',
+ '1.2.3.4::5555',
+ '1.2.3.4::',
+
+ # Testing IPv4 addresses represented as dotted-quads Leading zero's in
+ # IPv4 addresses not allowed: some systems treat the leading "0" in
+ # ".086" as the start of an octal number Update: The BNF in RFC-3986
+ # explicitly defines the dec-octet (for IPv4 addresses) not to have a
+ # leading zero
+ 'fe80:0000:0000:0000:0204:61ff:254.157.241.086',
+ 'XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4',
+ '1111:2222:3333:4444:5555:6666:00.00.00.00',
+ '1111:2222:3333:4444:5555:6666:000.000.000.000',
+ '1111:2222:3333:4444:5555:6666:256.256.256.256',
+
+ '1111:2222:3333:4444::5555:',
+ '1111:2222:3333::5555:',
+ '1111:2222::5555:',
+ '1111::5555:',
+ '::5555:',
+ ':::',
+ '1111:',
+ ':',
+
+ ':1111:2222:3333:4444::5555',
+ ':1111:2222:3333::5555',
+ ':1111:2222::5555',
+ ':1111::5555',
+ ':::5555',
+ ':::',
+
+ # Additional test cases from http://rt.cpan.org/Public/Bug/Display.html?id=50693
+ '123',
+ 'ldkfj',
+ '2001::FFD3::57ab',
+ '2001:db8:85a3::8a2e:37023:7334',
+ '2001:db8:85a3::8a2e:370k:7334',
+ '1:2:3:4:5:6:7:8:9',
+ '1::2::3',
+ '1:::3:4:5',
+ '1:2:3::4:5:6:7:8:9',
+
+ # Invalid data
+ 'XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX',
+
+ # Too many components
+ '1111:2222:3333:4444:5555:6666:7777:8888:9999',
+ '1111:2222:3333:4444:5555:6666:7777:8888::',
+ '::2222:3333:4444:5555:6666:7777:8888:9999',
+
+ # Too few components
+ '1111:2222:3333:4444:5555:6666:7777',
+ '1111:2222:3333:4444:5555:6666',
+ '1111:2222:3333:4444:5555',
+ '1111:2222:3333:4444',
+ '1111:2222:3333',
+ '1111:2222',
+ '1111',
+
+ # Missing :
+ '11112222:3333:4444:5555:6666:7777:8888',
+ '1111:22223333:4444:5555:6666:7777:8888',
+ '1111:2222:33334444:5555:6666:7777:8888',
+ '1111:2222:3333:44445555:6666:7777:8888',
+ '1111:2222:3333:4444:55556666:7777:8888',
+ '1111:2222:3333:4444:5555:66667777:8888',
+ '1111:2222:3333:4444:5555:6666:77778888',
+
+ # Missing : intended for ::
+ '1111:2222:3333:4444:5555:6666:7777:8888:',
+ '1111:2222:3333:4444:5555:6666:7777:',
+ '1111:2222:3333:4444:5555:6666:',
+ '1111:2222:3333:4444:5555:',
+ '1111:2222:3333:4444:',
+ '1111:2222:3333:',
+ '1111:2222:',
+ '1111:',
+ ':',
+ ':8888',
+ ':7777:8888',
+ ':6666:7777:8888',
+ ':5555:6666:7777:8888',
+ ':4444:5555:6666:7777:8888',
+ ':3333:4444:5555:6666:7777:8888',
+ ':2222:3333:4444:5555:6666:7777:8888',
+ ':1111:2222:3333:4444:5555:6666:7777:8888',
+
+ # :::
+ ':::2222:3333:4444:5555:6666:7777:8888',
+ '1111:::3333:4444:5555:6666:7777:8888',
+ '1111:2222:::4444:5555:6666:7777:8888',
+ '1111:2222:3333:::5555:6666:7777:8888',
+ '1111:2222:3333:4444:::6666:7777:8888',
+ '1111:2222:3333:4444:5555:::7777:8888',
+ '1111:2222:3333:4444:5555:6666:::8888',
+ '1111:2222:3333:4444:5555:6666:7777:::',
+
+ # Double ::",
+ '::2222::4444:5555:6666:7777:8888',
+ '::2222:3333::5555:6666:7777:8888',
+ '::2222:3333:4444::6666:7777:8888',
+ '::2222:3333:4444:5555::7777:8888',
+ '::2222:3333:4444:5555:7777::8888',
+ '::2222:3333:4444:5555:7777:8888::',
+
+ '1111::3333::5555:6666:7777:8888',
+ '1111::3333:4444::6666:7777:8888',
+ '1111::3333:4444:5555::7777:8888',
+ '1111::3333:4444:5555:6666::8888',
+ '1111::3333:4444:5555:6666:7777::',
+
+ '1111:2222::4444::6666:7777:8888',
+ '1111:2222::4444:5555::7777:8888',
+ '1111:2222::4444:5555:6666::8888',
+ '1111:2222::4444:5555:6666:7777::',
+
+ '1111:2222:3333::5555::7777:8888',
+ '1111:2222:3333::5555:6666::8888',
+ '1111:2222:3333::5555:6666:7777::',
+
+ '1111:2222:3333:4444::6666::8888',
+ '1111:2222:3333:4444::6666:7777::',
+
+ '1111:2222:3333:4444:5555::7777::',
+
+ # Too many components"
+ '1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4',
+ '1111:2222:3333:4444:5555:6666:7777:1.2.3.4',
+ '1111:2222:3333:4444:5555:6666::1.2.3.4',
+ '::2222:3333:4444:5555:6666:7777:1.2.3.4',
+ '1111:2222:3333:4444:5555:6666:1.2.3.4.5',
+
+ # Too few components
+ '1111:2222:3333:4444:5555:1.2.3.4',
+ '1111:2222:3333:4444:1.2.3.4',
+ '1111:2222:3333:1.2.3.4',
+ '1111:2222:1.2.3.4',
+ '1111:1.2.3.4',
+
+ # Missing :
+ '11112222:3333:4444:5555:6666:1.2.3.4',
+ '1111:22223333:4444:5555:6666:1.2.3.4',
+ '1111:2222:33334444:5555:6666:1.2.3.4',
+ '1111:2222:3333:44445555:6666:1.2.3.4',
+ '1111:2222:3333:4444:55556666:1.2.3.4',
+ '1111:2222:3333:4444:5555:66661.2.3.4',
+
+ # Missing .
+ '1111:2222:3333:4444:5555:6666:255255.255.255',
+ '1111:2222:3333:4444:5555:6666:255.255255.255',
+ '1111:2222:3333:4444:5555:6666:255.255.255255',
+
+ # Missing : intended for ::
+ ':1.2.3.4',
+ ':6666:1.2.3.4',
+ ':5555:6666:1.2.3.4',
+ ':4444:5555:6666:1.2.3.4',
+ ':3333:4444:5555:6666:1.2.3.4',
+ ':2222:3333:4444:5555:6666:1.2.3.4',
+ ':1111:2222:3333:4444:5555:6666:1.2.3.4',
+
+ # :::
+ ':::2222:3333:4444:5555:6666:1.2.3.4',
+ '1111:::3333:4444:5555:6666:1.2.3.4',
+ '1111:2222:::4444:5555:6666:1.2.3.4',
+ '1111:2222:3333:::5555:6666:1.2.3.4',
+ '1111:2222:3333:4444:::6666:1.2.3.4',
+ '1111:2222:3333:4444:5555:::1.2.3.4',
+
+ # Double ::
+ '::2222::4444:5555:6666:1.2.3.4',
+ '::2222:3333::5555:6666:1.2.3.4',
+ '::2222:3333:4444::6666:1.2.3.4',
+ '::2222:3333:4444:5555::1.2.3.4',
+
+ '1111::3333::5555:6666:1.2.3.4',
+ '1111::3333:4444::6666:1.2.3.4',
+ '1111::3333:4444:5555::1.2.3.4',
+
+ '1111:2222::4444::6666:1.2.3.4',
+ '1111:2222::4444:5555::1.2.3.4',
+
+ '1111:2222:3333::5555::1.2.3.4',
+
+ # Missing parts
+ '::.',
+ '::..',
+ '::...',
+ '::1...',
+ '::1.2..',
+ '::1.2.3.',
+ '::.2..',
+ '::.2.3.',
+ '::.2.3.4',
+ '::..3.',
+ '::..3.4',
+ '::...4',
+
+ # Extra : in front
+ ':1111:2222:3333:4444:5555:6666:7777::',
+ ':1111:2222:3333:4444:5555:6666::',
+ ':1111:2222:3333:4444:5555::',
+ ':1111:2222:3333:4444::',
+ ':1111:2222:3333::',
+ ':1111:2222::',
+ ':1111::',
+ ':::',
+ ':1111:2222:3333:4444:5555:6666::8888',
+ ':1111:2222:3333:4444:5555::8888',
+ ':1111:2222:3333:4444::8888',
+ ':1111:2222:3333::8888',
+ ':1111:2222::8888',
+ ':1111::8888',
+ ':::8888',
+ ':1111:2222:3333:4444:5555::7777:8888',
+ ':1111:2222:3333:4444::7777:8888',
+ ':1111:2222:3333::7777:8888',
+ ':1111:2222::7777:8888',
+ ':1111::7777:8888',
+ ':::7777:8888',
+ ':1111:2222:3333:4444::6666:7777:8888',
+ ':1111:2222:3333::6666:7777:8888',
+ ':1111:2222::6666:7777:8888',
+ ':1111::6666:7777:8888',
+ ':::6666:7777:8888',
+ ':1111:2222:3333::5555:6666:7777:8888',
+ ':1111:2222::5555:6666:7777:8888',
+ ':1111::5555:6666:7777:8888',
+ ':::5555:6666:7777:8888',
+ ':1111:2222::4444:5555:6666:7777:8888',
+ ':1111::4444:5555:6666:7777:8888',
+ ':::4444:5555:6666:7777:8888',
+ ':1111::3333:4444:5555:6666:7777:8888',
+ ':::3333:4444:5555:6666:7777:8888',
+ ':::2222:3333:4444:5555:6666:7777:8888',
+ ':1111:2222:3333:4444:5555:6666:1.2.3.4',
+ ':1111:2222:3333:4444:5555::1.2.3.4',
+ ':1111:2222:3333:4444::1.2.3.4',
+ ':1111:2222:3333::1.2.3.4',
+ ':1111:2222::1.2.3.4',
+ ':1111::1.2.3.4',
+ ':::1.2.3.4',
+ ':1111:2222:3333:4444::6666:1.2.3.4',
+ ':1111:2222:3333::6666:1.2.3.4',
+ ':1111:2222::6666:1.2.3.4',
+ ':1111::6666:1.2.3.4',
+ ':::6666:1.2.3.4',
+ ':1111:2222:3333::5555:6666:1.2.3.4',
+ ':1111:2222::5555:6666:1.2.3.4',
+ ':1111::5555:6666:1.2.3.4',
+ ':::5555:6666:1.2.3.4',
+ ':1111:2222::4444:5555:6666:1.2.3.4',
+ ':1111::4444:5555:6666:1.2.3.4',
+ ':::4444:5555:6666:1.2.3.4',
+ ':1111::3333:4444:5555:6666:1.2.3.4',
+ ':::2222:3333:4444:5555:6666:1.2.3.4',
+
+ # Extra : at end
+ '1111:2222:3333:4444:5555:6666:7777:::',
+ '1111:2222:3333:4444:5555:6666:::',
+ '1111:2222:3333:4444:5555:::',
+ '1111:2222:3333:4444:::',
+ '1111:2222:3333:::',
+ '1111:2222:::',
+ '1111:::',
+ ':::',
+ '1111:2222:3333:4444:5555:6666::8888:',
+ '1111:2222:3333:4444:5555::8888:',
+ '1111:2222:3333:4444::8888:',
+ '1111:2222:3333::8888:',
+ '1111:2222::8888:',
+ '1111::8888:',
+ '::8888:',
+ '1111:2222:3333:4444:5555::7777:8888:',
+ '1111:2222:3333:4444::7777:8888:',
+ '1111:2222:3333::7777:8888:',
+ '1111:2222::7777:8888:',
+ '1111::7777:8888:',
+ '::7777:8888:',
+ '1111:2222:3333:4444::6666:7777:8888:',
+ '1111:2222:3333::6666:7777:8888:',
+ '1111:2222::6666:7777:8888:',
+ '1111::6666:7777:8888:',
+ '::6666:7777:8888:',
+ '1111:2222:3333::5555:6666:7777:8888:',
+ '1111:2222::5555:6666:7777:8888:',
+ '1111::5555:6666:7777:8888:',
+ '::5555:6666:7777:8888:',
+ '1111:2222::4444:5555:6666:7777:8888:',
+ '1111::4444:5555:6666:7777:8888:',
+ '::4444:5555:6666:7777:8888:',
+ '1111::3333:4444:5555:6666:7777:8888:',
+ '::3333:4444:5555:6666:7777:8888:',
+ '::2222:3333:4444:5555:6666:7777:8888:'].each do |ip|
+ it "should reject #{ip.inspect} as an IPv6 address" do
+ expect { described_class.new(name: 'foo', ip: ip) }.to raise_error(Puppet::ResourceError, %r{Parameter ip failed})
+ end
+ end
+
+ it 'does not accept newlines in resourcename' do
+ expect { described_class.new(name: "fo\no", ip: '127.0.0.1') }.to raise_error(Puppet::ResourceError, %r{Hostname cannot include newline})
+ end
+
+ it 'does not accept newlines in ipaddress' do
+ expect { described_class.new(name: 'foo', ip: "127.0.0.1\n") }.to raise_error(Puppet::ResourceError, %r{Invalid IP address})
+ end
+
+ it 'does not accept newlines in host_aliases' do
+ expect { described_class.new(name: 'foo', ip: '127.0.0.1', host_aliases: ['well_formed', "thisalias\nhavenewline"]) }.to raise_error(Puppet::ResourceError, %r{Host aliases cannot include whitespace})
+ end
+
+ it 'does not accept newlines in comment' do
+ expect { described_class.new(name: 'foo', ip: '127.0.0.1', comment: "Test of comment blah blah \n test 123") }.to raise_error(Puppet::ResourceError, %r{Comment cannot include newline})
+ end
+
+ it 'does not accept spaces in resourcename' do
+ expect { described_class.new(name: 'foo bar') }.to raise_error(Puppet::ResourceError, %r{Invalid host name})
+ end
+
+ it 'does not accept host_aliases with spaces' do
+ expect { described_class.new(name: 'foo', host_aliases: ['well_formed', 'not wellformed']) }.to raise_error(Puppet::ResourceError, %r{Host aliases cannot include whitespace})
+ end
+
+ it 'does not accept empty host_aliases' do
+ expect { described_class.new(name: 'foo', host_aliases: ['alias1', '']) }.to raise_error(Puppet::ResourceError, %r{Host aliases cannot be an empty string})
+ end
+ end
+
+ describe 'when syncing' do
+ it 'sends the first value to the provider for ip property' do
+ ip = described_class.attrclass(:ip).new(resource: resource, should: ['192.168.0.1', '192.168.0.2'])
+ ip.sync
+
+ expect(provider.ip).to eq('192.168.0.1')
+ end
+
+ it 'sends the first value to the provider for comment property' do
+ comment = described_class.attrclass(:comment).new(resource: resource, should: ['Bazinga', 'Notme'])
+ comment.sync
+
+ expect(provider.comment).to eq('Bazinga')
+ end
+
+ it 'sends the joined array to the provider for host_alias' do
+ host_aliases = described_class.attrclass(:host_aliases).new(resource: resource, should: ['foo', 'bar'])
+ host_aliases.sync
+
+ expect(provider.host_aliases).to eq('foo bar')
+ end
+
+ it 'alsoes use the specified delimiter for joining' do
+ host_aliases = described_class.attrclass(:host_aliases).new(resource: resource, should: ['foo', 'bar'])
+ host_aliases.stubs(:delimiter).returns "\t"
+ host_aliases.sync
+
+ expect(provider.host_aliases).to eq("foo\tbar")
+ end
+
+ it 'cares about the order of host_aliases' do
+ host_aliases = described_class.attrclass(:host_aliases).new(resource: resource, should: ['foo', 'bar'])
+ expect(host_aliases.insync?(['foo', 'bar'])).to eq(true)
+ expect(host_aliases.insync?(['bar', 'foo'])).to eq(false)
+ end
+
+ it 'does not consider aliases to be in sync if should is a subset of current' do
+ host_aliases = described_class.attrclass(:host_aliases).new(resource: resource, should: ['foo', 'bar'])
+ expect(host_aliases.insync?(['foo', 'bar', 'anotherone'])).to eq(false)
+ end
+ end
+end