clouddns 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clouddns.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+ require 'bundler/gem_tasks'
4
+
5
+ task :default => [:test]
6
+
7
+ desc 'Run tests.'
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << 'lib' << 'test'
10
+ t.pattern = 'test/**/*_test.rb'
11
+ t.verbose = true
12
+ end
13
+
@@ -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
+
@@ -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
@@ -0,0 +1,10 @@
1
+ require "fog"
2
+
3
+ require "clouddns/version"
4
+ require "clouddns/dsl"
5
+ require "clouddns/zone"
6
+ require "clouddns/record"
7
+ require "clouddns/actions"
8
+
9
+ module Clouddns
10
+ end
@@ -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,13 @@
1
+ module Clouddns
2
+ module Actions
3
+ class GenericAction
4
+ def initialize zone, options = {}
5
+ @zone = zone
6
+ @options = options
7
+ end
8
+ def self.run zone, options = {}
9
+ new(zone, options).run
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
+
@@ -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
+
@@ -0,0 +1,3 @@
1
+ module Clouddns
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module Clouddns
3
+ class Zone
4
+ attr_accessor :name, :records
5
+ def initialize name
6
+ @name = name
7
+ @records = []
8
+ end
9
+ end
10
+ end
11
+
@@ -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
+
@@ -0,0 +1,5 @@
1
+
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'clouddns'
5
+
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