clouddns 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/Rakefile +13 -0
- data/bin/clouddns +47 -0
- data/clouddns.gemspec +17 -0
- data/lib/clouddns.rb +10 -0
- data/lib/clouddns/actions.rb +18 -0
- data/lib/clouddns/actions/generic_action.rb +13 -0
- data/lib/clouddns/actions/migrate.rb +101 -0
- data/lib/clouddns/actions/print.rb +23 -0
- data/lib/clouddns/actions/print_record.rb +19 -0
- data/lib/clouddns/dsl.rb +62 -0
- data/lib/clouddns/record.rb +24 -0
- data/lib/clouddns/version.rb +3 -0
- data/lib/clouddns/zone.rb +11 -0
- data/test/dsl_test.rb +23 -0
- data/test/test_helper.rb +5 -0
- metadata +83 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/clouddns
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'optparse'
|
5
|
+
require 'clouddns'
|
6
|
+
|
7
|
+
options = {
|
8
|
+
:fog => {},
|
9
|
+
:force => false
|
10
|
+
}
|
11
|
+
|
12
|
+
OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: #{$0} [OPTIONS] [ACTION] FILES"
|
14
|
+
|
15
|
+
opts.on('-p', '--provider provider', "Override DNS provider (AWS, Zerigo, etc)") do |provider|
|
16
|
+
options[:fog][:provider] = provider
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on('-f', '--force', 'Assume yes to all questions') do
|
20
|
+
options[:force] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('-v', '--version') do
|
24
|
+
puts "clouddns v#{Clouddns::VERSION}"
|
25
|
+
exit(0)
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
29
|
+
puts opts
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end.parse!
|
33
|
+
|
34
|
+
action = ARGV.slice!(0)
|
35
|
+
files = ARGV
|
36
|
+
|
37
|
+
raise "No action specified" unless action
|
38
|
+
raise "No files specified" if files.empty?
|
39
|
+
|
40
|
+
zones = files.map do |file|
|
41
|
+
Clouddns::DSL.parse_file(file).zones
|
42
|
+
end.flatten
|
43
|
+
|
44
|
+
zones.each do |zone|
|
45
|
+
Clouddns::Actions.by_name(action).run(zone, options)
|
46
|
+
end
|
47
|
+
|
data/clouddns.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/clouddns/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["John Hawthorn"]
|
6
|
+
gem.email = ["john.hawthorn@gmail.com"]
|
7
|
+
gem.description = %q{A DSL for managing DNS through cloud services}
|
8
|
+
gem.summary = %q{A DSL and tools for managing DNS through any of the services supported by fog. Currently, this is Amazon Route 53, bluebox, DNSimple, DNS Made Easy, Linode DNS, Slicehost DNS and Zerigo DNS }
|
9
|
+
gem.homepage = 'https://github.com/jhawthorn/clouddns'
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "clouddns"
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
gem.version = Clouddns::VERSION
|
17
|
+
end
|
data/lib/clouddns.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
require 'clouddns/actions/generic_action'
|
3
|
+
require 'clouddns/actions/print'
|
4
|
+
require 'clouddns/actions/migrate'
|
5
|
+
|
6
|
+
module Clouddns
|
7
|
+
module Actions
|
8
|
+
def self.by_name name
|
9
|
+
case name.downcase
|
10
|
+
when 'print' then Print
|
11
|
+
when 'migrate' then Migrate
|
12
|
+
else
|
13
|
+
raise "Unknown action: #{name}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'clouddns/actions/print_record'
|
2
|
+
|
3
|
+
module Clouddns
|
4
|
+
module Actions
|
5
|
+
class Migrate < GenericAction
|
6
|
+
include PrintRecord
|
7
|
+
|
8
|
+
def run
|
9
|
+
@fog = Fog::DNS.new(@options[:fog])
|
10
|
+
|
11
|
+
@fog_zone = @fog.zones.find { |z| z.domain == @zone.name }
|
12
|
+
|
13
|
+
puts
|
14
|
+
puts "Migrating '#{@zone.name}'"
|
15
|
+
|
16
|
+
unless @fog_zone
|
17
|
+
puts
|
18
|
+
puts "Zone '#{@zone.name}' does not exist. Creating..."
|
19
|
+
require_confirmation!
|
20
|
+
@fog_zone = @fog.zones.create(:domain => @zone.name)
|
21
|
+
puts "Zone '#{@zone.name}' created."
|
22
|
+
end
|
23
|
+
|
24
|
+
# required to pick up nameservers
|
25
|
+
@fog_zone = @fog_zone.reload
|
26
|
+
|
27
|
+
puts
|
28
|
+
puts "Nameservers:"
|
29
|
+
@fog_zone.nameservers.each do |nameserver|
|
30
|
+
puts " #{nameserver}"
|
31
|
+
end
|
32
|
+
|
33
|
+
puts
|
34
|
+
puts "Changes:"
|
35
|
+
@records_to_remove = []
|
36
|
+
@records_to_add = []
|
37
|
+
|
38
|
+
@namelength = (@fog_zone.records.map(&:name) + @zone.records.map(&:name)).map(&:length).max
|
39
|
+
|
40
|
+
@actions = []
|
41
|
+
|
42
|
+
fog_records = Hash[@fog_zone.records.map {|r| [[r.name.gsub('\052', '*'), r.type], r] } ]
|
43
|
+
@zone.records.each do |record|
|
44
|
+
if (fog_record = fog_records.delete([record.name, record.type]))
|
45
|
+
if equal?(record, fog_record)
|
46
|
+
@actions << [' ', record]
|
47
|
+
else
|
48
|
+
@actions << ['-', fog_record]
|
49
|
+
@actions << ['+', record]
|
50
|
+
end
|
51
|
+
else
|
52
|
+
@actions << ['+', record]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
fog_records.each do |name, record|
|
57
|
+
@actions << ['-', record]
|
58
|
+
end
|
59
|
+
|
60
|
+
@actions.each do |(action, record)|
|
61
|
+
print_record record, @namelength, action
|
62
|
+
end
|
63
|
+
|
64
|
+
# no changes required
|
65
|
+
return if @actions.all? {|(action, record)| action == ' ' }
|
66
|
+
|
67
|
+
require_confirmation!
|
68
|
+
|
69
|
+
@actions.each do |(action, record)|
|
70
|
+
if action == '+'
|
71
|
+
@fog_zone.records.create(:type => record.type, :name => record.name, :ip => record.value, :ttl => record.ttl)
|
72
|
+
elsif action == '-'
|
73
|
+
record.destroy
|
74
|
+
end
|
75
|
+
end
|
76
|
+
puts "Done"
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def equal? record, fog_record
|
82
|
+
# AWS replaces * with \052
|
83
|
+
record.name == fog_record.name.gsub('\052', '*') &&
|
84
|
+
record.type == fog_record.type &&
|
85
|
+
record.value == fog_record.ip &&
|
86
|
+
record.ttl.to_i == fog_record.ttl.to_i
|
87
|
+
end
|
88
|
+
|
89
|
+
def require_confirmation!
|
90
|
+
print "Type 'Yes' to continute: "
|
91
|
+
STDOUT.flush
|
92
|
+
|
93
|
+
unless STDIN.readline.strip == 'Yes'
|
94
|
+
puts "aborting"
|
95
|
+
exit 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'clouddns/actions/print_record'
|
2
|
+
|
3
|
+
module Clouddns
|
4
|
+
module Actions
|
5
|
+
class Print < GenericAction
|
6
|
+
include PrintRecord
|
7
|
+
|
8
|
+
def run
|
9
|
+
print_all
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
def print_all
|
14
|
+
puts "Zone '#{@zone.name}'"
|
15
|
+
namelength = @zone.records.map{ |x| x.name.length }.max
|
16
|
+
@zone.records.each do |record|
|
17
|
+
print_record record, namelength
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Clouddns
|
2
|
+
module Actions
|
3
|
+
module PrintRecord
|
4
|
+
protected
|
5
|
+
def print_record record, namelength, prefix=''
|
6
|
+
values = record.respond_to?(:value) ? record.value : record.ip
|
7
|
+
values.each_with_index do |value, i|
|
8
|
+
if i.zero?
|
9
|
+
args = [prefix, record.type, record.ttl, record.name, value]
|
10
|
+
else
|
11
|
+
args = [prefix, '', '', '', value]
|
12
|
+
end
|
13
|
+
puts "%s%5s %5s %#{namelength}s %s" % args
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/lib/clouddns/dsl.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Clouddns
|
2
|
+
class DSL
|
3
|
+
attr_reader :zones
|
4
|
+
|
5
|
+
# This need not be too strict. Only exists to help catch typos.
|
6
|
+
DNS_REGEX = /\A.*\..*\.\z/
|
7
|
+
|
8
|
+
def scope options={}
|
9
|
+
oldscope = @scope
|
10
|
+
@scope = @scope.merge(options)
|
11
|
+
yield
|
12
|
+
@scope = oldscope
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@zones = []
|
17
|
+
@scope = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_string string
|
21
|
+
dsl = DSL.new
|
22
|
+
dsl.instance_eval string
|
23
|
+
dsl
|
24
|
+
end
|
25
|
+
def self.parse_file filename
|
26
|
+
parse_string open(filename).read
|
27
|
+
end
|
28
|
+
|
29
|
+
def A *args
|
30
|
+
add_record 'A', *args
|
31
|
+
end
|
32
|
+
def CNAME *args
|
33
|
+
add_record 'CNAME', *args
|
34
|
+
end
|
35
|
+
def MX *args
|
36
|
+
add_record 'MX', *args
|
37
|
+
end
|
38
|
+
def NS *args
|
39
|
+
add_record 'NS', *args
|
40
|
+
end
|
41
|
+
def TXT name, value, options={}
|
42
|
+
add_record 'TXT', name, "\"#{value}\"", options
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_record type, name, value, options={}
|
46
|
+
zone = @scope[:zone]
|
47
|
+
|
48
|
+
raise "records must be added inside a zone" unless zone
|
49
|
+
raise "record's dns name must end with the current zone" unless name.end_with? zone.name
|
50
|
+
|
51
|
+
@scope[:zone].records << Record.new(type, name, value, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def zone name, &block
|
55
|
+
raise "zone must be at the top level" if @scope[:zone]
|
56
|
+
|
57
|
+
zone = Zone.new(name)
|
58
|
+
@zones << zone
|
59
|
+
scope :zone => zone, &block
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Clouddns
|
3
|
+
class Record
|
4
|
+
attr_accessor :attributes
|
5
|
+
alias :to_hash :attributes
|
6
|
+
|
7
|
+
def initialize type, name, value, options = {}
|
8
|
+
value = [value] unless value.is_a? Array
|
9
|
+
options[:ttl] ||= 600
|
10
|
+
|
11
|
+
self.attributes = options.merge({
|
12
|
+
:type => type,
|
13
|
+
:name => name,
|
14
|
+
:value => value
|
15
|
+
})
|
16
|
+
end
|
17
|
+
|
18
|
+
def name; attributes[:name]; end
|
19
|
+
def value; attributes[:value]; end
|
20
|
+
def type; attributes[:type]; end
|
21
|
+
def ttl; attributes[:ttl]; end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/test/dsl_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class DslTest < Test::Unit::TestCase
|
5
|
+
def test_simple
|
6
|
+
dsl = Clouddns::DSL.parse_string <<-EOL
|
7
|
+
zone "example.com." do
|
8
|
+
A 'www.example.com.', '1.2.3.4'
|
9
|
+
end
|
10
|
+
EOL
|
11
|
+
assert_equal 1, dsl.zones.count
|
12
|
+
|
13
|
+
zone = dsl.zones.first
|
14
|
+
assert_equal 'example.com.', zone.name
|
15
|
+
assert_equal 1, zone.records.count
|
16
|
+
|
17
|
+
record = zone.records.first
|
18
|
+
assert_equal 'A', record.type
|
19
|
+
assert_equal 'www.example.com.', record.name
|
20
|
+
assert_equal ['1.2.3.4'], record.value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clouddns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- John Hawthorn
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-11 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: A DSL for managing DNS through cloud services
|
22
|
+
email:
|
23
|
+
- john.hawthorn@gmail.com
|
24
|
+
executables:
|
25
|
+
- clouddns
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- Rakefile
|
34
|
+
- bin/clouddns
|
35
|
+
- clouddns.gemspec
|
36
|
+
- lib/clouddns.rb
|
37
|
+
- lib/clouddns/actions.rb
|
38
|
+
- lib/clouddns/actions/generic_action.rb
|
39
|
+
- lib/clouddns/actions/migrate.rb
|
40
|
+
- lib/clouddns/actions/print.rb
|
41
|
+
- lib/clouddns/actions/print_record.rb
|
42
|
+
- lib/clouddns/dsl.rb
|
43
|
+
- lib/clouddns/record.rb
|
44
|
+
- lib/clouddns/version.rb
|
45
|
+
- lib/clouddns/zone.rb
|
46
|
+
- test/dsl_test.rb
|
47
|
+
- test/test_helper.rb
|
48
|
+
homepage: https://github.com/jhawthorn/clouddns
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.5
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: A DSL and tools for managing DNS through any of the services supported by fog. Currently, this is Amazon Route 53, bluebox, DNSimple, DNS Made Easy, Linode DNS, Slicehost DNS and Zerigo DNS
|
81
|
+
test_files:
|
82
|
+
- test/dsl_test.rb
|
83
|
+
- test/test_helper.rb
|