dnsomatic 0.2.2 → 0.4.0
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/bin/dnsomatic +6 -4
- data/lib/dnsomatic.rb +2 -2
- data/lib/dnsomatic/config.rb +92 -46
- data/lib/dnsomatic/opts.rb +1 -1
- data/tests/confs/1_stanza_no_defs_no_u_p.cf +2 -0
- data/tests/confs/3_good_stanzas.cf +9 -0
- data/tests/confs/3_stanzas_no_def_no_u_p.cf +6 -0
- data/tests/confs/defs_with_bad_mx_set.cf +4 -0
- data/tests/confs/defs_with_mx_set.cf +4 -0
- data/tests/confs/defs_with_override_ipfetch.cf +4 -0
- data/tests/confs/empty_conf.cf +0 -0
- data/tests/confs/working_only_defs.cf +3 -0
- data/tests/test_config.rb +78 -0
- data/tests/test_logger.rb +76 -0
- data/tests/test_opts.rb +22 -0
- metadata +13 -2
data/bin/dnsomatic
CHANGED
@@ -34,15 +34,17 @@ begin
|
|
34
34
|
obj.send( opts.force ? 'update!' : 'update')
|
35
35
|
end
|
36
36
|
end
|
37
|
-
rescue => e
|
37
|
+
rescue SystemExit => e
|
38
|
+
# we call this in a few places (mainly from option processing [-h, -V])
|
39
|
+
# a no-op for us.
|
40
|
+
rescue Exception => e
|
38
41
|
if e.kind_of?(DNSOMatic::Error)
|
39
|
-
msg = e
|
42
|
+
msg = e.message
|
40
43
|
else
|
41
44
|
msg = "Rescued an unhandled exception of type: #{e.class}\n"
|
45
|
+
msg += e.message + "\n"
|
42
46
|
end
|
43
47
|
|
44
|
-
msg += "The exception contains the following message:\n"
|
45
|
-
msg += e.message
|
46
48
|
|
47
49
|
if opts.debug
|
48
50
|
msg += "Backtrace:\n"
|
data/lib/dnsomatic.rb
CHANGED
@@ -106,7 +106,7 @@ require 'open-uri'
|
|
106
106
|
# -h, --help Display this help text
|
107
107
|
|
108
108
|
module DNSOMatic
|
109
|
-
VERSION = '0.
|
109
|
+
VERSION = '0.4.0'
|
110
110
|
USERAGENT = "Ruby_DNS-o-Matic/#{VERSION}"
|
111
111
|
|
112
112
|
# We provide our easily distinguishable exception class so that we can easily
|
@@ -127,7 +127,7 @@ module DNSOMatic
|
|
127
127
|
rescue OpenURI::HTTPError, SocketError => e
|
128
128
|
msg = "Error communicating with #{uri.host}\n"
|
129
129
|
msg += "Message was: #{e.message}\n"
|
130
|
-
msg += "Full URL being requested: #{uri}"
|
130
|
+
msg += "Full URL being requested: #{uri}\n"
|
131
131
|
raise(DNSOMatic::Error, msg)
|
132
132
|
end
|
133
133
|
end
|
data/lib/dnsomatic/config.rb
CHANGED
@@ -1,49 +1,37 @@
|
|
1
|
-
require '
|
1
|
+
require 'resolv'
|
2
2
|
|
3
3
|
module DNSOMatic
|
4
4
|
# A class to handle 'parsing' the configuration files and setting defaults
|
5
5
|
# as required. Config files are actually YAML files, so the parsing is
|
6
6
|
# offloaded for the most part.
|
7
7
|
class Config
|
8
|
-
#in most cases, a user can simply set username and password in a defaults:
|
9
|
-
#stanza and fire the client.
|
10
|
-
@@defaults = { 'hostname' => 'all.dnsomatic.com',
|
11
|
-
'wildcard' => 'NOCHG',
|
12
|
-
'mx' => 'NOCHG',
|
13
|
-
'backmx' => 'NOCHG',
|
14
|
-
'offline' => 'NOCHG',
|
15
|
-
'webipfetchurl' => 'http://myip.dnsomatic.com/' }
|
16
|
-
|
17
8
|
# Create a new instance with the option of specifying an alternate config
|
18
9
|
# file to read. The default config file is either $HOME/.dnsomatic.cf (on
|
19
10
|
# unix or if Windows specifies a $HOME environment variable) or
|
20
11
|
# %APPDATA%/.dnsomatic.cf for most Windows environments.
|
21
12
|
def initialize(conffile = nil)
|
22
13
|
stdcf = File.join(ENV['HOME'] || ENV['APPDATA'], '.dnsomatic.cf')
|
23
|
-
|
24
|
-
|
25
|
-
@cf = conffile
|
26
|
-
else
|
27
|
-
raise(DNSOMatic::ConfErr, "Invalid config file: #{conffile}")
|
28
|
-
end
|
29
|
-
elsif File.exists?(stdcf)
|
30
|
-
@cf = stdcf
|
31
|
-
else
|
32
|
-
#in this case, the values from the defaults: stanza in the default conf
|
33
|
-
#will provide all values required.
|
34
|
-
@cf = nil
|
35
|
-
end
|
14
|
+
@cf = conffile.nil? ? stdcf : conffile
|
15
|
+
raise(DNSOMatic::Error, "Invalid config file: #{conffile}") unless File.exists?(@cf)
|
36
16
|
|
37
17
|
@updaters = nil
|
38
18
|
@config = {}
|
39
19
|
|
40
|
-
#
|
41
|
-
#
|
42
|
-
@
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
20
|
+
#in most cases, a user can simply set username and password in a defaults:
|
21
|
+
#stanza and fire the client.
|
22
|
+
@defaults = { 'hostname' => 'all.dnsomatic.com',
|
23
|
+
'wildcard' => 'NOCHG',
|
24
|
+
'mx' => 'NOCHG',
|
25
|
+
'backmx' => 'NOCHG',
|
26
|
+
'offline' => 'NOCHG',
|
27
|
+
'webipfetchurl' => 'http://myip.dnsomatic.com/' }
|
28
|
+
|
29
|
+
@type_validators = { 'hostname' => 'host',
|
30
|
+
'wildcard' => 'on_nochg',
|
31
|
+
'mx' => 'host',
|
32
|
+
'backmx' => 'yes_nochg',
|
33
|
+
'offline' => 'yes_nochg',
|
34
|
+
'webipfetchurl' => 'url' }
|
47
35
|
|
48
36
|
load()
|
49
37
|
end
|
@@ -86,37 +74,61 @@ module DNSOMatic
|
|
86
74
|
end
|
87
75
|
|
88
76
|
def load
|
89
|
-
return @@defaults if @cf.nil?
|
90
|
-
|
91
77
|
conf = DNSOMatic::yaml_read(@cf)
|
92
|
-
raise DNSOMatic::
|
78
|
+
raise DNSOMatic::Error, "Invalid configuration format in #{@cf}" unless conf.kind_of?(Hash)
|
93
79
|
|
94
80
|
if conf.has_key?('defaults')
|
95
81
|
#allow the user to override our built-in defaults
|
96
|
-
|
82
|
+
@defaults.merge!(conf['defaults'])
|
97
83
|
#if they've provided only the defaults stanza, we'll use it to perform
|
98
|
-
#the update, otherwise remove it as it has been folded into
|
84
|
+
#the update, otherwise remove it as it has been folded into @defaults
|
99
85
|
conf.delete('defaults') if conf.keys.size > 1
|
100
86
|
end
|
101
87
|
|
102
|
-
conf.each_key do |
|
103
|
-
stanza =
|
104
|
-
@req_conf.each do |required|
|
105
|
-
#still test for existence in case the defaults get munged.
|
106
|
-
if !stanza.has_key?(required) or stanza[required].nil?
|
107
|
-
msg = "Invalid configuration for Host Updater named '#{token}'\n"
|
108
|
-
msg += "Please define the field: #{required}."
|
109
|
-
raise DNSOMatic::ConfErr, msg
|
110
|
-
end
|
111
|
-
end
|
88
|
+
conf.each_key do |name|
|
89
|
+
stanza = @defaults.merge(conf[name])
|
112
90
|
|
113
91
|
#just in case
|
114
92
|
stanza.each_pair do |k,v|
|
115
93
|
stanza[k] = fmt(v)
|
116
94
|
end
|
117
95
|
|
96
|
+
validate(name, stanza)
|
97
|
+
|
118
98
|
#save our merged version in case we're just dump our config to stdout
|
119
|
-
@config[
|
99
|
+
@config[name] = stanza
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate(name, stanza)
|
104
|
+
#first, ensure we have the _required_ fields in an update def stanza
|
105
|
+
%w(username password).each do |required|
|
106
|
+
#still test for existence in case the defaults get munged.
|
107
|
+
if !stanza.has_key?(required) or stanza[required].nil?
|
108
|
+
msg = "Invalid configuration for Host Updater named '#{name}'\n"
|
109
|
+
msg += "Please define the field: #{required}.\n"
|
110
|
+
raise(DNSOMatic::Error, msg)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
@type_validators.each do |field, validator|
|
115
|
+
self.send("validate_#{validator}", field, stanza[field])
|
116
|
+
end
|
117
|
+
|
118
|
+
#the dnsomatic api spec indicates that mx/back mx can be either NOCHG
|
119
|
+
#or a hostname that must resolve to an IP.
|
120
|
+
%w(mx backmx).each do |mxtype|
|
121
|
+
mxval = stanza[mxtype]
|
122
|
+
next if mxval.eql?('NOCHG')
|
123
|
+
|
124
|
+
begin
|
125
|
+
Resolv.getaddress(mxval)
|
126
|
+
rescue Resolv::ResolvError => e
|
127
|
+
msg = "Invalid value for #{mxtype}.\n"
|
128
|
+
msg += "It must be either NOCHG or a valid hostname.\n"
|
129
|
+
msg += e.message + "\n"
|
130
|
+
raise(DNSOMatic::Error, msg)
|
131
|
+
end
|
120
132
|
end
|
121
133
|
end
|
122
134
|
|
@@ -138,5 +150,39 @@ module DNSOMatic
|
|
138
150
|
val.to_s.gsub(/\s+/, '')
|
139
151
|
end
|
140
152
|
end
|
153
|
+
|
154
|
+
def validate_host(field, val)
|
155
|
+
return true if val.eql?('NOCHG')
|
156
|
+
return true if val.match(/[^\s]+\.[^\s]+/) #no great, but workable
|
157
|
+
msg = "Invalid hostname defined for #{field}.\n"
|
158
|
+
msg += "You gave: #{val}\n"
|
159
|
+
raise(DNSOMatic::Error, msg)
|
160
|
+
end
|
161
|
+
|
162
|
+
def validate_yes_nochg(field, val)
|
163
|
+
valid = %w(YES NO NOCHG)
|
164
|
+
return true if valid.include?(val.upcase)
|
165
|
+
msg = "Invalid value for #{field}.\n"
|
166
|
+
msg += "It should be one of: #{valid.join(', ')}\n"
|
167
|
+
msg += "You gave: #{val}\n"
|
168
|
+
raise(DNSOMatic::Error, msg)
|
169
|
+
end
|
170
|
+
|
171
|
+
def validate_on_nochg(field, val)
|
172
|
+
valid = %w(ON OFF NOCHG)
|
173
|
+
return true if valid.include?(val.upcase)
|
174
|
+
msg = "Invalid value for #{field}.\n"
|
175
|
+
msg += "It should be one of: #{valid.join(', ')}\n"
|
176
|
+
msg += "You gave: #{val}\n"
|
177
|
+
raise(DNSOMatic::Error, msg)
|
178
|
+
end
|
179
|
+
|
180
|
+
def validate_url(field, val)
|
181
|
+
return true if val.match('http.//.*')
|
182
|
+
msg = "Invalid value for #{field}.\n"
|
183
|
+
msg += "It should be on http(s)-style URL.\n"
|
184
|
+
msg += "You gave: #{val}\n"
|
185
|
+
raise(DNSOMatic::Error, msg)
|
186
|
+
end
|
141
187
|
end
|
142
188
|
end
|
data/lib/dnsomatic/opts.rb
CHANGED
@@ -84,7 +84,7 @@ module DNSOMatic
|
|
84
84
|
rescue OptionParser::ParseError => e
|
85
85
|
msg = "Extra/Unknown arguments used:\n"
|
86
86
|
msg += "\t#{e.message}\n"
|
87
|
-
msg += "Remaining args: #{args.join(', ')}" if args.size > 0
|
87
|
+
msg += "Remaining args: #{args.join(', ')}\n" if args.size > 0
|
88
88
|
raise(DNSOMatic::Error, msg)
|
89
89
|
end
|
90
90
|
end
|
File without changes
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
$: << '../lib'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
require 'dnsomatic'
|
7
|
+
|
8
|
+
class TestConfig < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
$cfd = File.join(Dir.pwd, 'confs')
|
11
|
+
$orig_home = ENV['HOME']
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_no_exception_with_basic_default_conf
|
15
|
+
assert_nothing_raised { DNSOMatic::Config.new }
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_exception_on_no_default_config_file
|
19
|
+
ENV['HOME'] = '/tmp'
|
20
|
+
assert_raises(DNSOMatic::Error) { DNSOMatic::Config.new }
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_non_existent_conf_raises_exception
|
24
|
+
conf = File.join($cfd, 'bad_conf_file_name.cf')
|
25
|
+
assert_raises(DNSOMatic::Error) { DNSOMatic::Config.new(conf) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_empty_conf_raises_exception
|
29
|
+
conf = File.join($cfd, 'empty_conf.cf')
|
30
|
+
assert_raises(DNSOMatic::Error) { DNSOMatic::Config.new(conf) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_no_exception_raised_for_basic_conf
|
34
|
+
conf = File.join($cfd, 'working_only_defs.cf')
|
35
|
+
assert_nothing_raised { DNSOMatic::Config.new(conf) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_no_exception_raised_for_conf_with_multi_stanzas
|
39
|
+
conf = File.join($cfd, '3_good_stanzas.cf')
|
40
|
+
assert_nothing_raised { DNSOMatic::Config.new(conf) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_1_def_and_3_alternates_gives_3_updaters
|
44
|
+
conf = File.join($cfd, '3_good_stanzas.cf')
|
45
|
+
c = DNSOMatic::Config.new(conf)
|
46
|
+
assert_equal(3, c.updaters.length)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_3_stanzas_no_def_no_u_p_raises
|
50
|
+
conf = File.join($cfd, '3_stanzas_no_def_no_u_p.cf')
|
51
|
+
assert_raises(DNSOMatic::Error) { DNSOMatic::Config.new(conf) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_no_defs_no_u_p_raises
|
55
|
+
conf = File.join($cfd, '1_stanza_no_defs_no_u_p.cf')
|
56
|
+
assert_raises(DNSOMatic::Error) { DNSOMatic::Config.new(conf) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_override_webipfetchurl_works
|
60
|
+
conf = File.join($cfd, 'defs_with_override_ipfetch.cf')
|
61
|
+
c = DNSOMatic::Config.new(conf)
|
62
|
+
#so we know our match below isn't hitting on another stanza
|
63
|
+
assert_equal(1, c.updaters.length)
|
64
|
+
assert_match(/.*webipfetchurl: http:\/\/example.com.*/m, c.merged_config)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_mx_rejects_improper_values
|
68
|
+
#either NOCHG or a name that resolves to an IP
|
69
|
+
conf = File.join($cfd, 'defs_with_mx_set.cf')
|
70
|
+
assert_nothing_raised { DNSOMatic::Config.new(conf) }
|
71
|
+
conf = File.join($cfd, 'defs_with_bad_mx_set.cf')
|
72
|
+
assert_raises(DNSOMatic::Error) { DNSOMatic::Config.new(conf) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def teardown
|
76
|
+
ENV['HOME'] = $orig_home
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
$: << '../lib'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
require 'dnsomatic'
|
8
|
+
|
9
|
+
class TestLogger < Test::Unit::TestCase
|
10
|
+
def setup
|
11
|
+
$opts = DNSOMatic::Opts.instance
|
12
|
+
$stdout = StringIO.new('')
|
13
|
+
$opts.parse([])
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_nothing_logged_if_not_verbose
|
17
|
+
DNSOMatic::Logger.log('nothing logged')
|
18
|
+
assert_equal('', $stdout.string)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_log_matches_when_verbose
|
22
|
+
$opts.parse(%w(-v)) #enable verbose so that logs are generated
|
23
|
+
DNSOMatic::Logger.log('logged')
|
24
|
+
assert_equal("logged\n", $stdout.string)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_nothing_alerted_without_verb_or_alert
|
28
|
+
DNSOMatic::Logger.alert('nothing logged')
|
29
|
+
assert_equal('', $stdout.string)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_alert_with_alert_opt
|
33
|
+
$opts.parse(%w(-a))
|
34
|
+
DNSOMatic::Logger.alert('something logged')
|
35
|
+
assert_equal("something logged\n", $stdout.string)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_alert_with_verbose_opt
|
39
|
+
$opts.parse(%w(-v))
|
40
|
+
DNSOMatic::Logger.alert('something logged')
|
41
|
+
assert_equal("something logged\n", $stdout.string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_alert_with_verbose_and_alert_opts
|
45
|
+
$opts.parse(%w(-v -a))
|
46
|
+
DNSOMatic::Logger.alert('something logged')
|
47
|
+
assert_equal("something logged\n", $stdout.string)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_warn_without_verbose_or_alert
|
51
|
+
DNSOMatic::Logger.warn('something logged')
|
52
|
+
assert_equal("something logged\n", $stdout.string)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_warn_with_verbose
|
56
|
+
$opts.parse(%w(-v))
|
57
|
+
DNSOMatic::Logger.warn('something logged')
|
58
|
+
assert_equal("something logged\n", $stdout.string)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_warn_with_alert
|
62
|
+
$opts.parse(%w(-a))
|
63
|
+
DNSOMatic::Logger.warn('something logged')
|
64
|
+
assert_equal("something logged\n", $stdout.string)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_warn_with_alert_and_verbose
|
68
|
+
$opts.parse(%w(-a -v))
|
69
|
+
DNSOMatic::Logger.warn('something logged')
|
70
|
+
assert_equal("something logged\n", $stdout.string)
|
71
|
+
end
|
72
|
+
|
73
|
+
def teardown
|
74
|
+
$stdout.truncate(0)
|
75
|
+
end
|
76
|
+
end
|
data/tests/test_opts.rb
CHANGED
@@ -9,6 +9,24 @@ require 'dnsomatic'
|
|
9
9
|
class TestOpts < Test::Unit::TestCase
|
10
10
|
def setup
|
11
11
|
$opts = DNSOMatic::Opts.instance
|
12
|
+
$stdout = StringIO.new('')
|
13
|
+
$opts.parse([])
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_version_request_exits
|
17
|
+
assert_raises(SystemExit) { $opts.parse(%w(-V)) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_version_number_sane
|
21
|
+
begin
|
22
|
+
$opts.parse(%w(-V))
|
23
|
+
rescue SystemExit
|
24
|
+
assert_equal("#{DNSOMatic::VERSION}\n", $stdout.string)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_help_exits
|
29
|
+
assert_raises(SystemExit) { $opts.parse(%w(-h)) }
|
12
30
|
end
|
13
31
|
|
14
32
|
def test_set_verbose
|
@@ -74,4 +92,8 @@ class TestOpts < Test::Unit::TestCase
|
|
74
92
|
def test_extra_args_raise_exception
|
75
93
|
assert_raise(DNSOMatic::Error) { $opts.parse(%w(somearg)) }
|
76
94
|
end
|
95
|
+
|
96
|
+
def teardown
|
97
|
+
$stdout.truncate(0)
|
98
|
+
end
|
77
99
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dnsomatic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Walton
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-09-01 00:00:00 -04:00
|
13
13
|
default_executable: dnsomatic
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -32,8 +32,19 @@ files:
|
|
32
32
|
- lib/dnsomatic.rb
|
33
33
|
- tests/test_ipstatus.rb
|
34
34
|
- tests/test_opts.rb
|
35
|
+
- tests/test_logger.rb
|
35
36
|
- tests/test_updater.rb
|
36
37
|
- tests/all_tests.rb
|
38
|
+
- tests/confs
|
39
|
+
- tests/confs/1_stanza_no_defs_no_u_p.cf
|
40
|
+
- tests/confs/working_only_defs.cf
|
41
|
+
- tests/confs/defs_with_mx_set.cf
|
42
|
+
- tests/confs/3_good_stanzas.cf
|
43
|
+
- tests/confs/empty_conf.cf
|
44
|
+
- tests/confs/defs_with_override_ipfetch.cf
|
45
|
+
- tests/confs/defs_with_bad_mx_set.cf
|
46
|
+
- tests/confs/3_stanzas_no_def_no_u_p.cf
|
47
|
+
- tests/test_config.rb
|
37
48
|
- tests/test_iplookup.rb
|
38
49
|
has_rdoc: true
|
39
50
|
homepage: http://rubyforge.org/projects/dnsomatic
|