dnser 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/LICENSE +20 -0
- data/README.md +69 -0
- data/bin/dnser +35 -0
- data/dnser.gemspec +22 -0
- data/lib/dnser.rb +52 -0
- data/lib/dnser/builder.rb +20 -0
- data/lib/dnser/builders/stream.rb +66 -0
- data/lib/dnser/domain.rb +92 -0
- data/lib/dnser/record.rb +126 -0
- data/lib/dnser/records/base.rb +59 -0
- data/lib/dnser/records/soa.rb +44 -0
- data/lib/dnser/records/srv.rb +56 -0
- data/lib/dnser/template.rb +25 -0
- data/lib/dnser/version.rb +3 -0
- data/spec/record_spec.rb +149 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/template_spec.rb +70 -0
- data/spec/zone_spec.rb +47 -0
- data/test.rb +41 -0
- metadata +83 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 Alexey Shcherbakov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
DNSer
|
|
2
|
+
=====
|
|
3
|
+
|
|
4
|
+
[](http://badge.fury.io/rb/dnser)
|
|
5
|
+
|
|
6
|
+
Ruby DSL for create DNS Zone file
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
template :google_app do |host, code, params|
|
|
10
|
+
|
|
11
|
+
TXT host, ('google-site-verification=' + code), :comment => "Google verification code"
|
|
12
|
+
|
|
13
|
+
MX host, 'ASPMX.L.GOOGLE.COM.', priority: 1, comment: "GMail"
|
|
14
|
+
MX host, 'ALT1.ASPMX.L.GOOGLE.COM.', priority: 5, comment: "GMail"
|
|
15
|
+
MX host, 'ALT2.ASPMX.L.GOOGLE.COM.', priority: 5, comment: "GMail"
|
|
16
|
+
MX host, 'ASPMX2.GOOGLEMAIL.COM.', priority: 10, comment: "GMail"
|
|
17
|
+
MX host, 'ASPMX23.GOOGLEMAIL.COM.', priority: 10, comment: "GMail"
|
|
18
|
+
TXT host, 'v=spf1 include:_spf.google.com ~all', comment: "GMail SPF"
|
|
19
|
+
|
|
20
|
+
%w(mail doc).each do |sub|
|
|
21
|
+
CNAME [sub, host].join('.'), 'ghs.googlehosted.com.'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
zone 'domain.ltd' do
|
|
26
|
+
ttl 3600
|
|
27
|
+
ns = NS 'ns1', 'ns1.dnsimple.com.'
|
|
28
|
+
|
|
29
|
+
SOA host do
|
|
30
|
+
nameserver ns
|
|
31
|
+
email 'admin@domain.ltd'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
A IPAddr.new('127.0.0.1'), alias: :www
|
|
35
|
+
|
|
36
|
+
A :dev, IPAddr.new('127.0.0.2')
|
|
37
|
+
|
|
38
|
+
apply_google_app host, '6tTalLzrBXBO4Gy9700TAbpg2QTKzGYEuZ_Ls69jle8'
|
|
39
|
+
|
|
40
|
+
SRV 'corp', 'calendar.abc.com.' do
|
|
41
|
+
service '_caldavs'
|
|
42
|
+
protocol :tcp
|
|
43
|
+
port 8443
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
SRV 'calendar2.abc.com.', service: :caldavs, protocol: :tcp, port: 8443, weight: 0, priority: 1
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Result:
|
|
51
|
+
|
|
52
|
+
$ORIGIN domain.ltd.
|
|
53
|
+
$TTL 3600
|
|
54
|
+
domain.ltd. IN SOA ns1.domain.ltd. admin.domain.ltd. (20130803 3600 3600 3600 3600)
|
|
55
|
+
ns1 IN NS ns1.dnsimple.com.
|
|
56
|
+
@ IN A 127.0.0.1
|
|
57
|
+
@ IN TXT "v=spf1 include:_spf.google.com ~all" #GMail SPF
|
|
58
|
+
@ IN MX 10 ASPMX23.GOOGLEMAIL.COM. #GMail
|
|
59
|
+
@ IN TXT google-site-verification=6tTalLzrBXBO4Gy9700TAbpg2QTKzGYEuZ_Ls69jle8 #Google verification code
|
|
60
|
+
@ IN MX 1 ASPMX.L.GOOGLE.COM. #GMail
|
|
61
|
+
@ IN MX 5 ALT1.ASPMX.L.GOOGLE.COM. #GMail
|
|
62
|
+
@ IN MX 5 ALT2.ASPMX.L.GOOGLE.COM. #GMail
|
|
63
|
+
@ IN MX 10 ASPMX2.GOOGLEMAIL.COM. #GMail
|
|
64
|
+
_caldavs._tcp.corp.domain.ltd. IN SRV 0 0 8443 calendar.abc.com.
|
|
65
|
+
_caldavs._tcp.domain.ltd. IN SRV 1 0 8443 calendar2.abc.com.
|
|
66
|
+
dev IN A 127.0.0.2
|
|
67
|
+
doc IN CNAME ghs.googlehosted.com.
|
|
68
|
+
mail IN CNAME ghs.googlehosted.com.
|
|
69
|
+
www IN CNAME domain.ltd.
|
data/bin/dnser
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
require 'dnser'
|
|
5
|
+
require 'dnser/version'
|
|
6
|
+
|
|
7
|
+
OptionParser.new do |opts|
|
|
8
|
+
opts.banner = "Usage: dnser [options] zone"
|
|
9
|
+
opts.on('-o', '--output [file]', 'Path to output file') do |file|
|
|
10
|
+
DNSer.config.output = File.open(file, 'w')
|
|
11
|
+
end
|
|
12
|
+
opts.on('-f', '--full', 'Use full domain name in records') do
|
|
13
|
+
DNSer.config.full_domain = true
|
|
14
|
+
end
|
|
15
|
+
opts.on_tail('-v', '--version') { puts "DNSer v#{DNSer::VERSION}"; exit(0) }
|
|
16
|
+
|
|
17
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
18
|
+
puts opts
|
|
19
|
+
exit
|
|
20
|
+
end
|
|
21
|
+
end.parse!
|
|
22
|
+
|
|
23
|
+
zone_file = ARGV.last
|
|
24
|
+
|
|
25
|
+
unless zone_file
|
|
26
|
+
puts "Zone file not defined."
|
|
27
|
+
exit(0)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
unless File.exist?(zone_file)
|
|
31
|
+
puts "Zone file #{zone_file} doesn't exist."
|
|
32
|
+
exit(0)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
load zone_file
|
data/dnser.gemspec
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "dnser/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = 'dnser'
|
|
7
|
+
s.version = DNSer::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.date = '2013-08-03'
|
|
10
|
+
s.summary = "DNS zone file in ruby"
|
|
11
|
+
s.description = "Clean ruby syntax for writing DNS zone file."
|
|
12
|
+
s.authors = ["Alexey Shcherbakov"]
|
|
13
|
+
s.email = 'schalexey@gmail.com'
|
|
14
|
+
s.files = `git ls-files`.split("\n")
|
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.homepage = 'https://github.com/fuCtor/DNSer'
|
|
17
|
+
s.license = 'MIT'
|
|
18
|
+
|
|
19
|
+
s.add_development_dependency "rspec"
|
|
20
|
+
|
|
21
|
+
s.require_paths = ["lib"]
|
|
22
|
+
end
|
data/lib/dnser.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require ::File.expand_path('../dnser/domain.rb', __FILE__)
|
|
2
|
+
require ::File.expand_path('../dnser/template.rb', __FILE__)
|
|
3
|
+
require 'singleton'
|
|
4
|
+
|
|
5
|
+
module DNSer
|
|
6
|
+
|
|
7
|
+
def self.domain domain_name, params = {} , &block
|
|
8
|
+
Domain.new domain_name, params, &block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.create_template name, params = {}, &block
|
|
12
|
+
@templates ||= {}
|
|
13
|
+
@templates[name.to_s.downcase.to_sym] = DNSer::Template.new params, &block
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.apply_template name, &block
|
|
17
|
+
if @templates
|
|
18
|
+
tpl = @templates[name.to_s.downcase.to_sym].dup rescue nil
|
|
19
|
+
yield tpl if tpl if block_given?
|
|
20
|
+
raise Template::Unknown.new(name.to_s.downcase.to_sym), 'Unknown DNS template' unless tpl
|
|
21
|
+
tpl
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.config
|
|
26
|
+
Config.instance
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Config
|
|
31
|
+
include ::Singleton
|
|
32
|
+
|
|
33
|
+
attr_accessor :output, :full_domain
|
|
34
|
+
|
|
35
|
+
def output
|
|
36
|
+
@output ||= DNSer::StreamBuilder.new($stdout)
|
|
37
|
+
end
|
|
38
|
+
def full_domain
|
|
39
|
+
@full_domain ||= false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def zone *args , &block
|
|
47
|
+
DNSer.domain *args , &block
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def template *args , &block
|
|
51
|
+
DNSer.create_template *args , &block
|
|
52
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module DNSer
|
|
2
|
+
class Builder
|
|
3
|
+
def write *args
|
|
4
|
+
raise NotImplementedError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def origin name
|
|
8
|
+
raise NotImplementedError
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def ttl value
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def sync
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Dir.glob(::File.expand_path('../builders/*.rb', __FILE__)).each{|file| require file }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
module DNSer
|
|
3
|
+
class StreamBuilder < Builder
|
|
4
|
+
attr_reader :stream
|
|
5
|
+
def initialize stream
|
|
6
|
+
case stream
|
|
7
|
+
when String
|
|
8
|
+
@stream = File.new stream, 'w'
|
|
9
|
+
when ::IO,::File,::StringIO
|
|
10
|
+
@stream = stream
|
|
11
|
+
else
|
|
12
|
+
raise "Unknown stream type #{stream.class}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@buffer = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def origin name
|
|
19
|
+
@stream << ('$ORIGIN ' + name.to_s + "\n")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ttl value
|
|
23
|
+
@stream << ('$TTL ' + value.to_s + "\n")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def write *args
|
|
27
|
+
if (args.count == 1)
|
|
28
|
+
record = args.first
|
|
29
|
+
write record.host, record.type, record.value, record.comment
|
|
30
|
+
else
|
|
31
|
+
|
|
32
|
+
value = args[2]
|
|
33
|
+
if value.is_a? DNSer::Record
|
|
34
|
+
value = value.full_host
|
|
35
|
+
else
|
|
36
|
+
value = value.to_s
|
|
37
|
+
end
|
|
38
|
+
@buffer << {host: args[0].to_s, type: args[1].to_s.upcase ,value: value, comment: args[3] }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def sync
|
|
43
|
+
|
|
44
|
+
max_host_length = 0
|
|
45
|
+
max_type_length = 0
|
|
46
|
+
max_value_length = 0
|
|
47
|
+
|
|
48
|
+
@buffer.each do |r|
|
|
49
|
+
max_host_length = max_host_length < r[:host].size ? r[:host].length : max_host_length
|
|
50
|
+
max_type_length = max_type_length < r[:type].size ? r[:type].length : max_type_length
|
|
51
|
+
max_value_length = max_value_length < r[:value].size ? r[:value].length : max_value_length
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
@buffer.each do |r|
|
|
55
|
+
@stream << r[:host].ljust(max_host_length + 2)
|
|
56
|
+
@stream << 'IN ' + r[:type].ljust(max_type_length)
|
|
57
|
+
@stream << "\t" + r[:value].ljust(max_value_length)
|
|
58
|
+
@stream << "\t##{r[:comment]}" unless r[:comment].empty?
|
|
59
|
+
@stream << "\n"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@buffer.clear
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/dnser/domain.rb
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
require 'ipaddr'
|
|
3
|
+
|
|
4
|
+
require ::File.expand_path('../record.rb', __FILE__)
|
|
5
|
+
require ::File.expand_path('../builder.rb', __FILE__)
|
|
6
|
+
|
|
7
|
+
module DNSer
|
|
8
|
+
class Domain
|
|
9
|
+
attr :name
|
|
10
|
+
|
|
11
|
+
def initialize domain_name, params = {}, &block
|
|
12
|
+
@name = domain_name
|
|
13
|
+
|
|
14
|
+
@builder = params[:builder] || DNSer.config.output #DNSer::StreamBuilder.new($stdout)
|
|
15
|
+
@builder = DNSer::StreamBuilder.new(@builder) unless @builder.is_a? DNSer::Builder
|
|
16
|
+
|
|
17
|
+
@name = @name + '.' unless @name.end_with?('.')
|
|
18
|
+
@ttl_val = 3600
|
|
19
|
+
@records = []
|
|
20
|
+
instance_exec self, &block if block
|
|
21
|
+
|
|
22
|
+
dump
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def name
|
|
26
|
+
@name
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
alias_method :current, :name
|
|
30
|
+
alias_method :host, :name
|
|
31
|
+
|
|
32
|
+
def ttl(*args)
|
|
33
|
+
@ttl_val = args.first unless args.empty?
|
|
34
|
+
@ttl_val
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def dump
|
|
38
|
+
@builder.origin @name
|
|
39
|
+
@builder.ttl @ttl_val
|
|
40
|
+
|
|
41
|
+
@records_tmp = @records.dup
|
|
42
|
+
|
|
43
|
+
soa_index = @records_tmp.index {|x| x.is_a?(DNSer::SoaRecord) }
|
|
44
|
+
@builder.write @records_tmp.delete_at( soa_index ) if soa_index
|
|
45
|
+
|
|
46
|
+
ns = []
|
|
47
|
+
@records_tmp = @records_tmp.map do |r|
|
|
48
|
+
if r.type.to_s.downcase == 'ns'
|
|
49
|
+
@builder.write r
|
|
50
|
+
nil
|
|
51
|
+
else
|
|
52
|
+
r
|
|
53
|
+
end
|
|
54
|
+
end .compact
|
|
55
|
+
|
|
56
|
+
# @records_tmp.sort! {|x, y| x.host <=> y.host }
|
|
57
|
+
@records_tmp.each {|r| @builder.write r }
|
|
58
|
+
|
|
59
|
+
@builder.sync
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def method_missing name, *args, &block
|
|
63
|
+
name = name.to_s.downcase
|
|
64
|
+
|
|
65
|
+
return DNSer.apply_template name.gsub('apply_', '') do |tpl|
|
|
66
|
+
tpl.apply self, *args, &block
|
|
67
|
+
end if name.start_with? 'apply_'
|
|
68
|
+
|
|
69
|
+
params = args.last.dup if args.last.is_a? Hash
|
|
70
|
+
|
|
71
|
+
record_class = begin
|
|
72
|
+
eval "::DNSer::#{name.capitalize}Record"
|
|
73
|
+
rescue NameError => e
|
|
74
|
+
args = [name] + args
|
|
75
|
+
DNSer::BaseRecord
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
record = record_class.new(self, *args, &block)
|
|
79
|
+
@records << record
|
|
80
|
+
|
|
81
|
+
if params.key? :alias
|
|
82
|
+
[params[:alias]].flatten.each do |host|
|
|
83
|
+
@records << DNSer::BaseRecord.new(self, :CNAME, host, record, &block)
|
|
84
|
+
end
|
|
85
|
+
end if params
|
|
86
|
+
|
|
87
|
+
record
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/dnser/record.rb
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
module DNSer
|
|
2
|
+
class Record
|
|
3
|
+
attr_writer :host
|
|
4
|
+
attr_reader :domain
|
|
5
|
+
|
|
6
|
+
class Unknown < RuntimeError
|
|
7
|
+
attr_reader :name
|
|
8
|
+
attr_reader :args
|
|
9
|
+
def initialize name, args
|
|
10
|
+
@name = name
|
|
11
|
+
@args = args
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class EmptyValue < RuntimeError
|
|
16
|
+
attr_reader :record
|
|
17
|
+
attr_reader :field
|
|
18
|
+
def initialize record
|
|
19
|
+
@record = record
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def initialize domain, host, params = {}
|
|
25
|
+
|
|
26
|
+
ttl domain.ttl
|
|
27
|
+
priority 0 unless @priority_val
|
|
28
|
+
|
|
29
|
+
unless host.is_a? Symbol
|
|
30
|
+
@host = host.dup
|
|
31
|
+
else
|
|
32
|
+
@host = host
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@domain = domain
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def ttl(value)
|
|
39
|
+
@ttl = value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def priority(*args)
|
|
43
|
+
@priority_val = args.first if args.size == 1
|
|
44
|
+
@priority_val
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
alias_method :prio, :priority
|
|
48
|
+
|
|
49
|
+
def host
|
|
50
|
+
DNSer.config.full_domain ? expand_domain(@host.to_s) : collapse_domain(@host.to_s)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def full_host
|
|
54
|
+
short_host = host
|
|
55
|
+
if short_host == '@'
|
|
56
|
+
domain.name
|
|
57
|
+
else
|
|
58
|
+
[short_host, domain.name].join('.')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def type
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def value
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def comment(v = nil)
|
|
71
|
+
@comment = v if v
|
|
72
|
+
@comment || ''
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
protected
|
|
77
|
+
def expand_domain( name )
|
|
78
|
+
return domain.host if name == '@'
|
|
79
|
+
return [name, domain.host].join('.') unless name.end_with?('.')
|
|
80
|
+
name
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def collapse_domain( name )
|
|
84
|
+
|
|
85
|
+
name = name.dup
|
|
86
|
+
|
|
87
|
+
return name if name == '@'
|
|
88
|
+
return name unless name.end_with?('.')
|
|
89
|
+
|
|
90
|
+
name = name + '.' unless name.end_with?('.')
|
|
91
|
+
|
|
92
|
+
name.gsub!( domain.name.dup, '')
|
|
93
|
+
name.chop! if name.end_with?('.')
|
|
94
|
+
|
|
95
|
+
if name.empty?
|
|
96
|
+
'@'
|
|
97
|
+
else
|
|
98
|
+
name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def canonical_host( target_host )
|
|
104
|
+
|
|
105
|
+
case host
|
|
106
|
+
when DNSer::Record
|
|
107
|
+
target_host = target_host.full_host.to_s
|
|
108
|
+
when IPAddr
|
|
109
|
+
target_host = target_host.to_s
|
|
110
|
+
else
|
|
111
|
+
|
|
112
|
+
ip = IPAddr.new target_host rescue nil
|
|
113
|
+
return target_host.to_s if ip
|
|
114
|
+
|
|
115
|
+
target_host = target_host.to_s
|
|
116
|
+
target_host = target_host + '.' unless target_host.end_with? '.'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
target_host
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
Dir.glob(::File.expand_path('../records/*.rb', __FILE__)).each{|file| require file }
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module DNSer
|
|
2
|
+
class BaseRecord < DNSer::Record
|
|
3
|
+
attr :ttl_val
|
|
4
|
+
attr :value
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
7
|
+
def initialize domain, name, *args, &block
|
|
8
|
+
|
|
9
|
+
params = {}
|
|
10
|
+
params = args.pop if args.last.is_a? Hash
|
|
11
|
+
|
|
12
|
+
host = domain.host
|
|
13
|
+
value = nil
|
|
14
|
+
|
|
15
|
+
case args.size
|
|
16
|
+
when 2
|
|
17
|
+
host = args[0]
|
|
18
|
+
value = args[1]
|
|
19
|
+
when 1
|
|
20
|
+
value = args[0]
|
|
21
|
+
else
|
|
22
|
+
raise DNSer::Record::EmptyValue.new(name), 'Content must be defined'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@name = name.to_s.upcase.to_sym
|
|
26
|
+
@value = value
|
|
27
|
+
params = {ttl: domain.ttl}.merge(params)
|
|
28
|
+
|
|
29
|
+
params.each do |key, value|
|
|
30
|
+
self.send "#{key}", value if self.respond_to? key
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
super domain, host, params
|
|
34
|
+
instance_eval &block if block_given?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def type
|
|
38
|
+
self.name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def value
|
|
42
|
+
content = if type == :TXT
|
|
43
|
+
'"' + @value.to_s + '"'
|
|
44
|
+
else
|
|
45
|
+
@value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if @priority_val == 0
|
|
49
|
+
content
|
|
50
|
+
else
|
|
51
|
+
"#{@priority_val} #{content}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ttl v
|
|
56
|
+
@ttl_val = v
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module DNSer
|
|
2
|
+
class SoaRecord < DNSer::Record
|
|
3
|
+
def initialize domain, host, params = {}, &block
|
|
4
|
+
|
|
5
|
+
[:minimum, :nameserver, :email, :serial, :refresh, :retry, :expire].each do |m|
|
|
6
|
+
instance_variable_set("@#{m}".to_sym, nil)
|
|
7
|
+
self.class.send :define_method, m, proc { |*args|
|
|
8
|
+
instance_variable_set("@#{m}", args.first) unless args.empty?
|
|
9
|
+
instance_variable_get("@#{m}")
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
params = {minimum: domain.ttl, refresh: domain.ttl, retry: domain.ttl, expire: domain.ttl, serial: Time.now.strftime("%Y%m%d%H%M")}.merge(params)
|
|
14
|
+
|
|
15
|
+
params.each do |key, value|
|
|
16
|
+
self.send key, value if self.respond_to? key
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
super domain, host, params
|
|
20
|
+
instance_eval &block if block_given?
|
|
21
|
+
|
|
22
|
+
raise DNSer::Record::EmptyValue.new(self), 'Email must be defined' unless @email
|
|
23
|
+
raise DNSer::Record::EmptyValue.new(self), 'Email must be defined' unless @nameserver
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def host
|
|
27
|
+
@host.to_s
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def type
|
|
31
|
+
:SOA
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def value
|
|
35
|
+
ns = @nameserver.to_s
|
|
36
|
+
if @nameserver.is_a?(DNSer::Record)
|
|
37
|
+
ns = @nameserver.full_host.to_s
|
|
38
|
+
end
|
|
39
|
+
em = @email.to_s.gsub('@', '.')
|
|
40
|
+
em = em + '.' unless em.end_with?('.')
|
|
41
|
+
[ns, em, @serial, @refresh, @retry, @expire, @ttl].join(' ')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require ::File.expand_path('../base.rb', __FILE__)
|
|
2
|
+
|
|
3
|
+
module DNSer
|
|
4
|
+
class SrvRecord < DNSer::BaseRecord
|
|
5
|
+
def initialize domain, *args, &block
|
|
6
|
+
|
|
7
|
+
params = {}
|
|
8
|
+
params = args.pop if args.last.is_a? Hash
|
|
9
|
+
|
|
10
|
+
host = domain.host
|
|
11
|
+
value = nil
|
|
12
|
+
|
|
13
|
+
[:port, :weight, :protocol, :service].each do |m|
|
|
14
|
+
instance_variable_set("@#{m}".to_sym, nil)
|
|
15
|
+
self.class.send :define_method, m, proc { |*args|
|
|
16
|
+
instance_variable_set("@#{m}", args.first) unless args.empty?
|
|
17
|
+
instance_variable_get("@#{m}")
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
params = {weight: 0, priority: 0, port: 0}.merge(params)
|
|
22
|
+
|
|
23
|
+
params.each do |key, value|
|
|
24
|
+
self.send key, value if self.respond_to? key
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
super domain, :SRV, *args, &block
|
|
28
|
+
|
|
29
|
+
raise DNSer::Record::EmptyValue.new(self), 'Service must be defined' unless @service
|
|
30
|
+
raise DNSer::Record::EmptyValue.new(self), 'Protocol must be defined' unless @protocol
|
|
31
|
+
raise DNSer::Record::EmptyValue.new(self), 'Port must be defined' if @port == 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def host
|
|
35
|
+
first_part = [@service, @protocol].map(&:to_s).map {|i| i.start_with?('_') ? i : ('_' + i)} .join('.')
|
|
36
|
+
|
|
37
|
+
short_host = collapse_domain @host
|
|
38
|
+
if(short_host == '@')
|
|
39
|
+
[first_part, domain.name ]
|
|
40
|
+
else
|
|
41
|
+
[first_part, short_host, domain.name]
|
|
42
|
+
end .join('.')
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def value
|
|
47
|
+
res = super.split(' ')
|
|
48
|
+
case res.size
|
|
49
|
+
when 1
|
|
50
|
+
['0', @weight, @port, res.first ]
|
|
51
|
+
when 2
|
|
52
|
+
[res.first, @weight, @port, res.last ]
|
|
53
|
+
end .map(&:to_s).join(' ')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module DNSer
|
|
2
|
+
class Template
|
|
3
|
+
def initialize(params = {}, &block)
|
|
4
|
+
@block = block
|
|
5
|
+
@params = params
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def apply(domain, *args, &block)
|
|
9
|
+
if args.last.is_a? Hash
|
|
10
|
+
args.push @params.merge(args.pop)
|
|
11
|
+
else
|
|
12
|
+
args.push @params
|
|
13
|
+
end
|
|
14
|
+
domain.instance_exec *args, &@block if @block
|
|
15
|
+
domain.instance_exec *args, &block if block
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Unknown < RuntimeError
|
|
19
|
+
attr_reader :name
|
|
20
|
+
def initialize name
|
|
21
|
+
@name = name
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/spec/record_spec.rb
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require "rspec"
|
|
2
|
+
require ::File.expand_path('../../lib/dnser.rb', __FILE__)
|
|
3
|
+
|
|
4
|
+
describe DNSer::Record do
|
|
5
|
+
|
|
6
|
+
def test_zone &block
|
|
7
|
+
io = StringIO.new
|
|
8
|
+
zone = DNSer.domain 'domain.ltd', builder: io, &block
|
|
9
|
+
[zone, io]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context 'for Base record' do
|
|
13
|
+
it "should create base record" do
|
|
14
|
+
|
|
15
|
+
zone, io = test_zone
|
|
16
|
+
expect( DNSer::BaseRecord.new(zone, :A, '1.1.1.1') ).to be_a(DNSer::BaseRecord)
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should return correct type" do
|
|
21
|
+
zone, io = test_zone
|
|
22
|
+
expect( DNSer::BaseRecord.new(zone, :A, '1.1.1.1').type ).to eq(:A)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should return correct value" do
|
|
26
|
+
zone, io = test_zone
|
|
27
|
+
expect( DNSer::BaseRecord.new(zone, :A, '1.1.1.1').value ).to eq('1.1.1.1')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should return correct priority" do
|
|
31
|
+
zone, io = test_zone
|
|
32
|
+
expect( DNSer::BaseRecord.new(zone, :A, '1.1.1.1', priority: 1).value ).to eq('1 1.1.1.1')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should return correct host" do
|
|
36
|
+
zone, io = test_zone
|
|
37
|
+
expect( DNSer::BaseRecord.new(zone, :A, zone.host, '1.1.1.1').host ).to eq('@')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should return correct canonical host" do
|
|
41
|
+
zone, io = test_zone
|
|
42
|
+
expect( DNSer::BaseRecord.new(zone, :A, zone.host, '1.1.1.1').full_host ).to eq(zone.host)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should return correct priority" do
|
|
46
|
+
zone, io = test_zone
|
|
47
|
+
expect( DNSer::BaseRecord.new(zone, :A, zone.host, '1.1.1.1', priority: 10).priority ).to eq(10)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should wrap value if TXT record" do
|
|
51
|
+
zone, io = test_zone
|
|
52
|
+
expect( DNSer::BaseRecord.new(zone, :TXT, zone.host, 'value', priority: 10).value ).to eq('10 "value"')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should support IPAddr as value" do
|
|
56
|
+
zone, io = test_zone
|
|
57
|
+
ip = IPAddr.new('1.1.1.1')
|
|
58
|
+
expect( DNSer::BaseRecord.new(zone, :A, zone.host, ip).value ).to eq(ip.to_s)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should support another record as value" do
|
|
62
|
+
zone, io = test_zone
|
|
63
|
+
ip = IPAddr.new('1.1.1.1')
|
|
64
|
+
record = DNSer::BaseRecord.new(zone, :A, zone.host, ip)
|
|
65
|
+
expect( DNSer::BaseRecord.new(zone, :CNAME, 'www', record).value ).to eq(record)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'for SOA record' do
|
|
71
|
+
it "should create" do
|
|
72
|
+
zone, io = test_zone
|
|
73
|
+
expect( DNSer::SoaRecord.new(zone, zone.name, email: 'user@test.ltd', nameserver: '1.1.1.1') ).to be_a(DNSer::SoaRecord)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should return correct type" do
|
|
77
|
+
zone, io = test_zone
|
|
78
|
+
expect( DNSer::SoaRecord.new(zone, zone.name, email: 'user@test.ltd', nameserver: '1.1.1.1').type ).to eq(:SOA)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should validate email " do
|
|
82
|
+
zone, io = test_zone
|
|
83
|
+
expect{ DNSer::SoaRecord.new(zone, zone.name, nameserver: '1.1.1.1') }.to raise_error(DNSer::Record::EmptyValue)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should validate nameserver " do
|
|
87
|
+
zone, io = test_zone
|
|
88
|
+
expect{ DNSer::SoaRecord.new(zone, zone.name, email: 'user@test.ltd') }.to raise_error(DNSer::Record::EmptyValue)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should return correct email" do
|
|
92
|
+
zone, io = test_zone
|
|
93
|
+
expect( DNSer::SoaRecord.new(zone, zone.name, email: 'user@test.ltd', nameserver: '1.1.1.1').email ).to eq('user@test.ltd')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should return correct nameserver" do
|
|
97
|
+
zone, io = test_zone
|
|
98
|
+
expect( DNSer::SoaRecord.new(zone, zone.name, email: 'user@test.ltd', nameserver: '1.1.1.1').nameserver ).to eq('1.1.1.1')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should return correct value" do
|
|
102
|
+
zone, io = test_zone
|
|
103
|
+
expect( DNSer::SoaRecord.new(zone, zone.name, email: 'admin@domain.ltd', nameserver: 'ns1.domain.ltd.', serial: 100).value ).to eq('ns1.domain.ltd. admin.domain.ltd. 100 3600 3600 3600 3600')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should return correct host" do
|
|
107
|
+
zone, io = test_zone
|
|
108
|
+
expect( DNSer::SoaRecord.new(zone, zone.name, email: 'admin@domain.ltd', nameserver: 'ns1.domain.ltd.', serial: 100).host ).to eq(zone.host)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context 'for SRV record' do
|
|
113
|
+
it "should create " do
|
|
114
|
+
zone, io = test_zone
|
|
115
|
+
expect( DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', service: 'test', protocol: 'tcp', port: 80) ).to be_a(DNSer::BaseRecord)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "should validate service " do
|
|
119
|
+
zone, io = test_zone
|
|
120
|
+
expect{ DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', protocol: 'tcp', port: 80) }.to raise_error(DNSer::Record::EmptyValue)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should validate protocol" do
|
|
124
|
+
zone, io = test_zone
|
|
125
|
+
expect{ DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', service: 'test', port: 80) }.to raise_error(DNSer::Record::EmptyValue)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "should validate port " do
|
|
129
|
+
zone, io = test_zone
|
|
130
|
+
expect{ DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', service: 'test', protocol: 'tcp') }.to raise_error(DNSer::Record::EmptyValue)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should return correct value" do
|
|
134
|
+
zone, io = test_zone
|
|
135
|
+
expect( DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', service: 'test', protocol: 'tcp', port: 80, weight: 100, priority: 1).value ).to eq('1 100 80 1.1.1.1')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "should return support host" do
|
|
139
|
+
zone, io = test_zone
|
|
140
|
+
expect( DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', service: 'test', protocol: 'tcp', port: 80, weight: 100, priority: 1).host ).to eq("_test._tcp.dev.#{zone.host}")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "should support weight" do
|
|
144
|
+
zone, io = test_zone
|
|
145
|
+
expect( DNSer::SrvRecord.new(zone, 'dev', '1.1.1.1', service: 'test', protocol: 'tcp', port: 80, weight: 100, priority: 1) ).to respond_to('weight')
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
|
4
|
+
# loaded once.
|
|
5
|
+
#
|
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
RSpec.configure do |config|
|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
11
|
+
config.run_all_when_everything_filtered = true
|
|
12
|
+
config.filter_run :focus
|
|
13
|
+
|
|
14
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
15
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
16
|
+
# the seed, which is printed after each run.
|
|
17
|
+
# --seed 1234
|
|
18
|
+
config.order = 'random'
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "rspec"
|
|
2
|
+
require ::File.expand_path('../../lib/dnser.rb', __FILE__)
|
|
3
|
+
|
|
4
|
+
describe DNSer::Template do
|
|
5
|
+
|
|
6
|
+
def test_zone &block
|
|
7
|
+
io = StringIO.new
|
|
8
|
+
zone = DNSer.domain 'domain.ltd', builder: io, &block
|
|
9
|
+
[zone, io]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should create template" do
|
|
13
|
+
expect( DNSer::create_template(:empty) ).to be_a(DNSer::Template )
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should be available" do
|
|
17
|
+
expect(DNSer::apply_template(:empty)).to be_a(DNSer::Template)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should be throw error on unknown template" do
|
|
21
|
+
expect { DNSer::apply_template :other }.to raise_error(DNSer::Template::Unknown)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'should apply template for zone' do
|
|
25
|
+
|
|
26
|
+
DNSer::create_template(:first) do
|
|
27
|
+
A host, '1.1.1.1'
|
|
28
|
+
end
|
|
29
|
+
zone, io = test_zone do
|
|
30
|
+
apply_first
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
expect(io.string).to include('IN A')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should arguments params' do
|
|
37
|
+
DNSer::create_template(:first) do |arg|
|
|
38
|
+
A host, arg
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
zone, io = test_zone do
|
|
42
|
+
apply_first 'test.com.'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
expect(io.string).to include('test.com.')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'should support default params' do
|
|
49
|
+
|
|
50
|
+
DNSer::create_template(:first, host: 'sub', target: 'domain.ltd.') do |arg, params|
|
|
51
|
+
A params[:host], params[:target]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
zone, io = test_zone do
|
|
55
|
+
apply_first 'test.com.'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
expect(io.string).to include('sub')
|
|
59
|
+
expect(io.string).to include('domain.ltd.')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'should apply with params' do
|
|
63
|
+
|
|
64
|
+
zone, io = test_zone do
|
|
65
|
+
apply_first 'test.com.', target: 'foo.ltd.'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
expect(io.string).to include('foo.ltd.')
|
|
69
|
+
end
|
|
70
|
+
end
|
data/spec/zone_spec.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require "rspec"
|
|
2
|
+
require ::File.expand_path('../../lib/dnser.rb', __FILE__)
|
|
3
|
+
|
|
4
|
+
describe DNSer::Domain do
|
|
5
|
+
|
|
6
|
+
it "should create zone w block" do
|
|
7
|
+
io = StringIO.new
|
|
8
|
+
zone = DNSer.domain 'domain.ltd', builder: io do
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
expect( zone ).to be_a(DNSer::Domain)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should create zone w/o block" do
|
|
16
|
+
io = StringIO.new
|
|
17
|
+
expect( DNSer.domain('domain.ltd', builder: io) ).to be_a(DNSer::Domain)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should create zone with name" do
|
|
21
|
+
io = StringIO.new
|
|
22
|
+
zone = DNSer.domain 'domain.ltd', builder: io
|
|
23
|
+
expect(zone.name).to eq('domain.ltd.')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should create zone with builder" do
|
|
27
|
+
io = StringIO.new
|
|
28
|
+
zone = DNSer.domain 'domain.ltd', builder: io
|
|
29
|
+
|
|
30
|
+
expect(zone.name).to eq('domain.ltd.')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should write in builder" do
|
|
34
|
+
io = StringIO.new
|
|
35
|
+
zone = DNSer.domain 'domain.ltd', builder: io
|
|
36
|
+
|
|
37
|
+
expect(io.string).to eq("$ORIGIN domain.ltd.\n$TTL 3600\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should set zone ttl" do
|
|
41
|
+
io = StringIO.new
|
|
42
|
+
zone = DNSer.domain 'domain.ltd', builder: io
|
|
43
|
+
zone.ttl 100
|
|
44
|
+
expect(zone.ttl).to eq(100)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
data/test.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "./lib/dnser.rb"
|
|
2
|
+
|
|
3
|
+
template :google_app do |host, code, params|
|
|
4
|
+
|
|
5
|
+
TXT host, ('google-site-verification=' + code), :comment => "Google verification code"
|
|
6
|
+
|
|
7
|
+
MX host, 'ASPMX.L.GOOGLE.COM.', priority: 1, comment: "GMail"
|
|
8
|
+
MX host, 'ALT1.ASPMX.L.GOOGLE.COM.', priority: 5, comment: "GMail"
|
|
9
|
+
MX host, 'ALT2.ASPMX.L.GOOGLE.COM.', priority: 5, comment: "GMail"
|
|
10
|
+
MX host, 'ASPMX2.GOOGLEMAIL.COM.', priority: 10, comment: "GMail"
|
|
11
|
+
MX host, 'ASPMX23.GOOGLEMAIL.COM.', priority: 10, comment: "GMail"
|
|
12
|
+
TXT host, 'v=spf1 include:_spf.google.com ~all', comment: "GMail SPF"
|
|
13
|
+
|
|
14
|
+
%w(mail doc).each do |sub|
|
|
15
|
+
CNAME [sub, host].join('.'), 'ghs.googlehosted.com.'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
zone 'domain.ltd' do
|
|
20
|
+
ttl 3600
|
|
21
|
+
ns = NS 'ns1', 'ns1.dnsimple.com.'
|
|
22
|
+
|
|
23
|
+
SOA host do
|
|
24
|
+
nameserver ns
|
|
25
|
+
email 'admin@domain.ltd'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
A IPAddr.new('127.0.0.1'), alias: :www
|
|
29
|
+
A :dev, IPAddr.new('127.0.0.2')
|
|
30
|
+
|
|
31
|
+
apply_google_app host, '6tTalLzrBXBO4Gy9700TAbpg2QTKzGYEuZ_Ls69jle8'
|
|
32
|
+
|
|
33
|
+
SRV 'corp', 'calendar.abc.com.' do
|
|
34
|
+
service '_caldavs'
|
|
35
|
+
protocol :tcp
|
|
36
|
+
port 8443
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
SRV 'calendar2.abc.com.', service: :caldavs, protocol: :tcp, port: 8443, weight: 0, priority: 1
|
|
40
|
+
|
|
41
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dnser
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.3
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Alexey Shcherbakov
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-08-03 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: rspec
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
30
|
+
description: Clean ruby syntax for writing DNS zone file.
|
|
31
|
+
email: schalexey@gmail.com
|
|
32
|
+
executables:
|
|
33
|
+
- dnser
|
|
34
|
+
extensions: []
|
|
35
|
+
extra_rdoc_files: []
|
|
36
|
+
files:
|
|
37
|
+
- .gitignore
|
|
38
|
+
- .rspec
|
|
39
|
+
- LICENSE
|
|
40
|
+
- README.md
|
|
41
|
+
- bin/dnser
|
|
42
|
+
- dnser.gemspec
|
|
43
|
+
- lib/dnser.rb
|
|
44
|
+
- lib/dnser/builder.rb
|
|
45
|
+
- lib/dnser/builders/stream.rb
|
|
46
|
+
- lib/dnser/domain.rb
|
|
47
|
+
- lib/dnser/record.rb
|
|
48
|
+
- lib/dnser/records/base.rb
|
|
49
|
+
- lib/dnser/records/soa.rb
|
|
50
|
+
- lib/dnser/records/srv.rb
|
|
51
|
+
- lib/dnser/template.rb
|
|
52
|
+
- lib/dnser/version.rb
|
|
53
|
+
- spec/record_spec.rb
|
|
54
|
+
- spec/spec_helper.rb
|
|
55
|
+
- spec/template_spec.rb
|
|
56
|
+
- spec/zone_spec.rb
|
|
57
|
+
- test.rb
|
|
58
|
+
homepage: https://github.com/fuCtor/DNSer
|
|
59
|
+
licenses:
|
|
60
|
+
- MIT
|
|
61
|
+
post_install_message:
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
none: false
|
|
67
|
+
requirements:
|
|
68
|
+
- - ! '>='
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
none: false
|
|
73
|
+
requirements:
|
|
74
|
+
- - ! '>='
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
requirements: []
|
|
78
|
+
rubyforge_project:
|
|
79
|
+
rubygems_version: 1.8.23
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 3
|
|
82
|
+
summary: DNS zone file in ruby
|
|
83
|
+
test_files: []
|