├── .fixtures.yml ├── .gitignore ├── .puppet-lint.rc ├── .sync.yml ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── lib └── puppet │ └── parser │ └── functions │ └── bind_check_hostname.rb ├── manifests ├── a.pp ├── acl.pp ├── config.pp ├── generate.pp ├── init.pp ├── install.pp ├── key.pp ├── mx.pp ├── params.pp ├── record.pp ├── service.pp ├── view.pp └── zone.pp ├── metadata.json ├── spec ├── .rspec ├── acceptance │ ├── bind_advanced_spec.rb │ ├── bind_cache_spec.rb │ ├── bind_spec.rb │ └── nodesets │ │ ├── centos-5.yml │ │ ├── centos-6.yml │ │ ├── centos-7.yml │ │ ├── debian-6.yml │ │ ├── debian-7.yml │ │ ├── debian-8.yml │ │ ├── ubuntu-12.04.yml │ │ ├── ubuntu-14.04.yml │ │ ├── ubuntu-14.10.yml │ │ ├── ubuntu-15.04.yml │ │ ├── ubuntu-15.10.yml │ │ └── ubuntu-16.04.yml ├── classes │ └── bind_spec.rb ├── defines │ ├── bind_a_spec.rb │ ├── bind_acl_spec.rb │ ├── bind_generate_spec.rb │ ├── bind_key_spec.rb │ ├── bind_record_spec.rb │ ├── bind_view_spec.rb │ └── bind_zone_spec.rb ├── spec.opts ├── spec_helper.rb └── spec_helper_acceptance.rb └── templates ├── acl.erb ├── default-record.erb ├── dnskey.conf.erb ├── generate.erb ├── mx-record.erb ├── named.conf.erb ├── named.conf.options.erb ├── view.erb ├── zone-forward.erb ├── zone-header.erb ├── zone-master.erb └── zone-slave.erb /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | forge_modules: 3 | stdlib: 'puppetlabs-stdlib' 4 | concat: 'puppetlabs-concat' 5 | symlinks: 6 | bind: "#{source_dir}" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/ 2 | Gemfile.lock 3 | vendor/ 4 | spec/fixtures/ 5 | .vagrant/ 6 | .bundle/ 7 | coverage/ 8 | log/ 9 | .*.swp 10 | *~ 11 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --fail-on-warnings 2 | --relative 3 | --no-140chars 4 | --no-documentation 5 | --no-class_inherits_from_params_class-check 6 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | .travis.yml: 3 | acceptance: 4 | - '*' 5 | forge_password: "HLOnMqlB03FRSusdbWf/afjyc0jbHgoVZ/HF4x8y9H7g8utuBQJxjsAAxdrAme5JOuN6i3+J1A8IrpcRIwicBDk/yav4IrT65fCyzfBz7p6Kx2doT4a3WwiMXzqgcDe8Z8l7hu531HnXzxXEjCKCoEFiUBWI+4ULXQk3ATMrm/U=" 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | sudo: false 4 | addons: 5 | apt: 6 | packages: 7 | - libaugeas-dev 8 | sources: 9 | - augeas 10 | cache: bundler 11 | bundler_args: --without system_tests 12 | script: ["bundle exec rake validate", "bundle exec rake lint", "bundle exec rake spec SPEC_OPTS='--format documentation'"] 13 | matrix: 14 | fast_finish: true 15 | include: 16 | - rvm: 2.1.9 17 | env: PUPPET_GEM_VERSION="~> 4.0" 18 | - rvm: 2.4.1 19 | env: PUPPET_GEM_VERSION="~> 5.0" 20 | - rvm: default 21 | sudo: required 22 | dist: trusty 23 | services: docker 24 | env: BEAKER_set="centos-6" 25 | bundler_args: 26 | script: sudo service docker restart ; sleep 10 && bundle exec rspec spec/acceptance/*_spec.rb 27 | - rvm: default 28 | sudo: required 29 | dist: trusty 30 | services: docker 31 | env: BEAKER_set="centos-7" 32 | bundler_args: 33 | script: sudo service docker restart ; sleep 10 && bundle exec rspec spec/acceptance/*_spec.rb 34 | - rvm: default 35 | sudo: required 36 | dist: trusty 37 | services: docker 38 | env: BEAKER_set="debian-7" 39 | bundler_args: 40 | script: sudo service docker restart ; sleep 10 && bundle exec rspec spec/acceptance/*_spec.rb 41 | - rvm: default 42 | sudo: required 43 | dist: trusty 44 | services: docker 45 | env: BEAKER_set="debian-8" 46 | bundler_args: 47 | script: sudo service docker restart ; sleep 10 && bundle exec rspec spec/acceptance/*_spec.rb 48 | - rvm: default 49 | sudo: required 50 | dist: trusty 51 | services: docker 52 | env: BEAKER_set="ubuntu-14.04" 53 | bundler_args: 54 | script: sudo service docker restart ; sleep 10 && bundle exec rspec spec/acceptance/*_spec.rb 55 | notifications: 56 | email: false 57 | deploy: 58 | provider: puppetforge 59 | user: camptocamp 60 | password: 61 | secure: "HLOnMqlB03FRSusdbWf/afjyc0jbHgoVZ/HF4x8y9H7g8utuBQJxjsAAxdrAme5JOuN6i3+J1A8IrpcRIwicBDk/yav4IrT65fCyzfBz7p6Kx2doT4a3WwiMXzqgcDe8Z8l7hu531HnXzxXEjCKCoEFiUBWI+4ULXQk3ATMrm/U=" 62 | on: 63 | tags: true 64 | # all_branches is required to use tags 65 | all_branches: true 66 | # Only publish if our main Ruby target builds 67 | rvm: 2.1.9 68 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2016-02-17 - Release 1.4.0 2 | 3 | - Corrected issues in 1.3.x 4 | - Added Configuration support 5 | - Added Logging support 6 | - Added new acceptance tests in order to spot errors 7 | 8 | ## 2016-02-15 - Release 1.3.1 9 | 10 | - Added missing fragment 11 | 12 | ## 2016-02-15 - Release 1.3.0 13 | 14 | - Added ACLs support 15 | - Added Views support 16 | - Some minor corrections to tests 17 | 18 | ## 2016-02-09 - Release 1.2.2 19 | 20 | - Corrected PTR zone content 21 | 22 | ## 2016-02-08 - Release 1.2.1 23 | 24 | - Corrected unit tests for puppet 4.0 25 | - Changed resource names for generate 26 | 27 | ## 2015-09-29 - Release 1.2.0 28 | 29 | - Add acceptance tests 30 | - Refactor module to use install/config/service 31 | - Fix ordering of service on RedHat systems 32 | 33 | ## 2015-09-29 - Release 1.1.5 34 | 35 | - pass $::path to exec 36 | 37 | ## 2015-09-16 - Release 1.1.4 38 | 39 | - fix default record order 40 | - add missing parameters to README (GH #55) 41 | - fix missing dollar in variable (GH #57) 42 | 43 | ## 2015-08-21 - Release 1.1.3 44 | 45 | Use docker for acceptance tests 46 | 47 | ## 2015-06-26 - Release 1.1.1 48 | 49 | Fix strict_variables activation with rspec-puppet 2.2 50 | 51 | ## 2015-06-22 - Release 1.1.0 52 | 53 | Add support for "forward zone" thanks to @vide 54 | 55 | ## 2015-05-28 - Release 1.0.16 56 | 57 | Add beaker_spec_helper to Gemfile 58 | 59 | ## 2015-05-26 - Release 1.0.15 60 | 61 | Use random application order in nodeset 62 | 63 | ## 2015-05-26 - Release 1.0.14 64 | 65 | add utopic & vivid nodesets 66 | 67 | ## 2015-05-25 - Release 1.0.13 68 | 69 | Don't allow failure on Puppet 4 70 | 71 | ## 2015-05-13 - Release 1.0.12 72 | 73 | Add puppet-lint-file_source_rights-check gem 74 | 75 | ## 2015-05-12 - Release 1.0.11 76 | 77 | Don't pin beaker 78 | 79 | ## 2015-04-27 - Release 1.0.10 80 | 81 | Add nodeset ubuntu-12.04-x86_64-openstack 82 | 83 | ## 2015-04-17 - Release 1.0.9 84 | 85 | - Fetch fixtures from puppet forge 86 | 87 | ## 2015-04-03 - Release 1.0.8 88 | 89 | - Compact templating 90 | - Confine rspec pinning to ruby 1.8 91 | 92 | ## 2015-03-24 - Release 1.0.7 93 | 94 | - Lint 95 | 96 | ## 2015-01-21 - Release 1.0.6 97 | 98 | - Corrected unit tests 99 | - Added support for multi NS records in zone file 100 | 101 | ## 2015-01-07 - Release 1.0.5 102 | 103 | - Fix unquoted strings in cases 104 | 105 | ## 2014-11-18 - Release 1.0.1 106 | 107 | - Lint metadata.json 108 | 109 | ## 2014-10-20 - Release 1.0.0 110 | 111 | - Setup automatic Forge releases 112 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || "https://rubygems.org" 2 | 3 | group :development, :unit_tests do 4 | gem 'rake', :require => false 5 | gem 'rspec', :require => false 6 | gem 'rspec-puppet', :require => false 7 | gem 'puppetlabs_spec_helper', :require => false 8 | gem 'metadata-json-lint', :require => false 9 | gem 'puppet-lint', :require => false 10 | gem 'puppet-lint-unquoted_string-check', :require => false 11 | gem 'puppet-lint-empty_string-check', :require => false 12 | gem 'puppet-lint-spaceship_operator_without_tag-check', :require => false 13 | gem 'puppet-lint-undef_in_function-check', :require => false 14 | gem 'puppet-lint-leading_zero-check', :require => false 15 | gem 'puppet-lint-trailing_comma-check', :require => false 16 | gem 'puppet-lint-file_ensure-check', :require => false 17 | gem 'puppet-lint-version_comparison-check', :require => false 18 | gem 'puppet-lint-file_source_rights-check', :require => false 19 | gem 'puppet-lint-alias-check', :require => false 20 | gem 'rspec-puppet-facts', :require => false 21 | gem 'ruby-augeas', :require => false 22 | gem 'puppet-blacksmith', :require => false if RUBY_VERSION !~ /^1\./ 23 | gem 'json_pure', '< 2.0.2', :require => false 24 | end 25 | 26 | group :system_tests do 27 | gem 'beaker', '~>3.13', :require => false 28 | gem 'beaker-rspec', '> 5', :require => false 29 | gem 'beaker_spec_helper', :require => false 30 | gem 'serverspec', :require => false 31 | gem 'specinfra', :require => false 32 | end 33 | 34 | if facterversion = ENV['FACTER_GEM_VERSION'] 35 | gem 'facter', facterversion, :require => false 36 | else 37 | gem 'facter', :require => false 38 | end 39 | 40 | if puppetversion = ENV['PUPPET_GEM_VERSION'] 41 | gem 'puppet', puppetversion, :require => false 42 | else 43 | gem 'puppet', :require => false 44 | end 45 | 46 | # vim:ft=ruby 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bind module for Puppet 2 | 3 | [![Puppet Forge](http://img.shields.io/puppetforge/v/camptocamp/bind.svg)](https://forge.puppetlabs.com/camptocamp/bind) 4 | [![Build Status](https://travis-ci.org/camptocamp/puppet-bind.png?branch=master)](https://travis-ci.org/camptocamp/puppet-bind) 5 | 6 | **Manages bind configuration under Debian / Ubuntu and CentOS.** 7 | 8 | This module is provided by [Camptocamp](http://www.camptocamp.com/) 9 | 10 | 11 | ## Classes 12 | 13 | * bind 14 | 15 | ### bind 16 | 17 | This class must be declared before using the definitions in this module. 18 | 19 | ## Definitions 20 | 21 | * bind::a 22 | * bind::acl 23 | * bind::generate 24 | * bind::mx 25 | * bind::record 26 | * bind::zone 27 | 28 | ### bind::a 29 | 30 | Creates an A record (or a series thereof). 31 | 32 | bind::a { 'Hosts in example.com': 33 | ensure => 'present', 34 | zone => 'example.com', 35 | ptr => false, 36 | hash_data => { 37 | 'host1' => { owner => '192.168.0.1', }, 38 | 'host2' => { owner => '192.168.0.2', }, 39 | }, 40 | } 41 | 42 | ##### `$ensure = present` 43 | Ensure the A record is present. 44 | ##### `$zone` 45 | Zone name. 46 | ##### `$hash_data` 47 | Zone data. 48 | ##### `$ptr = true` 49 | Pointer records (PTR) are used to map a network interface to a host name. Primarily used for reverse DNS. 50 | ##### `$zone_arpa = undef` 51 | Needed if `$ptr` is true. For reverse DNS you will have to setup your reverse DNS domain. This is a special domain that ends with `in-addr.arpa`. 52 | ##### `$content = undef` 53 | Zone content; 54 | ##### `$content_template = undef` 55 | Zone content template. 56 | 57 | ### bind::acl 58 | 59 | Creates an ACL bloc 60 | 61 | bind::acl {'my acl': 62 | ensure => present, 63 | acls => [ 64 | '!192.168.1.0/24', 65 | 'any' 66 | ], 67 | } 68 | 69 | #### `$ensure = present` 70 | Ensure the ACL is present (or absent if set to "absent") 71 | #### `acls = []` 72 | List of ACL directive 73 | 74 | ### bind::generate 75 | 76 | Creates a $GENERATE directive for a specific zone 77 | 78 | bind::generate {'a-records': 79 | zone => 'test.tld', 80 | range => '2-100', 81 | record_type => 'A', 82 | lhs => 'dhcp-$', # creates dhcp-2.test.tld, dhcp-3.test.tld … 83 | rhs => '10.10.0.$', # creates IP 10.10.0.2, 10.10.0.3 … 84 | } 85 | 86 | ##### `$ensure = present` 87 | Ensure the generate is present. 88 | ##### `$zone` 89 | Zone name. Must reflect a bind::zone resource. 90 | ##### `$range` 91 | Range allocated to internal generate directive. Must be in the form 'first-last'. 92 | ##### `$record_type` 93 | Record type. Must be one of PTR, CNAME, DNAME, A, AAAA and NS. 94 | ##### `$lhs` 95 | Generated name. 96 | ##### `$rhs` 97 | Record target. 98 | ##### `$record_class = undef` 99 | Record class. Not compatible with pre-9.3 bind versions. 100 | ##### `$ttl = undef` 101 | Time to live for generated records. 102 | 103 | ### bind::mx 104 | 105 | Creates an MX record. 106 | 107 | bind::mx {'mx1': 108 | zone => 'domain.ltd', 109 | owner => '@', 110 | priority => 1, 111 | host => 'mail.domain.ltd', 112 | } 113 | 114 | ##### `$ensure = present` 115 | Ensure the MX record is present. 116 | ##### `$zone` 117 | Zone name. 118 | ##### `$host` 119 | Target of the resource record. 120 | ##### `$priority` 121 | MX record priority. 122 | ##### `$owner = undef` 123 | Owner of the resource record. 124 | ##### `$ttl = undef` 125 | Time to live for the resource record. 126 | 127 | ### bind::record 128 | 129 | Creates a generic record (or a series thereof). 130 | 131 | bind::record {'CNAME foo.example.com': 132 | zone => 'foo.example.com', 133 | record_type => 'CNAME', 134 | hash_data => { 135 | 'ldap' => { owner => 'ldap.internal', }, 136 | 'voip' => { owner => 'voip.internal', }, 137 | } 138 | } 139 | 140 | ##### `$ensure = present` 141 | Ensure the record is present. 142 | ##### `$zone` 143 | Zone name. 144 | ##### `$hash_data` 145 | Hash containing data. 146 | ##### `$record_type` 147 | Resource record type. 148 | ##### `$content = undef` 149 | Record content. 150 | ##### `$content_template = undef` 151 | Allows you to do your own template, letting you use your own hash_data content structure. 152 | ##### `$ptr_zone = undef` 153 | PTR zone. 154 | 155 | ### bind::zone 156 | 157 | Creates a zone. 158 | 159 | bind::zone {'test.tld': 160 | zone_contact => 'contact.test.tld', 161 | zone_ns => ['ns0.test.tld'], 162 | zone_serial => '2012112901', 163 | zone_ttl => '604800', 164 | zone_origin => 'test.tld', 165 | } 166 | 167 | ##### `$ensure = present` 168 | Ensure the zone is present. 169 | ##### `$is_dynamic = false` 170 | Boolean to set if a zone is dynamic. 171 | ##### `$allow_update = []` 172 | List of hosts that are allowed to submit dynamic updates for master zones. 173 | ##### `$transfer_source = undef` 174 | Source IP to bind to when requesting a transfer (slave only). 175 | ##### `$zone_type = master` 176 | Specify if the zone is master/slave/forward. 177 | ##### `$zone_ttl = undef` 178 | Time to live for your zonefile (master only). 179 | ##### `$zone_contact = undef` 180 | Valid contact record (master only). 181 | ##### `$zone_serial = undef` 182 | Zone serial (master only). 183 | ##### `$zone_refresh = 3h` 184 | Time between each slave refresh (master only). 185 | ##### `$zone_retry = 1h` 186 | Time between each slave retry (master only). 187 | ##### `$zone_expirancy = 1w` 188 | Slave expiracy time (master only). 189 | ##### `$zone_ns = []` 190 | Valid NS for this zone (master only). 191 | ##### `$zone_xfers = undef` 192 | Valid xfers for zone (master only). 193 | ##### `$zone_masters = undef` 194 | Valid master for this zone (slave only). 195 | ##### `$zone_forwarders = undef` 196 | Valid forwarders for this zone (forward only). 197 | ##### `$zone_origin = undef` 198 | The origin of the zone. 199 | ##### `$zone_notify = undef` 200 | IPs to use for also-notify entry. 201 | ##### `$if_slave = false` 202 | Boolean to set if a zone is slave. 203 | 204 | ### bind::key 205 | 206 | Creates a key for dynamic zones. 207 | The 'secret' value is the key generated by dnssec-keygen. 208 | 209 | bind::key { 'key_dyn.test.tld': 210 | ensure => present, 211 | secret => 'xUjDQqpBHao/o7mR2dza2/Tv2DQVo9pEuMfMwhdfzeaEFZAvwA=' 212 | } 213 | 214 | bind::zone {'dyn.test.tld': 215 | zone_contact => 'contact.test.tld', 216 | zone_ns => ['ns0.test.tld'], 217 | zone_serial => '2012112901', 218 | zone_ttl => '604800', 219 | zone_origin => 'dyn.test.tld', 220 | is_dynamic => true, 221 | allow_update => ['key_dyn.test.tld'] 222 | } 223 | 224 | ##### `$ensure = present` 225 | Ensure the key is present. 226 | ##### `$secret` 227 | Key content. 228 | ##### `$algorithm = hmac-md5` 229 | Key algorithm. 230 | 231 | ## Simple Example 232 | 233 | bind::zone {'example.com': 234 | ensure => 'present', 235 | zone_contact => 'contact.example.com', 236 | zone_ns => ['ns0.example.com'], 237 | zone_serial => '2012112901', 238 | zone_ttl => '604800', 239 | zone_origin => 'example.com', 240 | } 241 | 242 | bind::a { 'example.com': 243 | ensure => 'present', 244 | zone => 'example.com', 245 | ptr => false, 246 | hash_data => { 247 | 'host1' => { owner => '192.168.0.1', }, 248 | 'host2' => { owner => '192.168.0.2', }, 249 | }, 250 | } 251 | 252 | 253 | ## Contributing 254 | 255 | Please report bugs and feature request using [GitHub issue 256 | tracker](https://github.com/camptocamp/puppet-bind/issues). 257 | 258 | For pull requests, it is very much appreciated to check your Puppet manifest 259 | with [puppet-lint](https://github.com/camptocamp/puppet-bind/issues) to follow the recommended Puppet style guidelines from the 260 | [Puppet Labs style guide](http://docs.puppetlabs.com/guides/style_guide.html). 261 | 262 | ## License 263 | 264 | Copyright (c) 2013 All rights reserved. 265 | 266 | Licensed under the Apache License, Version 2.0 (the "License"); 267 | you may not use this file except in compliance with the License. 268 | You may obtain a copy of the License at 269 | http://www.apache.org/licenses/LICENSE-2.0 270 | Unless required by applicable law or agreed to in writing, software 271 | distributed under the License is distributed on an "AS IS" BASIS, 272 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 273 | See the License for the specific language governing permissions and 274 | limitations under the License. 275 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'puppetlabs_spec_helper/rake_tasks' 2 | require 'puppet-lint/tasks/puppet-lint' 3 | 4 | Rake::Task[:lint].clear 5 | PuppetLint::RakeTask.new :lint do |config| 6 | config.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp", "vendor/**/*.pp"] 7 | config.disable_checks = ['140chars'] 8 | config.fail_on_warnings = true 9 | end 10 | 11 | PuppetSyntax.exclude_paths = ["spec/fixtures/**/*.pp", "vendor/**/*"] 12 | 13 | # Publishing tasks 14 | unless RUBY_VERSION =~ /^1\./ 15 | require 'puppet_blacksmith' 16 | require 'puppet_blacksmith/rake_tasks' 17 | end 18 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/bind_check_hostname.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bind_check_hostname.rb 3 | # 4 | module Puppet::Parser::Functions 5 | newfunction(:bind_check_hostname, :type => :rvalue, :doc => <<-EOS 6 | Prepare checked string for *is_domain_name()* (from stdlib) by removing /^\*\.?/ 7 | if present. *is_domain_name()* doesn't want any wildcard, which makes sense in 8 | most cases. 9 | Usage: bind_check_hostname(hostname, type) 10 | EOS 11 | ) do |arguments| 12 | 13 | if (arguments.size != 2) then 14 | raise(Puppet::ParseError, "bind_check_hostname(): Wrong number of arguments "+ 15 | "given #{arguments.size} for 2") 16 | end 17 | 18 | record = arguments[0] 19 | type = arguments[1] 20 | 21 | # Allows '@' 22 | return true if record == '@' 23 | 24 | # All is allowed for SRV and TXT record types 25 | return true if type == 'SRV' 26 | return true if type == 'TXT' 27 | 28 | # Allow wildcard only at the begining 29 | # As we're calling stdlib's *is_domain_name()* 30 | # which doesn't accept wildcards, we just clean it 31 | # from the record, and pass this new string to 32 | # *is_domain_name()* 33 | domain = record.sub(/^\*\.?/, '') 34 | 35 | # Nothing left to check, and is_domain_name fails empty 36 | return true if domain == '' 37 | 38 | return function_is_domain_name([domain]) 39 | end 40 | end 41 | 42 | # vim: set ts=2 sw=2 et : 43 | -------------------------------------------------------------------------------- /manifests/a.pp: -------------------------------------------------------------------------------- 1 | # = Definition: bind::a 2 | # 3 | # Creates an IPv4 record. 4 | # 5 | # Arguments: 6 | # *$zone*: Bind::Zone name 7 | # *zone_arpa*: needed if you set $ptr to true 8 | # *$ptr*: set it to true if you want the related PTR records 9 | # NOTE: don't forget to create the zone! 10 | # 11 | # For other arguments, please refer to bind::records ! 12 | # 13 | define bind::a( 14 | $zone, 15 | $hash_data, 16 | $ensure = present, 17 | $zone_arpa = undef, 18 | $ptr = true, 19 | $content = undef, 20 | $content_template = undef, 21 | ) { 22 | 23 | validate_string($ensure) 24 | validate_re($ensure, ['present', 'absent'], 25 | "\$ensure must be either 'present' or 'absent', got '${ensure}'") 26 | 27 | validate_string($zone) 28 | validate_string($zone_arpa) 29 | validate_hash($hash_data) 30 | validate_bool($ptr) 31 | 32 | if ($ptr and !$zone_arpa) { 33 | fail 'You need zone_arpa if you want the PTR!' 34 | } 35 | 36 | if ($content and $content_template) { 37 | fail '$content and $content_template are mutually exclusive' 38 | } 39 | 40 | bind::record {$name: 41 | ensure => $ensure, 42 | zone => $zone, 43 | hash_data => $hash_data, 44 | record_type => 'A', 45 | content => $content, 46 | content_template => $content_template, 47 | } 48 | 49 | if $ptr { 50 | bind::record {"PTR ${name}": 51 | ensure => $ensure, 52 | zone => $zone_arpa, 53 | record_type => 'PTR', 54 | ptr_zone => $zone, 55 | hash_data => $hash_data, 56 | content => $content, 57 | content_template => $content_template, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /manifests/acl.pp: -------------------------------------------------------------------------------- 1 | define bind::acl( 2 | $ensure=present, 3 | $acls = [], 4 | ) { 5 | 6 | validate_array($acls) 7 | validate_string($ensure) 8 | validate_re($ensure, ['^present$', '^absent$']) 9 | 10 | $_ensure = $ensure? { 11 | 'present' => 'file', 12 | default => 'absent', 13 | } 14 | 15 | $_name = regsubst($name, '\s', '-', 'G') 16 | 17 | file {$name: 18 | ensure => $_ensure, 19 | content => template('bind/acl.erb'), 20 | group => 'root', 21 | mode => '0640', 22 | notify => Exec['reload bind9'], 23 | owner => $bind::params::bind_group, 24 | path => "${bind::params::acls_directory}/${_name}", 25 | } 26 | 27 | if $ensure == 'present' { 28 | concat::fragment {"acl.${_name}": 29 | content => "include \"${bind::params::acls_directory}/${_name}\";\n", 30 | notify => Exec['reload bind9'], 31 | target => "${bind::params::config_base_dir}/acls.conf", 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /manifests/config.pp: -------------------------------------------------------------------------------- 1 | class bind::config { 2 | include ::bind::params 3 | 4 | concat {"${bind::params::config_base_dir}/${bind::params::named_local_name}": 5 | owner => root, 6 | group => $bind::params::bind_group, 7 | mode => '0644', 8 | force => true, 9 | } 10 | 11 | concat {"${bind::params::config_base_dir}/acls.conf": 12 | force => true, 13 | group => 'root', 14 | mode => '0644', 15 | owner => 'root', 16 | } 17 | 18 | $conf = deep_merge($bind::params::default_config, $bind::config) 19 | $logging = deep_merge($bind::params::default_logging, $bind::logging) 20 | 21 | file {"${bind::params::config_base_dir}/${bind::params::named_conf_name}": 22 | ensure => file, 23 | content => template('bind/named.conf.erb'), 24 | group => 'root', 25 | mode => '0644', 26 | owner => 'root', 27 | } 28 | 29 | file {"${bind::params::config_base_dir}/named.conf.options": 30 | ensure => file, 31 | content => template('bind/named.conf.options.erb'), 32 | group => 'root', 33 | mode => '0644', 34 | notify => Exec['reload bind9'], 35 | owner => 'root', 36 | } 37 | 38 | file {$bind::params::zones_directory: 39 | ensure => directory, 40 | owner => root, 41 | group => root, 42 | mode => '0755', 43 | purge => true, 44 | force => true, 45 | recurse => true, 46 | } 47 | 48 | file {$bind::params::pri_directory: 49 | ensure => directory, 50 | owner => root, 51 | group => root, 52 | mode => '0755', 53 | purge => true, 54 | force => true, 55 | recurse => true, 56 | } 57 | 58 | file {$bind::params::keys_directory: 59 | ensure => directory, 60 | owner => root, 61 | group => $bind::params::bind_group, 62 | mode => '0750', 63 | purge => true, 64 | force => true, 65 | recurse => true, 66 | } 67 | 68 | file {$bind::params::acls_directory: 69 | ensure => directory, 70 | owner => root, 71 | group => $bind::params::bind_group, 72 | mode => '0750', 73 | purge => true, 74 | force => true, 75 | recurse => true, 76 | } 77 | 78 | file {$bind::params::views_directory: 79 | ensure => directory, 80 | owner => 'root', 81 | group => 'root', 82 | mode => '0755', 83 | purge => true, 84 | force => true, 85 | recurse => true, 86 | } 87 | 88 | file {$bind::params::dynamic_directory: 89 | ensure => directory, 90 | owner => root, 91 | group => $bind::params::bind_group, 92 | mode => '0775', 93 | } 94 | 95 | file {'/var/log/named': 96 | ensure => directory, 97 | group => 'adm', 98 | mode => '0750', 99 | owner => $bind::params::bind_user, 100 | seltype => 'named_log_t', 101 | } 102 | 103 | $opts = { 104 | 105 | 'include' => "\"${bind::params::config_base_dir}/${bind::params::default_zones_file}\"", 106 | 'match-clients' => [ '"any"' ], 107 | 'recursion' => 'no', 108 | } 109 | 110 | $options = deep_merge($opts, $bind::default_view) 111 | 112 | ::bind::view {'default': 113 | options => $options, 114 | order => 100, 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /manifests/generate.pp: -------------------------------------------------------------------------------- 1 | # = definition: bind::generate 2 | # 3 | # Creates a $GENERATE directive for a specific zone 4 | # 5 | # == Arguments 6 | # $zone: mandatory - zone name. Must reflect a bind::zone resource 7 | # $range: mandatory - range allocated to internal generate directive. 8 | # Must be in the form 'first-last', like '2-254' 9 | # $record_type: mandatory - must be one of PTR, CNAME, DNAME, A, AAAA and NS 10 | # $lhs: mandatory - generated name (see examples) 11 | # $rhs: mandatory - record target (see examples) 12 | # $record_class: optional - incompatible with pre-9.3 bind versions 13 | # $ttl: optional - time tolive for generated records 14 | # 15 | # == Examples 16 | # 17 | # bind::zone {'test.tld': 18 | # zone_contact => 'contact.test.tld', 19 | # zone_ns => 'ns0.test.tld', 20 | # zone_serial => '2012112901', 21 | # zone_ttl => '604800', 22 | # zone_origin => 'test.tld', 23 | # } 24 | # ## Generate A records 25 | # bind::generate {'a-records': 26 | # zone => 'test.tld', 27 | # range => '2-100', 28 | # record_type => 'A', 29 | # lhs => 'dhcp-$', # creates dhcp-2.test.tld, dhcp-3.test.tld ... 30 | # rhs => '10.10.0.$', # creates IP 10.10.0.2, 10.10.0.3 ... 31 | # } 32 | # ## Means: dig dhcp-10.test.tld will resolv to 10.10.0.10 33 | # 34 | # ## Generate CNAME records 35 | # bind::generate {'a-records': 36 | # zone => 'test.tld', 37 | # range => '2-100', 38 | # record_type => 'CNAME', 39 | # lhs => 'dhcp-$', # creates dhcp-2.test.tld, dhcp-3.test.tld ... 40 | # rhs => 'dhcp$', # creates IP dhcp2.test.tld, dhcp3.test.tld ... 41 | # } 42 | # ## Means: dig dhcp10.test.tld => dhcp-10.test.tld => 10.10.0.10 43 | # 44 | # bind::zone {'0.10.10.IN-ADDR.ARPA': 45 | # zone_contact => 'contact.test.tld', 46 | # zone_ns => 'ns0.test.tld', 47 | # zone_serial => '2012112901', 48 | # zone_ttl => '604800', 49 | # zone_origin => '0.10.10.IN-ADDR.ARPA', 50 | # } 51 | # ## Generates PTR 52 | # bind::generate {'ptr-records': 53 | # zone => '0.10.10.IN-ADDR.ARPA', 54 | # range => '2-100', 55 | # record_type => 'PTR', 56 | # lhs => '$.0.10.10.IN-ADDR.ARPA.', # 2.0.10.10.IN-ADDR.ARPA ... 57 | # rhs => 'dhcp-$.test.tld.', # creates dhcp-2.test.tld ... 58 | # } 59 | # ## Means: dig 10.10.0.10 will resolv to dhcp-10.test.tld 60 | # 61 | # 62 | # For more information regarding this directive 63 | # and the definition arguments, please have a 64 | # look at 65 | # http://www.bind9.net/manual/bind/9.3.2/Bv9ARM.ch06.html#id2566761 66 | # 67 | # NOTE: in order to prevent some funky-funny thing, the orignal 68 | # "class" and "type" variables 69 | # are renamed as $record_class and $record_type in this definition. 70 | # 71 | define bind::generate( 72 | $zone, 73 | $range, 74 | $record_type, 75 | $lhs, 76 | $rhs, 77 | $ensure = present, 78 | $record_class = undef, 79 | $ttl = undef, 80 | ) { 81 | 82 | include ::bind::params 83 | 84 | validate_string($ensure) 85 | validate_re($ensure, ['present', 'absent'], 86 | "\$ensure must be either 'present' or 'absent', got '${ensure}'") 87 | 88 | validate_string($zone) 89 | validate_string($range) 90 | validate_string($record_type) 91 | validate_string($lhs) 92 | validate_string($rhs) 93 | validate_string($record_class) 94 | validate_string($ttl) 95 | 96 | if $ensure == 'present' { 97 | ::concat::fragment {"${name}.generate": 98 | target => "${bind::params::pri_directory}/${zone}.conf", 99 | content => template('bind/generate.erb'), 100 | notify => Service['bind9'], 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /manifests/init.pp: -------------------------------------------------------------------------------- 1 | # = Class: bind 2 | # Include this class to install bind9 server on your node. 3 | # 4 | # Bind documentation: 5 | # http://www.bind9.net/manuals 6 | # 7 | # Limitations: 8 | # This modules is valid for Bind 9.7.1 (squeeze version). 9 | # For 9.7.2, it will be really limited (no view nor ACL support). 10 | # 11 | # = Parameters 12 | # 13 | # $chroot:: If the chroot version of bind should be used. Default: false 14 | # 15 | # 16 | # Example: 17 | # 18 | # node 'ns1.domain.ltd' { 19 | # 20 | # include bind 21 | # 22 | # bind::zone {'domain.ltd': 23 | # ensure => present, 24 | # zone_contact => "contact.domain.ltd", 25 | # zone_ns => $fqdn, 26 | # zone_serial => '2010110804', 27 | # zone_ttl => '604800', 28 | # } 29 | # 30 | # bind::a {"ns $fqdn": 31 | # zone => 'domain.ltd', 32 | # owner => "${fqdn}.", 33 | # host => $ipaddress, 34 | # } 35 | # 36 | # bind::a {'mail.domain.ltd': 37 | # zone => 'domain.ltd', 38 | # owner => 'mail', 39 | # host => '6.6.6.6', 40 | # } 41 | # 42 | # bind::mx {'mx1': 43 | # zone => 'domain.ltd', 44 | # owner => '@', 45 | # priority => 1, 46 | # host => 'mail.domain.ltd', 47 | # } 48 | # } 49 | # 50 | class bind( 51 | $chroot = false, 52 | $default_view = {}, 53 | $config = {}, 54 | $logging = {}, 55 | ) { 56 | anchor { 'bind::begin': } 57 | -> class { '::bind::install': } 58 | -> class { '::bind::config': } 59 | ~> class { '::bind::service': } 60 | -> anchor { 'bind::end': } 61 | 62 | exec {'reload bind9': 63 | command => $bind::params::service_restart, 64 | onlyif => "named-checkconf -jz ${bind::params::config_base_dir}/${bind::params::named_conf_name}", 65 | refreshonly => true, 66 | path => $::path, 67 | require => Class['bind::service'], 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /manifests/install.pp: -------------------------------------------------------------------------------- 1 | class bind::install { 2 | include ::bind::params 3 | package { 'bind9': 4 | ensure => present, 5 | name => $bind::params::package_name, 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /manifests/key.pp: -------------------------------------------------------------------------------- 1 | # = Definition: bind::key 2 | # 3 | # Helper to manage dns keys (NOT dnssec) 4 | # used mainly for nsupdate (dynamic updates) 5 | # 6 | # Arguments: 7 | # *$secret*: key content 8 | # *$algorithm*: key algorithm. Default hmac-md5 9 | # 10 | # This definition does NOT generate the key, please refer 11 | # to Bind9 documentation regarding dynamic update setup and 12 | # key pair generation. 13 | # 14 | define bind::key( 15 | $secret, 16 | $ensure = present, 17 | $algorithm = 'hmac-md5', 18 | ) { 19 | 20 | include ::bind::params 21 | 22 | validate_string($ensure) 23 | validate_re($ensure, ['present', 'absent'], 24 | "\$ensure must be either 'present' or 'absent', got '${ensure}'") 25 | 26 | validate_string($algorithm) 27 | validate_string($secret) 28 | 29 | 30 | file {"${bind::params::keys_directory}/${name}.conf": 31 | ensure => $ensure, 32 | mode => '0600', 33 | owner => $bind::params::bind_user, 34 | group => $bind::params::bind_group, 35 | content => template("${module_name}/dnskey.conf.erb"), 36 | } 37 | 38 | if $ensure == 'present' { 39 | concat::fragment {"dnskey.${name}": 40 | target => "${bind::params::config_base_dir}/${bind::params::named_local_name}", 41 | content => "include \"${bind::params::keys_directory}/${name}.conf\";\n", 42 | notify => Exec['reload bind9'], 43 | require => Package['bind9'], 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /manifests/mx.pp: -------------------------------------------------------------------------------- 1 | # = Definition: bind::mx 2 | # Creates an MX record. 3 | # 4 | # Arguments: 5 | # *$zone*: Bind::Zone name 6 | # *$owner*: owner of the Resource Record 7 | # *$priority*: MX record priority 8 | # *$host*: target of the Resource Record 9 | # *$ttl*: Time to Live for the Resource Record. Optional. 10 | # 11 | define bind::mx ( 12 | $zone, 13 | $host, 14 | $priority, 15 | $ensure = present, 16 | $owner = undef, 17 | $ttl = undef, 18 | ) { 19 | 20 | validate_string($ensure) 21 | validate_re($ensure, ['present', 'absent'], 22 | "\$ensure must be either 'present' or 'absent', got '${ensure}'") 23 | 24 | validate_string($zone) 25 | validate_string($host) 26 | validate_string($priority) 27 | validate_string($owner) 28 | validate_string($ttl) 29 | 30 | $_owner = $owner ? { 31 | '' => $name, 32 | default => $owner 33 | } 34 | 35 | if $ensure == 'present' { 36 | concat::fragment {"bind.${name}": 37 | target => "${bind::params::pri_directory}/${zone}.conf", 38 | content => template('bind/mx-record.erb'), 39 | notify => Service['bind9'], 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /manifests/params.pp: -------------------------------------------------------------------------------- 1 | # = Class: bind::params 2 | # Setup parameters based on OS 3 | # You should NOT include this class as is, as it won't work at all! 4 | # Please refer to Class['bind']. 5 | 6 | class bind::params { 7 | $default_logging = { 8 | 'channels' => { 9 | 'simple_log' => { 10 | 'file' => '"/var/log/named/bind.log"', 11 | 'severity' => 'warning', 12 | 'print-time' => 'yes', 13 | 'print-severity' => 'yes', 14 | 'print-category' => 'yes', 15 | }, 16 | }, 17 | 'categories' => { 18 | 'default' => 'simple_log', 19 | }, 20 | } 21 | 22 | if $::osfamily == 'Debian' { 23 | $package_name = 'bind9' 24 | $service_name = 'bind9' 25 | $bind_user = 'bind' 26 | $bind_group = 'bind' 27 | $service_has_status = true 28 | #$service_pattern will only be used if $service_has_status is false 29 | $service_pattern = undef 30 | $service_restart = '/etc/init.d/bind9 reload' 31 | $config_base_dir = '/etc/bind' 32 | $named_conf_name = 'named.conf' 33 | $named_local_name = 'named.conf.local' 34 | $zones_directory = '/etc/bind/zones' 35 | $pri_directory = '/etc/bind/pri' 36 | $keys_directory = '/etc/bind/keys' 37 | $dynamic_directory = '/etc/bind/dynamic' 38 | $acls_directory = '/etc/bind/acls' 39 | $views_directory = '/etc/bind/views' 40 | $default_zones_file = 'named.conf.default-zones' 41 | $default_config = { 42 | 'directory' => '"/var/cache/bind"', 43 | 'dnssec-validation' => 'auto', 44 | 'auth-nxdomain' => 'no', 45 | 'listen-on-v6' => ['any'], 46 | } 47 | if $bind::chroot { 48 | fail('Chroot mode is not yet implemented for Debian in this module.') 49 | } 50 | } 51 | elsif $::osfamily == 'RedHat' { 52 | if $bind::chroot { 53 | $package_name = 'bind-chroot' 54 | $service_name = 'named-chroot' 55 | # moving this under named so it also is available within the chroot. 56 | $named_local_name = 'named/named.conf.local' 57 | } else { 58 | $package_name = 'bind' 59 | $service_name = 'named' 60 | $named_local_name = 'named.conf.local' 61 | } 62 | $bind_user = 'named' 63 | $bind_group = 'named' 64 | $service_pattern = undef 65 | if versioncmp($::operatingsystemmajrelease,'7') < 0 { 66 | $service_restart = "/etc/init.d/${service_name} restart" 67 | $service_has_status = false 68 | } else { 69 | $service_restart = "/usr/bin/systemctl reload ${service_name}" 70 | $service_has_status = true 71 | } 72 | $config_base_dir = '/etc' 73 | $named_conf_name = 'named.conf' 74 | $zones_directory = '/etc/named/zones' 75 | $pri_directory = '/etc/named/pri' 76 | $keys_directory = '/etc/named/keys' 77 | $dynamic_directory = '/etc/named/dynamic' 78 | $acls_directory = '/etc/named/acls' 79 | $views_directory = '/etc/named/views' 80 | $default_zones_file = 'named.rfc1912.zones' 81 | $default_config = { 82 | 'allow-query' => ['localhost'], 83 | 'auth-nxdomain' => 'no', 84 | 'bindkeys-file' => '"/etc/named.iscdlv.key"', 85 | 'directory' => '"/var/named"', 86 | 'dnssec-enable' => 'yes', 87 | 'dnssec-validation' => 'yes', 88 | 'dump-file' => '"/var/named/data/cache_dump.db"', 89 | 'managed-keys-directory' => '"/var/named/dynamic"', 90 | 'memstatistics-file' => '"/var/named/data/named_mem_stats.txt"', 91 | 'pid-file' => '"/run/named/named.pid"', 92 | 'listen-on' => ['127.0.0.1'], 93 | 'listen-on-v6' => ['::1'], 94 | 'session-keyfile' => '"/run/named/session.key"', 95 | 'statistics-file' => '"/var/named/data/named_stats.txt"', 96 | } 97 | } 98 | else { 99 | fail "Unknown ${::operatingsystem}" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /manifests/record.pp: -------------------------------------------------------------------------------- 1 | # = Definition: bind::record 2 | # 3 | # Helper to create any record you want (but NOT MX, please refer to Bind::Mx) 4 | # 5 | # Arguments: 6 | # *$zone*: Bind::Zone name 7 | # *$record_type*: Resource record type 8 | # *$ptr_zone*: PTR zone - optional 9 | # *$content_template*: Allows you to do your own template, letting you 10 | # use your own hash_data content structure 11 | # *$hash_data: Hash containing data, by default in this form: 12 | # { 13 | # => { 14 | # owner => , 15 | # ttl => (optional), 16 | # record_class => , (optional - default IN) 17 | # }, 18 | # => { 19 | # owner => , 20 | # ttl => (optional), 21 | # ptr => false, (optional, default to true) 22 | # record_class => , (optional - default IN) 23 | # }, 24 | # ... 25 | # } 26 | # 27 | define bind::record ( 28 | $zone, 29 | $hash_data, 30 | $record_type, 31 | $ensure = present, 32 | $content = undef, 33 | $content_template = undef, 34 | $ptr_zone = undef, 35 | ) { 36 | 37 | validate_string($ensure) 38 | validate_re($ensure, ['present', 'absent'], 39 | "\$ensure must be either 'present' or 'absent', got '${ensure}'") 40 | 41 | validate_string($zone) 42 | validate_string($record_type) 43 | validate_string($ptr_zone) 44 | validate_hash($hash_data) 45 | 46 | if ($content_template and $content) { 47 | fail '$content and $content_template are mutually exclusive' 48 | } 49 | 50 | if($content_template){ 51 | warning '$content_template is deprecated. Please use $content parameter.' 52 | validate_string($content_template) 53 | $record_content = template($content_template) 54 | }elsif($content){ 55 | $record_content = $content 56 | }else{ 57 | $record_content = template('bind/default-record.erb') 58 | } 59 | 60 | if $ensure == 'present' { 61 | concat::fragment {"${zone}.${record_type}.${name}": 62 | target => "${bind::params::pri_directory}/${zone}.conf", 63 | content => $record_content, 64 | order => '10', 65 | notify => Service['bind9'], 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /manifests/service.pp: -------------------------------------------------------------------------------- 1 | class bind::service { 2 | include ::bind::params 3 | 4 | service { 'bind9': 5 | ensure => running, 6 | name => $bind::params::service_name, 7 | enable => true, 8 | restart => $bind::params::service_restart, 9 | hasstatus => $bind::params::service_has_status, 10 | pattern => $bind::params::service_pattern, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /manifests/view.pp: -------------------------------------------------------------------------------- 1 | define bind::view( 2 | $ensure = 'present', 3 | $options = { 4 | 'recursion' => 'no', 5 | }, 6 | $order = 10, 7 | ) { 8 | 9 | validate_re($ensure, ['^present$', '^absent$']) 10 | validate_hash($options) 11 | 12 | $_ensure = $ensure? { 13 | 'present' => 'file', 14 | default => $ensure, 15 | } 16 | 17 | $_name = regsubst($name, '\s', '-', 'G') 18 | 19 | file {"${bind::params::views_directory}/${_name}.view": 20 | ensure => $_ensure, 21 | content => template('bind/view.erb'), 22 | group => 'root', 23 | mode => '0644', 24 | owner => 'root', 25 | } 26 | 27 | if $ensure == 'present' { 28 | concat {"${bind::params::views_directory}/${_name}.zones": 29 | force => true, 30 | group => 'root', 31 | mode => '0644', 32 | owner => 'root', 33 | } 34 | concat::fragment {"named.local.view.${_name}": 35 | target => "${bind::params::config_base_dir}/${bind::params::named_local_name}", 36 | content => "include \"${bind::params::views_directory}/${_name}.view\";\n", 37 | order => $order, 38 | notify => Exec['reload bind9'], 39 | require => Class['bind::install'], 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /manifests/zone.pp: -------------------------------------------------------------------------------- 1 | # = Definition: bind::zone 2 | # 3 | # Creates a valid Bind9 zone. 4 | # 5 | # Arguments: 6 | # *$zone_type*: String. Specify if the zone is master/slave/forward. Default master 7 | # *$transfer_source*: IPv4 address. Source IP to bind to when requesting a transfer (slave only) 8 | # *$zone_ttl*: Time period. Time to live for your zonefile (master only) 9 | # *$zone_contact*: Valid contact record (master only) 10 | # *$zone_serial*: Integer. Zone serial (master only) 11 | # *$zone_refresh*: Time period. Time between each slave refresh (master only) 12 | # *$zone_retry*: Time period. Time between each slave retry (master only) 13 | # *$zone_expiracy*: Time period. Slave expiracy time (master only) 14 | # *$zone_ns*: Valid NS for this zone (master only) 15 | # *$zone_xfers*: IPs. Valid xfers for zone (master only) 16 | # *$zone_masters*: IPs. Valid master for this zone (slave only) 17 | # *$zone_forwarders*: IPs. Valid forwarders for this zone (forward only) 18 | # *$zone_origin*: The origin of the zone 19 | # *$zone_notify*: IPs to use for also-notify entry 20 | # 21 | define bind::zone ( 22 | $ensure = present, 23 | $is_dynamic = false, 24 | $allow_update = [], 25 | $transfer_source = undef, 26 | $view = 'default', 27 | $zone_type = 'master', 28 | $zone_ttl = undef, 29 | $zone_contact = undef, 30 | $zone_serial = undef, 31 | $zone_refresh = '3h', 32 | $zone_retry = '1h', 33 | $zone_expiracy = '1w', 34 | $zone_ns = [], 35 | $zone_xfers = undef, 36 | $zone_masters = undef, 37 | $zone_forwarders = undef, 38 | $zone_origin = undef, 39 | $zone_notify = undef, 40 | $is_slave = false, 41 | ) { 42 | 43 | include ::bind::params 44 | 45 | validate_string($ensure) 46 | validate_re($ensure, ['present', 'absent'], 47 | "\$ensure must be either 'present' or 'absent', got '${ensure}'") 48 | 49 | validate_bool($is_slave) 50 | validate_bool($is_dynamic) 51 | validate_array($allow_update) 52 | validate_string($transfer_source) 53 | validate_string($view) 54 | validate_string($zone_type) 55 | validate_string($zone_ttl) 56 | validate_string($zone_contact) 57 | validate_string($zone_serial) 58 | validate_string($zone_refresh) 59 | validate_string($zone_retry) 60 | validate_string($zone_expiracy) 61 | validate_array($zone_ns) 62 | 63 | validate_string($zone_origin) 64 | 65 | $_view = regsubst($view, '\s', '-', 'G') 66 | 67 | # add backwards support for is_slave parameter 68 | if ($is_slave) and ($zone_type == 'master') { 69 | warning('$is_slave is deprecated. You should set $zone_type = \'slave\'') 70 | $int_zone_type = 'slave' 71 | } else { 72 | $int_zone_type = $zone_type 73 | } 74 | 75 | if ($int_zone_type != 'master' and $is_dynamic) { 76 | fail "Zone '${name}' cannot be ${int_zone_type} AND dynamic!" 77 | } 78 | 79 | if ($transfer_source and $int_zone_type != 'slave') { 80 | fail "Zone '${name}': transfer_source can be set only for slave zones!" 81 | } 82 | 83 | case $ensure { 84 | 'present': { 85 | concat::fragment {"${_view}.zone.${name}": 86 | target => "${bind::params::views_directory}/${_view}.zones", 87 | content => "include \"${bind::params::zones_directory}/${name}.conf\";\n", 88 | notify => Exec['reload bind9'], 89 | require => Package['bind9'], 90 | } 91 | concat {"${bind::params::zones_directory}/${name}.conf": 92 | owner => root, 93 | group => root, 94 | mode => '0644', 95 | notify => Exec['reload bind9'], 96 | } 97 | concat::fragment {"bind.zones.${name}": 98 | target => "${bind::params::zones_directory}/${name}.conf", 99 | notify => Exec['reload bind9'], 100 | require => Package['bind9'], 101 | } 102 | 103 | case $int_zone_type { 104 | 'master': { 105 | validate_re($zone_contact, '^\S+$', "Wrong contact value for ${name}!") 106 | validate_slength($zone_ns, 255, 3) 107 | validate_re($zone_serial, '^\d+$', "Wrong serial value for ${name}!") 108 | validate_re($zone_ttl, '^\d+$', "Wrong ttl value for ${name}!") 109 | 110 | $conf_file = $is_dynamic? { 111 | true => "${bind::params::dynamic_directory}/${name}.conf", 112 | default => "${bind::params::pri_directory}/${name}.conf", 113 | } 114 | 115 | $require = $is_dynamic? { 116 | true => Bind::Key[$allow_update], 117 | default => undef, 118 | } 119 | 120 | if $is_dynamic { 121 | file {$conf_file: 122 | owner => root, 123 | group => $bind::params::bind_group, 124 | mode => '0664', 125 | replace => false, 126 | content => template('bind/zone-header.erb'), 127 | notify => Exec['reload bind9'], 128 | require => [Package['bind9'], $require], 129 | } 130 | } else { 131 | concat {$conf_file: 132 | owner => root, 133 | group => $bind::params::bind_group, 134 | mode => '0664', 135 | notify => Exec['reload bind9'], 136 | require => Package['bind9'], 137 | } 138 | 139 | concat::fragment {"00.bind.${name}": 140 | target => $conf_file, 141 | order => '01', 142 | content => template('bind/zone-header.erb'), 143 | } 144 | } 145 | 146 | Concat::Fragment["bind.zones.${name}"] { 147 | content => template('bind/zone-master.erb'), 148 | } 149 | 150 | file {"${bind::params::pri_directory}/${name}.conf.d": 151 | ensure => absent, 152 | } 153 | } 154 | 'slave': { 155 | Concat::Fragment["bind.zones.${name}"] { 156 | content => template('bind/zone-slave.erb'), 157 | } 158 | 159 | } 160 | 'forward': { 161 | Concat::Fragment["bind.zones.${name}"] { 162 | content => template('bind/zone-forward.erb'), 163 | } 164 | } 165 | default: { fail("Zone type '${int_zone_type}' not supported.") } 166 | } 167 | } 168 | 'absent': { 169 | file {"${bind::params::pri_directory}/${name}.conf": 170 | ensure => absent, 171 | } 172 | file {"${bind::params::zones_directory}/${name}.conf": 173 | ensure => absent, 174 | } 175 | } 176 | default: {} 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "camptocamp-bind", 3 | "version": "1.4.4", 4 | "author": "Camptocamp", 5 | "description": "Bind defined resource types", 6 | "summary": "Camptocamp Bind Module", 7 | "license": "Apache-2.0", 8 | "source": "https://github.com/camptocamp/puppet-bind", 9 | "project_page": "https://github.com/camptocamp/puppet-bind", 10 | "issues_url": "https://github.com/puppetlabs/puppet-bind/issues", 11 | "operatingsystem_support": [ 12 | { 13 | "operatingsystem": "RedHat", 14 | "operatingsystemrelease": [ 15 | "6", 16 | "7" 17 | ] 18 | }, 19 | { 20 | "operatingsystem": "Debian", 21 | "operatingsystemrelease": [ 22 | "7", 23 | "8" 24 | ] 25 | }, 26 | { 27 | "operatingsystem": "Ubuntu", 28 | "operatingsystemrelease": [ 29 | "14.04" 30 | ] 31 | } 32 | ], 33 | "dependencies": [ 34 | { 35 | "name": "puppetlabs/stdlib", 36 | "version_requirement": ">=3.2.0 <5.0.0" 37 | }, 38 | { 39 | "name": "puppetlabs/concat", 40 | "version_requirement": ">= 1.0.0 <2.0.0" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /spec/.rspec: -------------------------------------------------------------------------------- 1 | --format 2 | s 3 | --colour 4 | --loadby 5 | mtime 6 | --backtrace 7 | -------------------------------------------------------------------------------- /spec/acceptance/bind_advanced_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'bind' do 4 | 5 | let(:serial) { '2016021209' } 6 | 7 | context "With dedicated view, acl and zone" do 8 | 9 | it "should create a view, attach a zone to it and load without error" do 10 | pp = <<-EOS 11 | class {'::bind': 12 | default_view => { 13 | 'match-clients' => ['!127.0.0.0/8', '"any"'], 14 | } 15 | } 16 | ::bind::acl {'internal': 17 | ensure => present, 18 | acls => [ 19 | '127.0.0.0/8', 20 | ], 21 | } 22 | ::bind::view {'my-view': 23 | options => { 24 | 'include' => "\"${bind::params::config_base_dir}/${bind::params::default_zones_file}\"", 25 | 'match-clients' => ['"internal"'], 26 | }, 27 | } 28 | ::bind::zone {'my-zone.tld': 29 | ensure => present, 30 | view => 'my-view', 31 | zone_contact => 'contact.my-zone.tld', 32 | zone_ns => [ 33 | 'ns0.my-zone.tld', 34 | 'ns1.my-zone.tld', 35 | ], 36 | zone_serial => '#{serial}', 37 | zone_ttl => '18600', 38 | } 39 | ::bind::a {'A records': 40 | ensure => present, 41 | zone => 'my-zone.tld', 42 | ptr => false, 43 | hash_data => { 44 | '@' => { owner => '192.168.10.1', }, 45 | 'test' => { owner => '192.168.10.2', }, 46 | 'ns0' => { owner => '192.168.10.252', }, 47 | 'ns1' => { owner => '192.168.10.253', }, 48 | } 49 | } 50 | case $::osfamily { 51 | 'Debian': { 52 | package {'dnsutils': } 53 | } 54 | 'RedHat': { 55 | package {'bind-utils': } 56 | } 57 | } 58 | EOS 59 | 60 | apply_manifest(pp, :cat_failures => true) 61 | apply_manifest(pp, :catch_changes => true) 62 | end 63 | 64 | describe port("53") { 65 | it { 66 | should be_listening.with('tcp') 67 | should be_listening.with('udp') 68 | } 69 | } 70 | 71 | describe command("host -4 ns1.my-zone.tld localhost") do 72 | its(:stdout) {should match /ns1.my-zone.tld has address 192.168.10.253/} 73 | end 74 | describe command("host -4 google-public-dns-b.google.com localhost") do 75 | its(:stdout) {should match /google-public-dns-b.google.com has address 8.8.4.4/} 76 | end 77 | 78 | 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/acceptance/bind_cache_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'bind' do 4 | 5 | let(:serial) { '2016021209' } 6 | 7 | context 'with defaults' do 8 | it 'should apply without error' do 9 | pp = <<-EOS 10 | class { 'bind': 11 | config => { 12 | 'allow-query' => [ 13 | 'any', 14 | ], 15 | 'allow-query-cache' => [ 16 | '127.0.0.0/8', 17 | '::1', 18 | ], 19 | 'allow-recursion' => [ 20 | 'any', 21 | ], 22 | 'forward' => 'first', 23 | 'forwarders' => [ 24 | '8.8.8.8', 25 | '8.8.4.4', 26 | ], 27 | 'listen-on' => [ 28 | 'any', 29 | ], 30 | 'listen-on-v6' => [ 31 | 'any', 32 | ], 33 | 'max-cache-ttl' => '300', 34 | 'max-ncache-ttl' => '300', 35 | }, 36 | default_view => { 37 | 'recursion' => 'yes', 38 | }, 39 | } 40 | case $::osfamily { 41 | 'Debian': { 42 | package {'dnsutils': } 43 | } 44 | 'RedHat': { 45 | package {'bind-utils': } 46 | } 47 | } 48 | EOS 49 | 50 | apply_manifest(pp, :catch_failures => true) 51 | apply_manifest(pp, :catch_changes => true) 52 | end 53 | 54 | describe command("host -4 google-public-dns-a.google.com localhost") do 55 | its(:stdout) {should match /google-public-dns-a.google.com has address 8.8.8.8/} 56 | end 57 | describe command("host -4 www.camptocamp.com localhost") do 58 | its(:stdout) {should match /has address/} 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /spec/acceptance/bind_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'bind' do 4 | 5 | let(:serial) { '2016021209' } 6 | 7 | context 'with defaults' do 8 | it 'should apply without error' do 9 | pp = <<-EOS 10 | class { 'bind': } 11 | EOS 12 | 13 | apply_manifest(pp, :catch_failures => true) 14 | end 15 | it 'should idempotently run' do 16 | pp = <<-EOS 17 | class { 'bind': } 18 | EOS 19 | 20 | apply_manifest(pp, :catch_changes => true) 21 | end 22 | 23 | it 'should create a zone and load it' do 24 | pp = <<-EOS 25 | class {'::bind': } 26 | ::bind::zone {'my-zone.tld': 27 | ensure => present, 28 | zone_contact => 'contact.my-zone.tld', 29 | zone_ns => [ 30 | 'ns0.my-zone.tld', 31 | 'ns1.my-zone.tld', 32 | ], 33 | zone_serial => '#{serial}', 34 | zone_ttl => '18600', 35 | } 36 | ::bind::a {'A records': 37 | ensure => present, 38 | zone => 'my-zone.tld', 39 | ptr => false, 40 | hash_data => { 41 | '@' => { owner => '192.168.10.1', }, 42 | 'test' => { owner => '192.168.10.2', }, 43 | 'ns0' => { owner => '192.168.10.252', }, 44 | 'ns1' => { owner => '192.168.10.253', }, 45 | } 46 | } 47 | case $::osfamily { 48 | 'Debian': { 49 | package {'dnsutils': } 50 | } 51 | 'RedHat': { 52 | package {'bind-utils': } 53 | package {'iproute': } 54 | } 55 | } 56 | EOS 57 | 58 | apply_manifest(pp, :cat_failures => true) 59 | apply_manifest(pp, :catch_changes => true) 60 | end 61 | 62 | describe port('53') do 63 | it { 64 | should be_listening.with('tcp') 65 | should be_listening.with('udp') 66 | } 67 | end 68 | 69 | describe command("host -4 google-public-dns-a.google.com localhost") do 70 | its(:stdout) {should match /not found: 5\(REFUSED\)/} 71 | end 72 | describe command("host -4 ns0.my-zone.tld localhost") do 73 | its(:stdout) {should match /ns0.my-zone.tld has address 192.168.10.252/} 74 | end 75 | end 76 | 77 | end 78 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-5.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-5-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: el-5-x86_64 7 | hypervisor : docker 8 | image: tianon/centos:5.10 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'yum install -y crontabs tar wget which' 13 | - 'sed -i -e "/mingetty/d" /etc/inittab' 14 | CONFIG: 15 | type: aio 16 | log_level: debug 17 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-6.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-6-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: el-6-x86_64 7 | hypervisor : docker 8 | image: centos:6 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'rm -rf /var/run/network/*' 13 | - 'yum install -y crontabs tar wget' 14 | - 'rm /etc/init/tty.conf' 15 | CONFIG: 16 | type: aio 17 | log_level: debug 18 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-7.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-7-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: el-7-x86_64 7 | hypervisor : docker 8 | image: centos:7 9 | docker_preserve_image: true 10 | docker_cmd: '["/usr/sbin/init"]' 11 | docker_image_commands: 12 | - 'yum install -y crontabs tar wget iproute' 13 | CONFIG: 14 | type: aio 15 | log_level: debug 16 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-6.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-6-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: debian-6-amd64 7 | hypervisor : docker 8 | image: debian/eol:squeeze 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y cron locales-all net-tools wget' 13 | CONFIG: 14 | type: aio 15 | log_level: debug 16 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-7.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-7-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: debian-7-amd64 7 | hypervisor : docker 8 | image: debian:7 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y cron locales-all net-tools wget' 13 | CONFIG: 14 | type: aio 15 | log_level: debug 16 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-8.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-8-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: debian-8-amd64 7 | hypervisor : docker 8 | image: debian:8 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y cron locales-all net-tools wget' 13 | - 'rm -f /usr/sbin/policy-rc.d' 14 | CONFIG: 15 | type: aio 16 | log_level: debug 17 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-12.04.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1204-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: ubuntu-12.04-amd64 7 | hypervisor : docker 8 | image: ubuntu:12.04 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y net-tools wget' 13 | - 'locale-gen en_US.UTF-8' 14 | CONFIG: 15 | type: aio 16 | log_level: debug 17 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-14.04.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1404-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: ubuntu-14.04-amd64 7 | hypervisor : docker 8 | image: ubuntu:14.04 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'rm /usr/sbin/policy-rc.d' 13 | - 'rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl' 14 | - 'apt-get install -y net-tools wget' 15 | - 'locale-gen en_US.UTF-8' 16 | CONFIG: 17 | type: aio 18 | log_level: debug 19 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-14.10.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1410-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: ubuntu-14.10-amd64 7 | hypervisor : docker 8 | image: ubuntu:14.10 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'rm /usr/sbin/policy-rc.d' 13 | - 'rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl' 14 | - 'apt-get install -y net-tools wget' 15 | - 'locale-gen en_US.UTF-8' 16 | CONFIG: 17 | type: aio 18 | log_level: debug 19 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-15.04.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1504-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: ubuntu-15.04-amd64 7 | hypervisor : docker 8 | image: ubuntu:15.04 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y net-tools wget' 13 | - 'locale-gen en_US.UTF-8' 14 | CONFIG: 15 | type: aio 16 | log_level: debug 17 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-15.10.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1510-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: ubuntu-15.10-amd64 7 | hypervisor : docker 8 | image: ubuntu:15.10 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y net-tools wget' 13 | - 'locale-gen en_US.UTF-8' 14 | CONFIG: 15 | type: aio 16 | log_level: debug 17 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-16.04.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1604-x64: 3 | default_apply_opts: 4 | order: random 5 | strict_variables: 6 | platform: ubuntu-16.04-amd64 7 | hypervisor : docker 8 | image: ubuntu:16.04 9 | docker_preserve_image: true 10 | docker_cmd: '["/sbin/init"]' 11 | docker_image_commands: 12 | - 'apt-get install -y net-tools wget' 13 | - 'locale-gen en_US.UTF-8' 14 | CONFIG: 15 | type: aio 16 | log_level: debug 17 | -------------------------------------------------------------------------------- /spec/classes/bind_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind' do 4 | 5 | on_supported_os.each do |os, facts| 6 | context "on #{os}" do 7 | let(:facts) do 8 | facts.merge({ 9 | :concat_basedir => '/foo', 10 | }) 11 | end 12 | it { is_expected.to compile.with_all_deps } 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/defines/bind_a_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::a' do 4 | 5 | let (:title) { 'foo.example.com' } 6 | 7 | let(:pre_condition) do 8 | "class { 'bind': }" 9 | end 10 | 11 | on_supported_os.each do |os, facts| 12 | context "on #{os}" do 13 | let(:facts) do 14 | facts.merge({ 15 | :concat_basedir => '/var/lib/puppet/concat', 16 | }) 17 | end 18 | 19 | context 'when using a wrong ensure value' do 20 | let (:params) { { 21 | :ensure => 'running', 22 | :zone => 'foo.example.com', 23 | :hash_data => {}, 24 | } } 25 | 26 | it 'should fail' do 27 | expect { should contain_bind__record('foo.example.com') 28 | }.to raise_error(Puppet::Error, /\$ensure must be either.* got 'running'/) 29 | end 30 | end 31 | 32 | context 'when zone is not specified' do 33 | let (:params) { { 34 | :hash_data => {}, 35 | } } 36 | 37 | it 'should fail' do 38 | expect { should contain_bind__record('foo.example.com') 39 | }.to raise_error(StandardError, /zone/) 40 | end 41 | end 42 | 43 | context 'when passing wrong type for zone' do 44 | let (:params) { { 45 | :zone => ['foo.example.com'], 46 | :hash_data => {}, 47 | } } 48 | 49 | it 'should fail' do 50 | expect { should contain_bind__record('foo.example.com') 51 | }.to raise_error(Puppet::Error, /\["foo.example.com"\] is not a string\./) 52 | end 53 | end 54 | 55 | context 'when hash_data is not specified' do 56 | let (:params) { { 57 | :zone => 'foo.example.com', 58 | } } 59 | 60 | it 'should fail' do 61 | expect { should contain_bind__record('foo.example.com') 62 | }.to raise_error(StandardError, /hash_data/) 63 | end 64 | end 65 | 66 | context 'when passing wrong type for hash_data' do 67 | let (:params) { { 68 | :zone => 'foo.example.com', 69 | :hash_data => 'bar', 70 | } } 71 | 72 | it 'should fail' do 73 | expect { should contain_bind__record('foo.example.com') 74 | }.to raise_error(Puppet::Error, /"bar" is not a Hash\./) 75 | end 76 | end 77 | 78 | context 'when passing wrong type for zone_arpa' do 79 | let (:params) { { 80 | :zone => 'foo.example.com', 81 | :hash_data => {}, 82 | :zone_arpa => ['bar'] 83 | } } 84 | 85 | it 'should fail' do 86 | expect { should contain_bind__record('foo.example.com') 87 | }.to raise_error(Puppet::Error, /\["bar"\] is not a string\./) 88 | end 89 | end 90 | 91 | context 'when passing wrong type for ptr' do 92 | let (:params) { { 93 | :zone => 'foo.example.com', 94 | :hash_data => {}, 95 | :ptr => 'false', 96 | } } 97 | 98 | it 'should fail' do 99 | expect { should contain_bind__record('foo.example.com') 100 | }.to raise_error(Puppet::Error, /"false" is not a boolean\./) 101 | end 102 | end 103 | 104 | context 'when passing ptr without zone_arpa' do 105 | let (:params) { { 106 | :zone => 'foo.example.com', 107 | :hash_data => {}, 108 | :ptr => true, 109 | } } 110 | 111 | it 'should fail' do 112 | expect { should contain_bind__record('foo.example.com') 113 | }.to raise_error(Puppet::Error, /You need zone_arpa if you want the PTR/) 114 | end 115 | end 116 | 117 | context 'when using not using ptr' do 118 | let (:params) { { 119 | :zone => 'foo.example.com', 120 | :hash_data => {}, 121 | :ptr => false 122 | } } 123 | 124 | it { should contain_bind__record('foo.example.com').with({ 125 | :ensure => 'present', 126 | :zone => 'foo.example.com', 127 | :record_type => 'A', 128 | :hash_data => {}, 129 | }) } 130 | end 131 | 132 | context 'when using using ptr' do 133 | let (:params) { { 134 | :zone => 'foo.example.com', 135 | :hash_data => {}, 136 | :ptr => true, 137 | :zone_arpa => 'foobar.arpa' 138 | } } 139 | 140 | it { should contain_bind__record('foo.example.com').with({ 141 | :ensure => 'present', 142 | :zone => 'foo.example.com', 143 | :record_type => 'A', 144 | :hash_data => {}, 145 | }) } 146 | 147 | it { should contain_bind__record('PTR foo.example.com').with({ 148 | :ensure => 'present', 149 | :zone => 'foobar.arpa', 150 | :record_type => 'PTR', 151 | :ptr_zone => 'foo.example.com', 152 | :hash_data => {}, 153 | }) } 154 | end 155 | 156 | context 'when using content' do 157 | let (:params) { { 158 | :zone => 'foo.example.com', 159 | :hash_data => {}, 160 | :ptr => false, 161 | :content => 'abcde' 162 | } } 163 | 164 | it { should contain_bind__record('foo.example.com').with({ 165 | :ensure => 'present', 166 | :zone => 'foo.example.com', 167 | :record_type => 'A', 168 | :hash_data => {}, 169 | :content => 'abcde', 170 | }) } 171 | end 172 | 173 | context 'when using star catchall' do 174 | let (:params) { { 175 | :zone => 'foo.example.com', 176 | :hash_data => {'*' => { 'owner' => 'foo.example.com', }}, 177 | :ptr => false, 178 | } } 179 | 180 | it { should contain_bind__record('foo.example.com').with({ 181 | :ensure => 'present', 182 | :zone => 'foo.example.com', 183 | :record_type => 'A', 184 | :hash_data => {'*' => { 'owner' => 'foo.example.com', }}, 185 | }) } 186 | end 187 | 188 | context 'when using blank host' do 189 | let (:params) { { 190 | :zone => 'foo.example.com', 191 | :hash_data => {'' => { 'owner' => 'foo.example.com', }}, 192 | :ptr => false, 193 | } } 194 | 195 | it { should contain_bind__record('foo.example.com').with({ 196 | :ensure => 'present', 197 | :zone => 'foo.example.com', 198 | :record_type => 'A', 199 | :hash_data => {'' => { 'owner' => 'foo.example.com', }}, 200 | }) } 201 | end 202 | 203 | context 'when passing syntactically incorrect domain name' do 204 | let (:params) { { 205 | :zone => 'foo.example.com', 206 | :hash_data => {'foo).bar' => { 'owner' => 'foo.example.com', }}, 207 | :ptr => false, 208 | } } 209 | 210 | it 'should fail' do 211 | expect { should contain_bind__record('foo.example.com') 212 | }.to raise_error(Puppet::Error, /'foo\)\.bar' is NOT a valid name/) 213 | end 214 | end 215 | end 216 | end 217 | end 218 | -------------------------------------------------------------------------------- /spec/defines/bind_acl_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::acl' do 4 | let(:pre_condition) do 5 | "class {'::bind': }" 6 | end 7 | 8 | on_supported_os.each do |os, facts| 9 | context "on #{os}" do 10 | 11 | let(:facts) do 12 | facts.merge({ 13 | :concat_basedir => '/var/lib/puppet/concat', 14 | }) 15 | end 16 | 17 | let(:confdir) do 18 | case facts[:osfamily] 19 | when 'Debian' 20 | '/etc/bind' 21 | when 'RedHat' 22 | '/etc/named' 23 | end 24 | end 25 | 26 | context 'when using a wrong ensure value' do 27 | let (:title) {'wrong acl'} 28 | let (:params) {{ 29 | :ensure => 'foo', 30 | :acls => ['any'], 31 | }} 32 | 33 | it 'should fail' do 34 | expect { should contain_file('wrong acl') }.to raise_error(Puppet::Error, /does not match/) 35 | end 36 | end 37 | 38 | context 'when passing wrong acls type' do 39 | let (:title) {'wrong acl'} 40 | let (:params) {{ 41 | :ensure => 'present', 42 | :acls => 1, 43 | }} 44 | 45 | it 'should fail' do 46 | expect { should contain_file('wrong acl') }.to raise_error(Puppet::Error, /is not an Array/) 47 | end 48 | end 49 | 50 | context 'correct acl' do 51 | let (:title) {'good acl'} 52 | let (:params) {{ 53 | :ensure => 'present', 54 | :acls => ['!192.168.10.0/24', 'any'], 55 | }} 56 | 57 | it { should contain_file('good acl').with({ 58 | :content => "acl good-acl {\n !192.168.10.0/24;\n any;\n};\n", 59 | :ensure => 'file', 60 | :path => "#{confdir}/acls/good-acl", 61 | }) } 62 | end 63 | 64 | 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/defines/bind_generate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::generate' do 4 | 5 | let (:title) { 'test.tld' } 6 | 7 | let(:pre_condition) do 8 | "class { 'bind': }" 9 | end 10 | 11 | on_supported_os.each do |os, facts| 12 | context "on #{os}" do 13 | let(:facts) do 14 | facts.merge({ 15 | :concat_basedir => '/var/lib/puppet/concat', 16 | }) 17 | end 18 | 19 | let(:confdir) do 20 | case facts[:osfamily] 21 | when 'Debian' 22 | '/etc/bind' 23 | when 'RedHat' 24 | '/etc/named' 25 | end 26 | end 27 | 28 | context 'when using a wrong ensure value' do 29 | let (:params) { { 30 | :ensure => 'running', 31 | :zone => 'test.tld', 32 | :range => '2-100', 33 | :record_type => 'A', 34 | :lhs => 'dhcp-$', 35 | :rhs => '10.10.0.$' 36 | } } 37 | 38 | it 'should fail' do 39 | expect { should contain_concat__fragment('test.tld') 40 | }.to raise_error(Puppet::Error, /\$ensure must be either.* got 'running'/) 41 | end 42 | end 43 | 44 | context 'when zone is not specified' do 45 | let (:params) { { 46 | :range => '2-100', 47 | :record_type => 'A', 48 | :lhs => 'dhcp-$', 49 | :rhs => '10.10.0.$' 50 | } } 51 | 52 | it 'should fail' do 53 | expect { should contain_concat__fragment('test.tld') 54 | }.to raise_error(StandardError, /zone/) 55 | end 56 | end 57 | 58 | context 'when passing wrong type for zone' do 59 | let (:params) { { 60 | :zone => ['test.tld'], 61 | :range => '2-100', 62 | :record_type => 'A', 63 | :lhs => 'dhcp-$', 64 | :rhs => '10.10.0.$' 65 | } } 66 | 67 | it 'should fail' do 68 | expect { should contain_concat__fragment('test.tld') 69 | }.to raise_error(Puppet::Error, /\["test.tld"\] is not a string\./) 70 | end 71 | end 72 | 73 | context 'when range is not specified' do 74 | let (:params) { { 75 | :zone => 'test.tld', 76 | :record_type => 'A', 77 | :lhs => 'dhcp-$', 78 | :rhs => '10.10.0.$' 79 | } } 80 | 81 | it 'should fail' do 82 | expect { should contain_concat__fragment('test.tld') 83 | }.to raise_error(StandardError, /range/) 84 | end 85 | end 86 | 87 | context 'when passing wrong type for range' do 88 | let (:params) { { 89 | :zone => 'test.tld', 90 | :range => ['2-100'], 91 | :record_type => 'A', 92 | :lhs => 'dhcp-$', 93 | :rhs => '10.10.0.$' 94 | } } 95 | 96 | it 'should fail' do 97 | expect { should contain_concat__fragment('test.tld') 98 | }.to raise_error(Puppet::Error, /\["2-100"\] is not a string\./) 99 | end 100 | end 101 | 102 | context 'when record_type is not specified' do 103 | let (:params) { { 104 | :zone => 'test.tld', 105 | :range => '2-100', 106 | :lhs => 'dhcp-$', 107 | :rhs => '10.10.0.$' 108 | } } 109 | 110 | it 'should fail' do 111 | expect { should contain_concat__fragment('test.tld') 112 | }.to raise_error(StandardError, /record_type/) 113 | end 114 | end 115 | 116 | context 'when passing wrong type for record_type' do 117 | let (:params) { { 118 | :zone => 'test.tld', 119 | :range => '2-100', 120 | :record_type => ['A'], 121 | :lhs => 'dhcp-$', 122 | :rhs => '10.10.0.$' 123 | } } 124 | 125 | it 'should fail' do 126 | expect { should contain_concat__fragment('test.tld') 127 | }.to raise_error(Puppet::Error, /\["A"\] is not a string\./) 128 | end 129 | end 130 | 131 | context 'when lhs is not specified' do 132 | let (:params) { { 133 | :zone => 'test.tld', 134 | :range => '2-100', 135 | :record_type => 'A', 136 | :rhs => '10.10.0.$' 137 | } } 138 | 139 | it 'should fail' do 140 | expect { should contain_concat__fragment('test.tld') 141 | }.to raise_error(StandardError, /lhs/) 142 | end 143 | end 144 | 145 | context 'when passing wrong type for lhs' do 146 | let (:params) { { 147 | :zone => 'test.tld', 148 | :range => '2-100', 149 | :record_type => 'A', 150 | :lhs => ['dhcp-$'], 151 | :rhs => '10.10.0.$' 152 | } } 153 | 154 | it 'should fail' do 155 | expect { should contain_concat__fragment('test.tld') 156 | }.to raise_error(Puppet::Error, /\["dhcp-\$"\] is not a string\./) 157 | end 158 | end 159 | 160 | context 'when rhs is not specified' do 161 | let (:params) { { 162 | :zone => 'test.tld', 163 | :range => '2-100', 164 | :record_type => 'A', 165 | :lhs => 'dhcp-$' 166 | } } 167 | 168 | it 'should fail' do 169 | expect { should contain_concat__fragment('test.tld') 170 | }.to raise_error(StandardError, /rhs/) 171 | end 172 | end 173 | 174 | context 'when passing wrong type for rhs' do 175 | let (:params) { { 176 | :zone => 'test.tld', 177 | :range => '2-100', 178 | :record_type => 'A', 179 | :lhs => 'dhcp-$', 180 | :rhs => ['10.10.0.$'] 181 | } } 182 | 183 | it 'should fail' do 184 | expect { should contain_concat__fragment('test.tld') 185 | }.to raise_error(Puppet::Error, /\["10\.10\.0\.\$"\] is not a string\./) 186 | end 187 | end 188 | 189 | context 'when passing wrong type for record_class' do 190 | let (:params) { { 191 | :zone => 'test.tld', 192 | :range => '2-100', 193 | :record_type => 'A', 194 | :lhs => 'dhcp-$', 195 | :rhs => '10.10.0.$', 196 | :record_class => [] 197 | } } 198 | 199 | it 'should fail' do 200 | expect { should contain_concat__fragment('test.tld') 201 | }.to raise_error(Puppet::Error, /\[\] is not a string\./) 202 | end 203 | end 204 | 205 | context 'when passing wrong type for ttl' do 206 | let (:params) { { 207 | :zone => 'test.tld', 208 | :range => '2-100', 209 | :record_type => 'A', 210 | :lhs => 'dhcp-$', 211 | :rhs => '10.10.0.$', 212 | :ttl => ['60'] 213 | } } 214 | 215 | it 'should fail' do 216 | expect { should contain_concat__fragment('test.tld') 217 | }.to raise_error(Puppet::Error, /\["60"\] is not a string\./) 218 | end 219 | end 220 | 221 | context 'when using example 1' do 222 | let (:title) { 'a-record' } 223 | let (:params) { { 224 | :zone => 'test.tld', 225 | :range => '2-100', 226 | :record_type => 'A', 227 | :lhs => 'dhcp-$', 228 | :rhs => '10.10.0.$', 229 | } } 230 | it { should contain_concat__fragment('a-record.generate').with({ 231 | :target => "#{confdir}/pri/test.tld.conf", 232 | :content => "\$GENERATE 2-100 dhcp-\$ A 10.10.0.\$ ; a-record\n" 233 | }) } 234 | end 235 | 236 | context 'when using example 2' do 237 | let (:title) { 'a-record' } 238 | let (:params) { { 239 | :zone => 'test.tld', 240 | :range => '2-100', 241 | :record_type => 'CNAME', 242 | :lhs => 'dhcp-$', 243 | :rhs => '10.10.0.$', 244 | } } 245 | it { should contain_concat__fragment('a-record.generate').with({ 246 | :target => "#{confdir}/pri/test.tld.conf", 247 | :content => "\$GENERATE 2-100 dhcp-\$ CNAME 10.10.0.\$ ; a-record\n" 248 | }) } 249 | end 250 | 251 | context 'when using example 3' do 252 | let (:title) { 'ptr-record' } 253 | let (:params) { { 254 | :zone => '0.10.10.IN-ADDR.ARPA', 255 | :range => '2-100', 256 | :record_type => 'PTR', 257 | :lhs => '$.0.10.10.IN-ADDR.ARPA.', 258 | :rhs => 'dhcp-$.test.tld.', 259 | } } 260 | it { should contain_concat__fragment('ptr-record.generate').with({ 261 | :target => "#{confdir}/pri/0.10.10.IN-ADDR.ARPA.conf", 262 | :content => "$GENERATE 2-100 $.0.10.10.IN-ADDR.ARPA. PTR dhcp-$.test.tld. ; ptr-record\n" 263 | }) } 264 | end 265 | end 266 | end 267 | end 268 | -------------------------------------------------------------------------------- /spec/defines/bind_key_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::key' do 4 | 5 | let (:title) {'update.domain.tld'} 6 | 7 | let(:pre_condition) do 8 | "class { 'bind': }" 9 | end 10 | 11 | on_supported_os.each do |os, facts| 12 | context "on #{os}" do 13 | let(:facts) do 14 | facts.merge({ 15 | :concat_basedir => '/var/lib/puppet/concat', 16 | }) 17 | end 18 | 19 | let(:confdir) do 20 | case facts[:osfamily] 21 | when 'Debian' 22 | '/etc/bind' 23 | when 'RedHat' 24 | '/etc/named' 25 | end 26 | end 27 | 28 | # Validate input 29 | context 'when missing secret value' do 30 | it 'should fail' do 31 | expect { should contain_concat("#{confdir}/keys/update.domain.tld.conf") 32 | }.to raise_error(StandardError, /secret/) 33 | end 34 | end 35 | 36 | context 'when using a wrong ensure value' do 37 | let (:params) { { 38 | :ensure => 'running', 39 | :secret => 'abcdefg' 40 | } } 41 | 42 | it 'should fail' do 43 | expect { should contain_concat("#{confdir}/keys/update.domain.tld.conf") 44 | }.to raise_error(Puppet::Error, /\$ensure must be either.* got 'running'/) 45 | end 46 | end 47 | 48 | context 'when passing wrong type for algorithm' do 49 | let (:params) { { 50 | :secret => 'abcdefg', 51 | :algorithm => ['abcde', 'fghij'] 52 | } } 53 | 54 | it 'should fail' do 55 | expect { should contain_concat("#{confdir}/keys/update.domain.tld.conf") 56 | }.to raise_error(Puppet::Error, /\["abcde", "fghij"\] is not a string\..+/) 57 | end 58 | end 59 | 60 | context 'when passing wrong type for secret' do 61 | let (:params) { { 62 | :secret => ['abcde', 'fghij'] 63 | } } 64 | 65 | it 'should fail' do 66 | expect { should contain_concat("#{confdir}/keys/update.domain.tld.conf") 67 | }.to raise_error(Puppet::Error, /\["abcde", "fghij"\] is not a string\..+/) 68 | end 69 | end 70 | 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/defines/bind_record_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::record' do 4 | 5 | let (:title) { 'CNAME foo.example.com' } 6 | 7 | let(:pre_condition) do 8 | "class { 'bind': }" 9 | end 10 | 11 | on_supported_os.each do |os, facts| 12 | context "on #{os}" do 13 | let(:facts) do 14 | facts.merge({ 15 | :concat_basedir => '/var/lib/puppet/concat', 16 | }) 17 | end 18 | 19 | let :pre_condition do 20 | "class {'bind':}" 21 | end 22 | 23 | context 'when using a wrong ensure value' do 24 | let (:params) { { 25 | :ensure => 'running', 26 | :zone => 'foo.example.com', 27 | :hash_data => {}, 28 | :record_type => 'CNAME' 29 | } } 30 | 31 | it 'should fail' do 32 | expect { should contain_concat__fragment('') 33 | }.to raise_error(Puppet::Error, /\$ensure must be either.* got 'running'/) 34 | end 35 | end 36 | 37 | context 'when zone is not specified' do 38 | let (:params) { { 39 | :hash_data => {}, 40 | :record_type => 'CNAME' 41 | } } 42 | 43 | it 'should fail' do 44 | expect { should contain_concat__fragment('') 45 | }.to raise_error(StandardError, /zone/) 46 | end 47 | end 48 | 49 | context 'when passing wrong type for zone' do 50 | let (:params) { { 51 | :zone => ['foo.example.com'], 52 | :hash_data => {}, 53 | :record_type => 'CNAME' 54 | } } 55 | 56 | it 'should fail' do 57 | expect { should contain_concat__fragment('') 58 | }.to raise_error(Puppet::Error, /\["foo.example.com"\] is not a string\./) 59 | end 60 | end 61 | 62 | context 'when hash_data is not specified' do 63 | let (:params) { { 64 | :zone => 'foo.example.com', 65 | :record_type => 'CNAME' 66 | } } 67 | 68 | it 'should fail' do 69 | expect { should contain_concat__fragment('') 70 | }.to raise_error(StandardError, /hash_data/) 71 | end 72 | end 73 | 74 | context 'when passing wrong type for hash_data' do 75 | let (:params) { { 76 | :zone => 'foo.example.com', 77 | :hash_data => 'bar', 78 | :record_type => 'CNAME' 79 | } } 80 | 81 | it 'should fail' do 82 | expect { should contain_concat__fragment('') 83 | }.to raise_error(Puppet::Error, /"bar" is not a Hash\./) 84 | end 85 | end 86 | 87 | context 'when record_type is not specified' do 88 | let (:params) { { 89 | :zone => 'foo.example.com', 90 | :hash_data => {} 91 | } } 92 | 93 | it 'should fail' do 94 | expect { should contain_concat__fragment('') 95 | }.to raise_error(StandardError, /record_type/) 96 | end 97 | end 98 | 99 | context 'when passing wrong type for record_type' do 100 | let (:params) { { 101 | :zone => 'foo.example.com', 102 | :hash_data => {}, 103 | :record_type => ['CNAME'] 104 | } } 105 | 106 | it 'should fail' do 107 | expect { should contain_concat__fragment('') 108 | }.to raise_error(Puppet::Error, /\["CNAME"\] is not a string\./) 109 | end 110 | end 111 | 112 | context 'when passing wrong type for ptr_zone' do 113 | let (:params) { { 114 | :zone => 'foo.example.com', 115 | :hash_data => {}, 116 | :record_type => 'CNAME', 117 | :ptr_zone => ['bar'] 118 | } } 119 | 120 | it 'should fail' do 121 | expect { should contain_concat__fragment('') 122 | }.to raise_error(Puppet::Error, /\["bar"\] is not a string\./) 123 | end 124 | end 125 | 126 | context 'when passing wrong type for content_template' do 127 | let (:params) { { 128 | :zone => 'foo.example.com', 129 | :hash_data => {}, 130 | :record_type => 'CNAME', 131 | :content_template => ['bar'] 132 | } } 133 | 134 | it 'should fail' do 135 | expect { should contain_concat__fragment('') 136 | }.to raise_error(Puppet::Error, /\["bar"\] is not a string\./) 137 | end 138 | end 139 | 140 | context 'when using default content_template' do 141 | let (:params) { { 142 | :zone => 'foo.example.com', 143 | :hash_data => {}, 144 | :record_type => 'CNAME' 145 | } } 146 | 147 | it { 148 | should contain_concat__fragment('foo.example.com.CNAME.CNAME foo.example.com') 149 | } 150 | end 151 | 152 | context 'when passing a wrong hostname in data' do 153 | let (:params) { { 154 | :zone => 'foo.example.com', 155 | :hash_data => { 156 | 'host 1' => {}, 157 | }, 158 | :record_type => 'CNAME', 159 | } } 160 | 161 | it 'should fail' do 162 | expect { 163 | should contain_concat__fragment('foo.example.com') 164 | }.to raise_error(Puppet::Error, /'host 1' is NOT a valid name/) 165 | end 166 | end 167 | 168 | context 'when passing a wrong owner in data with PTR' do 169 | let (:params) { { 170 | :zone => 'foo.example.com', 171 | :hash_data => { 172 | 'host1' => { 173 | 'owner' => 'wrong value', 174 | 'ptr' => true, 175 | }, 176 | }, 177 | :record_type => 'PTR', 178 | :ptr_zone => 'foo', 179 | } } 180 | 181 | it 'should fail' do 182 | expect { 183 | should contain_concat__fragment('') 184 | }.to raise_error(Puppet::Error, /invalid address/) 185 | end 186 | end 187 | 188 | context 'when passing data with PTR without ptr_zone' do 189 | let (:title) { 'PTR entry' } 190 | let (:params) { { 191 | :zone => 'foo.example.com', 192 | :hash_data => { 193 | 'host1' => { 194 | 'owner' => '1.2.3.4', 195 | 'ptr' => true, 196 | }, 197 | }, 198 | :record_type => 'PTR', 199 | } } 200 | 201 | it 'should fail' do 202 | expect { 203 | should contain_concat__fragment('') 204 | }.to raise_error(Puppet::Error, /Failed to parse template bind\/default-record.erb/) 205 | end 206 | end 207 | 208 | context 'when passing data with PTR' do 209 | let (:title) { 'PTR entry' } 210 | let (:params) { { 211 | :zone => 'foo.example.com', 212 | :hash_data => { 213 | 'host1' => { 214 | 'owner' => '1.2.3.4', 215 | 'ptr' => true, 216 | }, 217 | }, 218 | :record_type => 'PTR', 219 | :ptr_zone => 'foo', 220 | } } 221 | 222 | it { 223 | should contain_concat__fragment('foo.example.com.PTR.PTR entry').with_content( 224 | /4\.3\.2\.1\.in-addr\.arpa\. IN PTR host1\.foo\./ 225 | ) 226 | } 227 | end 228 | 229 | context 'when passing data with PTR and ttl' do 230 | let (:title) { 'PTR entry' } 231 | let (:params) { { 232 | :zone => 'foo.example.com', 233 | :hash_data => { 234 | 'host1' => { 235 | 'owner' => '1.2.3.4', 236 | 'ptr' => true, 237 | 'ttl' => '60', 238 | }, 239 | }, 240 | :record_type => 'PTR', 241 | :ptr_zone => 'foo', 242 | } } 243 | 244 | it { 245 | should contain_concat__fragment('foo.example.com.PTR.PTR entry').with_content( 246 | /4\.3\.2\.1\.in-addr\.arpa\. 60 IN PTR host1\.foo\./ 247 | ) 248 | } 249 | end 250 | 251 | context 'when passing data with PTR and host=@' do 252 | let (:title) { 'PTR entry' } 253 | let (:params) { { 254 | :zone => 'foo.example.com', 255 | :hash_data => { 256 | '@' => { 257 | 'owner' => '1.2.3.4', 258 | 'ptr' => true, 259 | }, 260 | }, 261 | :record_type => 'PTR', 262 | :ptr_zone => 'foo', 263 | } } 264 | 265 | it { 266 | should contain_concat__fragment('foo.example.com.PTR.PTR entry').with_content('') 267 | } 268 | end 269 | 270 | context 'when passing data with hash and type A' do 271 | let (:title) { 'A entry' } 272 | let (:params) { { 273 | :zone => 'foo.example.com', 274 | :hash_data => { 275 | 'host1' => { 276 | 'owner' => '1.2.3.4', 277 | }, 278 | }, 279 | :record_type => 'A', 280 | } } 281 | 282 | it { 283 | should contain_concat__fragment('foo.example.com.A.A entry').with_content( 284 | /host1 IN A 1\.2\.3\.4/ 285 | ) 286 | } 287 | end 288 | 289 | context 'when passing data with A with ttl' do 290 | let (:title) { 'A entry' } 291 | let (:params) { { 292 | :zone => 'foo.example.com', 293 | :hash_data => { 294 | 'host1' => { 295 | 'owner' => '1.2.3.4', 296 | 'ttl' => '60', 297 | }, 298 | }, 299 | :record_type => 'A', 300 | } } 301 | 302 | it { 303 | should contain_concat__fragment('foo.example.com.A.A entry').with_content( 304 | /host1 60 IN A 1\.2\.3\.4/ 305 | ) 306 | } 307 | end 308 | 309 | context 'when passing SRV with _' do 310 | let (:title) { 'SRV entry' } 311 | let (:params) { { 312 | :zone => 'foo.example.com', 313 | :hash_data => { 314 | '_sip._tcp.foo.example.com.' => { 315 | 'owner' => '0 5 5060 sipserver.example.com.', 316 | 'ttl' => '86400', 317 | }, 318 | }, 319 | :record_type => 'SRV', 320 | } } 321 | 322 | it { 323 | should contain_concat__fragment('foo.example.com.SRV.SRV entry').with_content( 324 | /_sip\._tcp\.foo\.example.com. 86400 IN SRV 0 5 5060 sipserver\.example\.com\./ 325 | ) 326 | } 327 | end 328 | end 329 | end 330 | end 331 | -------------------------------------------------------------------------------- /spec/defines/bind_view_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::view' do 4 | let(:pre_condition) do 5 | "class {'::bind': }" 6 | end 7 | 8 | on_supported_os.each do |os, facts| 9 | context "on #{os}" do 10 | 11 | let(:facts) do 12 | facts.merge({ 13 | :concat_basedir => '/var/lib/puppet/concat', 14 | }) 15 | end 16 | 17 | let(:confdir) do 18 | case facts[:osfamily] 19 | when 'Debian' 20 | '/etc/bind' 21 | when 'RedHat' 22 | '/etc/named' 23 | end 24 | end 25 | 26 | context 'my view' do 27 | let(:title) {'my view'} 28 | it { 29 | should contain_file("#{confdir}/views/my-view.view").with({ 30 | :ensure => 'file', 31 | :content => "view \"my-view\" {\n recursion no;\n include \"#{confdir}/views/my-view.zones\";\n};\n", 32 | }) 33 | should contain_concat("#{confdir}/views/my-view.zones") 34 | } 35 | end 36 | 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/defines/bind_zone_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'bind::zone' do 4 | 5 | let (:title) { 'domain.tld' } 6 | 7 | let(:pre_condition) do 8 | "class { 'bind': }" 9 | end 10 | 11 | on_supported_os.each do |os, facts| 12 | context "on #{os}" do 13 | let(:facts) do 14 | facts.merge({ 15 | :concat_basedir => '/var/lib/puppet/concat', 16 | }) 17 | end 18 | 19 | let(:confdir) do 20 | case facts[:osfamily] 21 | when 'Debian' 22 | '/etc/bind' 23 | when 'RedHat' 24 | '/etc/named' 25 | end 26 | end 27 | 28 | let(:bind_group) do 29 | case facts[:osfamily] 30 | when 'Debian' 31 | 'bind' 32 | when 'RedHat' 33 | 'named' 34 | end 35 | end 36 | 37 | # Validate input 38 | context 'when using a wrong ensure value' do 39 | let (:params) { { 40 | :ensure => 'running' 41 | } } 42 | 43 | it 'should fail' do 44 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 45 | }.to raise_error(Puppet::Error, /\$ensure must be either.* got 'running'/) 46 | end 47 | end 48 | 49 | context 'when passing an unexpected value to zone_type' do 50 | let (:params) { { 51 | :zone_type => 'hello' 52 | } } 53 | 54 | it 'should fail' do 55 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 56 | }.to raise_error(Puppet::Error, /Zone type 'hello' not supported\./) 57 | end 58 | end 59 | 60 | context 'when passing wrong type for is_dynamic' do 61 | let (:params) { { 62 | :is_dynamic => 'goodbye' 63 | } } 64 | 65 | it 'should fail' do 66 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 67 | }.to raise_error(Puppet::Error, /"goodbye" is not a boolean\./) 68 | end 69 | end 70 | 71 | context 'when zone is a slave with dynamic update enabled' do 72 | let (:params) { { 73 | :is_dynamic => true, 74 | :zone_type => 'slave' 75 | } } 76 | 77 | it 'should fail' do 78 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 79 | }.to raise_error(Puppet::Error, /Zone 'domain\.tld' cannot be slave AND dynamic!/) 80 | end 81 | end 82 | 83 | # Test all string parameters 84 | [:ensure, :zone_ttl, :zone_contact, :zone_serial, :zone_refresh, 85 | :zone_retry, :zone_expiracy, :zone_ns, 86 | :zone_origin].each do |p| 87 | context "when passing wrong type for #{p}" do 88 | let (:params) { { 89 | p => false 90 | } } 91 | 92 | it 'should fail' do 93 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 94 | }.to raise_error(Puppet::Error, /false is not (a string|an Array)\./) 95 | end 96 | end 97 | end 98 | 99 | context 'when master' do 100 | context 'when passing contact with spaces' do 101 | let (:params) { { 102 | :zone_type => 'master', 103 | :zone_contact => 'it has spaces', 104 | :zone_ns => ['ns.tld'], 105 | :zone_serial => '123456', 106 | :zone_ttl => '60' 107 | } } 108 | 109 | it 'should fail' do 110 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 111 | }.to raise_error(Puppet::Error, /Wrong contact value for domain\.tld/) 112 | end 113 | end 114 | 115 | context 'when passing ns with spaces' do 116 | let (:params) { { 117 | :zone_type => 'master', 118 | :zone_contact => 'admin@example.com', 119 | :zone_ns => ['ns space tld'], 120 | :zone_serial => '123456', 121 | :zone_ttl => '60' 122 | } } 123 | 124 | it 'should fail' do 125 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 126 | }.to raise_error(Puppet::Error, /Failed to parse template bind\/zone-header.erb/) 127 | #}.to raise_error(Puppet::Error, /Wrong ns value for 'ns space tld'/) 128 | end 129 | end 130 | 131 | context 'when passing wrong serial' do 132 | let (:params) { { 133 | :zone_type => 'master', 134 | :zone_contact => 'admin@example.com', 135 | :zone_ns => ['ns.tld'], 136 | :zone_serial => 'deadbeef', 137 | :zone_ttl => '60' 138 | } } 139 | 140 | it 'should fail' do 141 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 142 | }.to raise_error(Puppet::Error, /Wrong serial value for domain\.tld/) 143 | end 144 | end 145 | 146 | context 'when passing wrong ttl' do 147 | let (:params) { { 148 | :zone_type => 'master', 149 | :zone_contact => 'admin.example.com', 150 | :zone_ns => ['ns.tld'], 151 | :zone_serial => '123456', 152 | :zone_ttl => 'abc' 153 | } } 154 | 155 | it 'should fail' do 156 | expect { should contain_concat("#{confdir}/zones/domain.tld.conf") 157 | }.to raise_error(Puppet::Error, /Wrong ttl value for domain\.tld/) 158 | end 159 | end 160 | end 161 | 162 | 163 | # Check resources 164 | context 'when present' do 165 | context 'when slave' do 166 | let (:params) { { 167 | :zone_type => 'slave', 168 | :zone_masters => '1.2.3.4', 169 | :transfer_source => '2.3.4.5', 170 | } } 171 | 172 | it { should contain_concat("#{confdir}/zones/domain.tld.conf").with({ 173 | :owner => 'root', 174 | :group => 'root', 175 | :mode => '0644' 176 | }) } 177 | it { should contain_concat__fragment('bind.zones.domain.tld').with({ 178 | :target => "#{confdir}/zones/domain.tld.conf", 179 | :content => "# File managed by puppet\nzone domain.tld IN {\n type slave;\n masters { 1.2.3.4; };\n allow-query { any; };\n transfer-source 2.3.4.5;\n forwarders { };\n};\n" 180 | }) } 181 | end 182 | 183 | context 'when forward' do 184 | let (:params) { { 185 | :zone_type => 'forward', 186 | :zone_forwarders => '1.2.3.4', 187 | } } 188 | 189 | it { should contain_concat("#{confdir}/zones/domain.tld.conf").with({ 190 | :owner => 'root', 191 | :group => 'root', 192 | :mode => '0644' 193 | }) } 194 | it { should contain_concat__fragment('bind.zones.domain.tld').with({ 195 | :target => "#{confdir}/zones/domain.tld.conf", 196 | :content => "# File managed by puppet\nzone domain.tld IN {\n type forward;\n forwarders { 1.2.3.4; };\n};\n" 197 | }) } 198 | end 199 | 200 | context 'when master' do 201 | let (:params) { { 202 | :zone_type => 'master', 203 | :zone_contact => 'admin@example.com', 204 | :zone_ns => ['ns.tld', 'ns2.tld'], 205 | :zone_serial => '123456', 206 | :zone_ttl => '60', 207 | :zone_notify => ['1.1.1.1', '2.2.2.2'] 208 | } } 209 | 210 | it { should contain_concat("#{confdir}/zones/domain.tld.conf").with({ 211 | :owner => 'root', 212 | :group => 'root', 213 | :mode => '0644' 214 | }) } 215 | it { should contain_concat__fragment('bind.zones.domain.tld').with({ 216 | :target => "#{confdir}/zones/domain.tld.conf", 217 | :content => "# File managed by puppet\nzone \"domain.tld\" IN {\n type master;\n file \"#{confdir}/pri/domain.tld.conf\";\n allow-transfer { none; };\n allow-query { any; };\n notify yes;\n also-notify { 1.1.1.1; 2.2.2.2; };\n forwarders { };\n};\n" 218 | }) } 219 | it { should contain_concat("#{confdir}/pri/domain.tld.conf").with({ 220 | :owner => 'root', 221 | :group => bind_group, 222 | :mode => '0664' 223 | }) } 224 | it { should contain_concat__fragment('00.bind.domain.tld').with({ 225 | :target => "#{confdir}/pri/domain.tld.conf", 226 | :content => "; File managed by puppet\n$TTL 60\n@ IN SOA ns.tld. admin@example.com. (\n 123456 ; serial\n 3h ; refresh\n 1h ; retry\n 1w; expiracy\n 60 ) ; TTL\n IN NS ns.tld.\n IN NS ns2.tld.\n" 227 | }) } 228 | it { should contain_file("#{confdir}/pri/domain.tld.conf.d").with({ 229 | :ensure => 'absent' 230 | }) } 231 | end 232 | end 233 | 234 | context 'when absent' do 235 | let (:params) { { 236 | :ensure => 'absent' 237 | } } 238 | 239 | it { should contain_file("#{confdir}/pri/domain.tld.conf").with({ 240 | :ensure => 'absent' 241 | }) } 242 | it { should contain_file("#{confdir}/zones/domain.tld.conf").with({ 243 | :ensure => 'absent' 244 | }) } 245 | end 246 | end 247 | end 248 | end 249 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --format 2 | s 3 | --colour 4 | --loadby 5 | mtime 6 | --backtrace 7 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'puppetlabs_spec_helper/module_spec_helper' 2 | require 'rspec-puppet-facts' 3 | include RspecPuppetFacts 4 | 5 | 6 | RSpec.configure do |c| 7 | c.include PuppetlabsSpec::Files 8 | 9 | c.before :each do 10 | # Store any environment variables away to be restored later 11 | @old_env = {} 12 | ENV.each_key {|k| @old_env[k] = ENV[k]} 13 | 14 | c.strict_variables = Gem::Version.new(Puppet.version) >= Gem::Version.new('3.5') 15 | Puppet.features.stubs(:root?).returns(true) 16 | end 17 | 18 | c.after :each do 19 | PuppetlabsSpec::Files.cleanup 20 | end 21 | end 22 | 23 | require 'pathname' 24 | dir = Pathname.new(__FILE__).parent 25 | Puppet[:modulepath] = File.join(dir, 'fixtures', 'modules') 26 | 27 | # There's no real need to make this version dependent, but it helps find 28 | # regressions in Puppet 29 | # 30 | # 1. Workaround for issue #16277 where default settings aren't initialised from 31 | # a spec and so the libdir is never initialised (3.0.x) 32 | # 2. Workaround for 2.7.20 that now only loads types for the current node 33 | # environment (#13858) so Puppet[:modulepath] seems to get ignored 34 | # 3. Workaround for 3.5 where context hasn't been configured yet, 35 | # ticket https://tickets.puppetlabs.com/browse/MODULES-823 36 | # 37 | ver = Gem::Version.new(Puppet.version.split('-').first) 38 | if Gem::Requirement.new("~> 2.7.20") =~ ver || Gem::Requirement.new("~> 3.0.0") =~ ver || Gem::Requirement.new("~> 3.5") =~ ver || Gem::Requirement.new("~> 4.0") 39 | puts "augeasproviders: setting Puppet[:libdir] to work around broken type autoloading" 40 | # libdir is only a single dir, so it can only workaround loading of one external module 41 | Puppet[:libdir] = "#{Puppet[:modulepath]}/augeasproviders_core/lib" 42 | end 43 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | require 'beaker-rspec' 2 | 3 | install_puppet_agent_on hosts, {} 4 | 5 | RSpec.configure do |c| 6 | # Project root 7 | module_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) 8 | module_name = module_root.split('-').last 9 | 10 | # Readable test descriptions 11 | c.formatter = :documentation 12 | 13 | # Configure all nodes in nodeset 14 | c.before :suite do 15 | # Install module and dependencies 16 | puppet_module_install(:source => module_root, :module_name => module_name) 17 | hosts.each do |host| 18 | on host, puppet('module','install','puppetlabs-concat'), { :acceptable_exit_codes => [0,1] } 19 | on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /templates/acl.erb: -------------------------------------------------------------------------------- 1 | acl <%=@_name%> { 2 | <% @acls.each do |acl| -%> 3 | <%=acl%>; 4 | <% end -%> 5 | }; 6 | -------------------------------------------------------------------------------- /templates/default-record.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | @hash_data.sort.each do |host, datas| 3 | raise(Puppet::ParseError, "'#{host}' is NOT a valid name") unless scope.function_bind_check_hostname([host, @record_type]) 4 | if @record_type == 'PTR' 5 | if host != '@' 6 | if datas.fetch('ptr',true) 7 | scope.function_validate_re([@ptr_zone, '^\S+$']) 8 | -%> 9 | <%= require 'ipaddr'; IPAddr.new(datas.fetch('owner')).reverse %>. <%= datas.fetch('ttl', '') %> IN PTR <%= host %>.<%= @ptr_zone %>. 10 | <%- 11 | end 12 | end 13 | else 14 | raise(Puppet::ParseError, "#{datas.fetch('owner')} is NOT a valid IP") unless (scope.function_is_ip_address([datas.fetch('owner')]) or @record_type != 'A' or @record_type != 'AAAA') 15 | -%> 16 | <%= host %> <%= datas.fetch('ttl', '') %> <%=datas.fetch('record_class', 'IN')%> <%= @record_type %> <%= datas.fetch('owner') %> 17 | <%- 18 | end 19 | end -%> 20 | -------------------------------------------------------------------------------- /templates/dnskey.conf.erb: -------------------------------------------------------------------------------- 1 | key <%=@name%>. { 2 | algorithm <%=@algorithm%>; 3 | secret "<%=@secret%>"; 4 | }; 5 | -------------------------------------------------------------------------------- /templates/generate.erb: -------------------------------------------------------------------------------- 1 | $GENERATE <%= @range %> <%= @lhs %> <%= @ttl %> <%= @record_class %> <%= @record_type %> <%= @rhs %> ; <%= @name %> 2 | -------------------------------------------------------------------------------- /templates/mx-record.erb: -------------------------------------------------------------------------------- 1 | <%= @_owner %> <%= @ttl %> IN MX <%= @priority %> <%= @host %> 2 | -------------------------------------------------------------------------------- /templates/named.conf.erb: -------------------------------------------------------------------------------- 1 | include "<%=scope.lookupvar("bind::params::config_base_dir")%>/acls.conf"; 2 | include "<%=scope.lookupvar("bind::params::config_base_dir")%>/named.conf.options"; 3 | include "<%=scope.lookupvar("bind::params::config_base_dir")%>/named.conf.local"; 4 | -------------------------------------------------------------------------------- /templates/named.conf.options.erb: -------------------------------------------------------------------------------- 1 | options { 2 | <%- 3 | @conf.sort.each do |key, value| 4 | if value.is_a?(Array) 5 | -%> 6 | <%=key%> { 7 | <%- 8 | value.each do |i| 9 | -%> 10 | <%=i%>; 11 | <% end -%> 12 | }; 13 | <% else -%> 14 | <%=key%> <%=value%>; 15 | <%end -%> 16 | <%end -%> 17 | }; 18 | 19 | logging { 20 | <% @logging['channels'].sort.each do |key, hash| -%> 21 | channel <%=key%> { 22 | <% hash.sort.each do |directive, value| -%> 23 | <% if value.is_a?(Array) %> 24 | <%=directive%> { 25 | <% value.each do |i| -%> 26 | <%=i%>; 27 | <% end -%> 28 | }; 29 | <% else -%> 30 | <%=directive%> <%=value%>; 31 | <%end -%> 32 | <%end -%> 33 | }; 34 | <%end -%> 35 | <%- 36 | @logging['categories'].sort.each do |key, value| 37 | -%> 38 | category <%=key%> { 39 | <%=Array(value).join(';')%>; 40 | }; 41 | <%end -%> 42 | }; 43 | -------------------------------------------------------------------------------- /templates/view.erb: -------------------------------------------------------------------------------- 1 | view "<%=@_name%>" { 2 | <%- 3 | @options.sort.each do |key, value| 4 | if value.is_a?(Array) 5 | -%> 6 | <%=key%> { 7 | <%- 8 | value.each do |i| 9 | -%> 10 | <%=i%>; 11 | <% end -%> 12 | }; 13 | <% else -%> 14 | <%=key%> <%=value%>; 15 | <% end -%> 16 | <% end -%> 17 | include "<%=scope.lookupvar('bind::params::views_directory')%>/<%=@_name%>.zones"; 18 | }; 19 | -------------------------------------------------------------------------------- /templates/zone-forward.erb: -------------------------------------------------------------------------------- 1 | # File managed by puppet 2 | zone <%= @name %> IN { 3 | type forward; 4 | forwarders { <%= Array(@zone_forwarders).join('; ') -%>; }; 5 | }; 6 | -------------------------------------------------------------------------------- /templates/zone-header.erb: -------------------------------------------------------------------------------- 1 | ; File managed by puppet 2 | $TTL <%= @zone_ttl %> 3 | @ IN SOA <%= @zone_ns[0] %>. <%= @zone_contact %>. ( 4 | <%= @zone_serial %> ; serial 5 | <%= @zone_refresh %> ; refresh 6 | <%= @zone_retry %> ; retry 7 | <%= @zone_expiracy %>; expiracy 8 | <%= @zone_ttl %> ) ; TTL 9 | <% @zone_ns.each do |ns| - 10 | raise(Puppet::ParseError, "Wrong ns value: '#{ns}'") unless ( ns =~ Regexp.compile('^\S+$')) 11 | -%> 12 | IN NS <%= ns %>. 13 | <% end -%> 14 | <% if @zone_origin -%> 15 | $ORIGIN <%= @zone_origin %>. 16 | <% end -%> 17 | -------------------------------------------------------------------------------- /templates/zone-master.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | if @is_dynamic and @allow_update.empty? 3 | raise(Puppet::ParseError, "allow_update is empty for dynamic zone '#{name}'") 4 | end 5 | -%> 6 | # File managed by puppet 7 | zone "<%= @name %>" IN { 8 | type master; 9 | <% if @is_dynamic -%> 10 | file "<%= scope.lookupvar("bind::params::dynamic_directory") %>/<%= @name %>.conf"; 11 | <% else -%> 12 | file "<%= scope.lookupvar("bind::params::pri_directory") %>/<%= @name %>.conf"; 13 | <% end -%> 14 | <% if @zone_xfers and not @zone_xfers.empty? -%> 15 | allow-transfer { <%= Array(@zone_xfers).join('; ') -%>; }; 16 | <% else -%> 17 | allow-transfer { none; }; 18 | <% end -%> 19 | <% if @is_dynamic -%> 20 | allow-update { key <%= Array(@allow_update).join('.; key ') -%>.; }; 21 | <% end -%> 22 | allow-query { any; }; 23 | notify yes; 24 | <% if @zone_notify and not @zone_notify.empty? -%> 25 | also-notify { <%= Array(@zone_notify).join('; ') -%>; }; 26 | <% end -%> 27 | forwarders { }; 28 | }; 29 | -------------------------------------------------------------------------------- /templates/zone-slave.erb: -------------------------------------------------------------------------------- 1 | # File managed by puppet 2 | zone <%= @name %> IN { 3 | type slave; 4 | masters { <%= Array(@zone_masters).join('; ') -%>; }; 5 | allow-query { any; }; 6 | <% if @transfer_source and @transfer_source != '' -%> 7 | transfer-source <%= @transfer_source %>; 8 | <% end -%> 9 | forwarders { }; 10 | }; 11 | --------------------------------------------------------------------------------