ganddyn 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +5 -0
- data/bin/ganddyn +17 -0
- data/ganddyn.gemspec +32 -0
- data/lib/ganddyn/client.rb +297 -0
- data/lib/ganddyn/ipresolver.rb +44 -0
- data/lib/ganddyn/version.rb +3 -0
- data/lib/ganddyn.rb +5 -0
- data/spec/lib/ganddyn/client_spec.rb +559 -0
- data/spec/lib/ganddyn/ipresolver_spec.rb +81 -0
- data/spec/spec_helper.rb +35 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b0feada86e7855617874d6d329144969022a4c9c
|
4
|
+
data.tar.gz: 7191df63d2bea9643bc2ccdf768d2a72fc9dcc91
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 116d9fe483f702b7c70e690be5801ee37dd821b4008663cc2d8cb79991d62d71dba3dedd4d5ee1f93735ab5740c7d6d59b984d91b34343208ea90fc45de4b6d7
|
7
|
+
data.tar.gz: 66a5d1db13fe7dd1c29bdcee78fd03f3139f3c65065e1405e5bc560c7f0f6f6a3632db5217cefd6496f07e79e7cb22b9686c0da3839b5450330e79cffd99564a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Guillaume Virlet
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Ganddyn [](https://travis-ci.org/doc75/ganddyn)
|
2
|
+
=======
|
3
|
+
|
4
|
+
This gem allows to update your GANDI DNS zone with the current external IPv4 of your machine.
|
5
|
+
It duplicate current zone information in the last inactive version of the zone (or a newly
|
6
|
+
created one if only one version exist). It updates the IPv4 for the name requested and activate
|
7
|
+
this version.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'ganddyn'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install ganddyn
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
$ gannddyn hostname.domain.com GANDIAPIKEY /path/to/file/storing/last/ip/updated
|
26
|
+
|
27
|
+
**Warning**:
|
28
|
+
- This is an early version not fully tested, use it at your own risk.
|
29
|
+
- When used on multiple machines for the same domain, it might miss some update in case of concurrent update
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/ganddyn
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << './lib'
|
3
|
+
require 'ganddyn'
|
4
|
+
|
5
|
+
raise ArgumentError, 'Usage: ganddyn host.domain.com gandi_api_key yaml_file' if ARGV.size != 3
|
6
|
+
|
7
|
+
begin
|
8
|
+
cli = Ganddyn::Client.new({:hostname => ARGV[0], :api_key => ARGV[1], :config_file => ARGV[2]})
|
9
|
+
cli.update
|
10
|
+
retval = 0
|
11
|
+
rescue Exception => e
|
12
|
+
puts e
|
13
|
+
puts e.backtrace
|
14
|
+
retval = 1
|
15
|
+
end
|
16
|
+
|
17
|
+
exit retval
|
data/ganddyn.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ganddyn/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'ganddyn'
|
8
|
+
spec.version = Ganddyn::VERSION
|
9
|
+
spec.authors = ['Guillaume Virlet']
|
10
|
+
spec.email = ['github@virlet.org']
|
11
|
+
spec.description = %q{This gem allows to update your GANDI DNS zone with the current external IPv4 of your machine.
|
12
|
+
It duplicate current zone information in the last inactive version of the zone (or a newly
|
13
|
+
created one if only one version exist). It updates the IPv4 for the name requested and activate
|
14
|
+
this version.}
|
15
|
+
spec.summary = %q{Update GANDI DNS zone IPv4}
|
16
|
+
spec.homepage = 'https://github.com/doc75/ganddyn'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
|
+
spec.add_development_dependency 'rake', '~>10.3.1'
|
26
|
+
spec.add_development_dependency 'rspec', '~>2.14.1'
|
27
|
+
spec.add_development_dependency 'webmock', '~>1.17.4'
|
28
|
+
|
29
|
+
spec.add_dependency 'gandi', '~>2.0.10'
|
30
|
+
spec.add_dependency 'highline', '~>1.6.21'
|
31
|
+
spec.add_dependency 'certified', '~> 0.1.2'
|
32
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'gandi'
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'yaml'
|
5
|
+
require 'highline'
|
6
|
+
|
7
|
+
if RUBY_PLATFORM =~ /mingw32/
|
8
|
+
require 'certified'
|
9
|
+
end
|
10
|
+
|
11
|
+
module Ganddyn
|
12
|
+
class Client
|
13
|
+
|
14
|
+
TTL = 300
|
15
|
+
|
16
|
+
def initialize opts
|
17
|
+
raise ArgumentError, 'opts is not a Hash' unless opts.is_a? Hash
|
18
|
+
raise ArgumentError, 'opts does not contain key :hostname' unless opts.has_key? :hostname
|
19
|
+
raise ArgumentError, 'opts does not contain key :api_key' unless opts.has_key? :api_key
|
20
|
+
raise ArgumentError, 'opts does not contain key :config_file' unless opts.has_key? :config_file
|
21
|
+
|
22
|
+
@debug = false
|
23
|
+
|
24
|
+
@api_key = opts[:api_key]
|
25
|
+
@config_file = opts[:config_file]
|
26
|
+
|
27
|
+
tab = opts[:hostname].split('.')
|
28
|
+
@name = tab[0...-2].join('.')
|
29
|
+
@domain = tab[-2..-1].join('.')
|
30
|
+
|
31
|
+
@terminal = opts.has_key?(:terminal) ? opts[:terminal] : HighLine.new
|
32
|
+
|
33
|
+
begin
|
34
|
+
last_ips_gandi = YAML.load_file @config_file
|
35
|
+
print_debug last_ips_gandi.inspect, 2
|
36
|
+
rescue => e
|
37
|
+
print_debug 'Cannot load config file', 1
|
38
|
+
last_ips_gandi = false
|
39
|
+
end
|
40
|
+
|
41
|
+
if last_ips_gandi
|
42
|
+
print_debug "last_ips_gandi exist", 2
|
43
|
+
else
|
44
|
+
print_debug "last_ips_gandi is nil or false", 2
|
45
|
+
end
|
46
|
+
print_debug last_ips_gandi.inspect, 2
|
47
|
+
@last_ipv4 = (last_ips_gandi && last_ips_gandi[:ipv4].size > 0) ? last_ips_gandi[:ipv4] : nil
|
48
|
+
|
49
|
+
print_debug "Last IPv4 (form config file):#{@last_ipv4}", 1
|
50
|
+
|
51
|
+
@cur_ipv4 = nil
|
52
|
+
@api = nil
|
53
|
+
@zone_id = nil
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# return value:
|
58
|
+
# true => update done
|
59
|
+
# false => update not needed
|
60
|
+
# nil => no network found
|
61
|
+
def update
|
62
|
+
return nil unless ipv4_available?
|
63
|
+
|
64
|
+
last_ipv4 = @last_ipv4 ? @last_ipv4 : get_gandi_ipv4
|
65
|
+
|
66
|
+
update_config = false
|
67
|
+
|
68
|
+
if @last_ipv4.nil?
|
69
|
+
update_config = true
|
70
|
+
else
|
71
|
+
update_config = true if @last_ipv4.empty?
|
72
|
+
update_config = true if last_ipv4 != @last_ipv4
|
73
|
+
end
|
74
|
+
|
75
|
+
print_debug "Last IPv4 (form config file or Gandi):#{last_ipv4}", 1
|
76
|
+
|
77
|
+
# TODO: save it in YAML file to reduce Gandi queries
|
78
|
+
ip_resolv = IpResolver.new
|
79
|
+
cur_ipv4 = ipv4_available? ? ip_resolv.get_ipv4 : ''
|
80
|
+
|
81
|
+
if !cur_ipv4.empty? && cur_ipv4 != last_ipv4
|
82
|
+
to_update = cur_ipv4
|
83
|
+
update_config = true
|
84
|
+
else
|
85
|
+
to_update = ''
|
86
|
+
end
|
87
|
+
|
88
|
+
print_debug to_update.inspect, 2
|
89
|
+
retval = update_ips to_update
|
90
|
+
if retval and update_config
|
91
|
+
retval = update_config_file cur_ipv4
|
92
|
+
end
|
93
|
+
retval
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
# return value:
|
98
|
+
# nil => no network available for IPv4
|
99
|
+
# '' => no previous IPv4 stored in Ganddi DNS
|
100
|
+
# otherwise return IPv4 address as a string
|
101
|
+
def get_gandi_ipv4
|
102
|
+
get_record('A') if ipv4_available?
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_ping_option
|
106
|
+
if RUBY_PLATFORM =~ /mingw32/
|
107
|
+
return '-n', '> NUL'
|
108
|
+
else
|
109
|
+
return '-c', '> /dev/null 2>&1'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def ipv4_available?
|
114
|
+
return @ipv4_avail unless @ipv4_avail.nil?
|
115
|
+
|
116
|
+
opt, out = get_ping_option
|
117
|
+
@ipv4_avail = system("ping #{opt} 1 8.8.8.8 #{out}")
|
118
|
+
if @ipv4_avail
|
119
|
+
print_info 'IPv4 network available'
|
120
|
+
else
|
121
|
+
print_info 'IPv4 network not available'
|
122
|
+
end
|
123
|
+
@ipv4_avail
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_record type
|
127
|
+
raise ArgumentError, %Q{type is not 'A'} unless type == 'A'
|
128
|
+
res = gandi_api.domain.zone.record.list(get_zone_id, 0, {:name => @name, :type => type})
|
129
|
+
print_debug res.inspect, 2
|
130
|
+
if res.size == 0
|
131
|
+
print_debug "record not found: '#{@name}'", 1
|
132
|
+
''
|
133
|
+
else
|
134
|
+
print_debug "record found: '#{@name}' => #{res[0].value}", 1
|
135
|
+
res[0].value
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def update_ips update
|
140
|
+
|
141
|
+
raise(ArgumentError, 'update is not a String') unless update.is_a? String
|
142
|
+
|
143
|
+
if update.size == 0
|
144
|
+
print_info 'no update needed'
|
145
|
+
return true
|
146
|
+
end
|
147
|
+
|
148
|
+
cur_vers, all_vers = get_zone_version
|
149
|
+
if all_vers.size == 1
|
150
|
+
# create a new version and add it to list of all versions
|
151
|
+
new_vers = create_new_zone_version
|
152
|
+
else
|
153
|
+
all_vers = all_vers - [cur_vers]
|
154
|
+
# we take the last version of existing versions (my choice ;-)
|
155
|
+
new_vers = all_vers[-1]
|
156
|
+
clone_zone_version( cur_vers, new_vers )
|
157
|
+
end
|
158
|
+
|
159
|
+
retval = false
|
160
|
+
if update_ipv4(new_vers, update)
|
161
|
+
if activate_updated_version new_vers
|
162
|
+
retval = true
|
163
|
+
print_info 'update done'
|
164
|
+
else
|
165
|
+
retval = false
|
166
|
+
print_info 'update FAILED'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
retval
|
170
|
+
end
|
171
|
+
|
172
|
+
def activate_updated_version version
|
173
|
+
res = gandi_api.domain.zone.version.set(get_zone_id, version)
|
174
|
+
if res
|
175
|
+
print_debug 'activation of zone version successful', 1
|
176
|
+
else
|
177
|
+
print_debug 'activation of zone version failed', 1
|
178
|
+
end
|
179
|
+
res
|
180
|
+
end
|
181
|
+
|
182
|
+
def update_ipv4 zone_version, ip
|
183
|
+
update_record zone_version, ip, 'A'
|
184
|
+
end
|
185
|
+
|
186
|
+
def update_record zone_version, ip, type
|
187
|
+
return false if ip.empty?
|
188
|
+
retval = false
|
189
|
+
records = gandi_api.domain.zone.record.list( get_zone_id,
|
190
|
+
zone_version,
|
191
|
+
{:name => @name, :type => type} )
|
192
|
+
if records.size == 0
|
193
|
+
res = gandi_api.domain.zone.record.add( get_zone_id,
|
194
|
+
zone_version,
|
195
|
+
{ :name => @name,
|
196
|
+
:type => type,
|
197
|
+
:value => ip,
|
198
|
+
:ttl => TTL } )
|
199
|
+
retval = true
|
200
|
+
else
|
201
|
+
records.each do |rec|
|
202
|
+
# only update if ip is different
|
203
|
+
if rec.value != ip
|
204
|
+
res = gandi_api.domain.zone.record.update( get_zone_id,
|
205
|
+
zone_version,
|
206
|
+
{ :id => rec.id },
|
207
|
+
{ :name => rec.name,
|
208
|
+
:type => rec.type,
|
209
|
+
:value => ip,
|
210
|
+
:ttl => TTL } )
|
211
|
+
retval = true
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
retval
|
216
|
+
end
|
217
|
+
|
218
|
+
def clone_zone_version src, dest
|
219
|
+
src_list = gandi_api.domain.zone.record.list(get_zone_id, src)
|
220
|
+
print_debug src_list.inspect, 2
|
221
|
+
|
222
|
+
dest_list = gandi_api.domain.zone.record.list(get_zone_id, dest)
|
223
|
+
print_debug dest_list.inspect, 2
|
224
|
+
|
225
|
+
dest_list.each do |elt|
|
226
|
+
|
227
|
+
curs = src_list.select { |e| e.name == elt.name && e.type == elt.type }
|
228
|
+
|
229
|
+
if curs.size > 0
|
230
|
+
print_debug "\n#{curs.inspect}", 2
|
231
|
+
# normally with the same name and same type we should have only 1 record
|
232
|
+
cur = curs[0]
|
233
|
+
|
234
|
+
if cur.ttl != elt.ttl || cur.value != elt.value
|
235
|
+
print_debug "Updating record (name: #{elt.name} - type: #{elt.type}) to ttl: #{cur.ttl} - value: #{cur.value}", 1
|
236
|
+
res = gandi_api.domain.zone.record.update( get_zone_id, dest, { :id => elt.id }, {:name => cur.name, :type => cur.type, :value => cur.value, :ttl => cur.ttl} )
|
237
|
+
print_debug res.inspect, 2
|
238
|
+
else
|
239
|
+
print_debug "record (name: #{elt.name} - type: #{elt.type}) already up-to-date", 1
|
240
|
+
end
|
241
|
+
|
242
|
+
src_list = src_list - [cur]
|
243
|
+
|
244
|
+
else
|
245
|
+
# it does not exist in current zone, let's delete this record
|
246
|
+
print_debug "\nDeleting record (name: #{elt.name} - type: #{elt.type})", 1
|
247
|
+
res = gandi_api.domain.zone.record.delete( get_zone_id, dest, { :id => elt.id } )
|
248
|
+
|
249
|
+
if res == 1
|
250
|
+
print_debug "Deletion OK", 2
|
251
|
+
else
|
252
|
+
print_debug "ERROR: deletion of #{res} records", 2
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
src_list.each do |cur|
|
258
|
+
print_debug "Creating record (name: #{cur.name} - type: #{cur.type} - ttl: #{cur.ttl} - value: #{cur.value}", 1
|
259
|
+
res = gandi_api.domain.zone.record.add( get_zone_id, dest, {:name => cur.name, :type => cur.type, :value => cur.value, :ttl => cur.ttl} )
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def update_config_file ip
|
264
|
+
if File.open(@config_file, 'w+') { |f| f.write({:ipv4 => ip}.to_yaml) }
|
265
|
+
true
|
266
|
+
else
|
267
|
+
false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def get_zone_version
|
272
|
+
get_zone_id
|
273
|
+
infos = gandi_api.domain.zone.info(@zone_id)
|
274
|
+
return infos.version, infos.versions
|
275
|
+
end
|
276
|
+
|
277
|
+
def create_new_zone_version
|
278
|
+
gandi_api.domain.zone.version.new_version(@zone_id)
|
279
|
+
end
|
280
|
+
|
281
|
+
def get_zone_id
|
282
|
+
@zone_id ||= gandi_api.domain.info(@domain).zone_id
|
283
|
+
end
|
284
|
+
|
285
|
+
def gandi_api
|
286
|
+
@api ||= Gandi::Session.new(@api_key)
|
287
|
+
end
|
288
|
+
|
289
|
+
def print_info str
|
290
|
+
@terminal.say str
|
291
|
+
end
|
292
|
+
|
293
|
+
def print_debug str, level = 1
|
294
|
+
@terminal.say str if @debug and @debug >= level
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Ganddyn
|
4
|
+
|
5
|
+
class IpResolver
|
6
|
+
|
7
|
+
# urls: hash or ipv4 and ipv6 url services
|
8
|
+
# { :ipv4 => 'http://ipv4_url', :ipv6 => 'http://ipv6_url'}
|
9
|
+
def initialize urls = {}
|
10
|
+
raise ArgumentError, 'urls is not a Hash' unless urls.is_a? Hash
|
11
|
+
|
12
|
+
@ipv4_url = urls.has_key?(:ipv4) ? urls[:ipv4] : "http://v4.ipv6-test.com/api/myip.php"
|
13
|
+
@ipv6_url = urls.has_key?(:ipv6) ? urls[:ipv6] : "http://v6.ipv6-test.com/api/myip.php"
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_ipv4
|
17
|
+
get_url @ipv4_url
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_ipv6
|
21
|
+
get_url @ipv6_url
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def get_url( iUrl )
|
26
|
+
retval = nil
|
27
|
+
uri = URI(iUrl)
|
28
|
+
begin
|
29
|
+
res = Net::HTTP.get_response(uri)
|
30
|
+
if res.code == '200'
|
31
|
+
retval = res.body
|
32
|
+
end
|
33
|
+
rescue SocketError => e
|
34
|
+
# normally it happens if the server name is invalid
|
35
|
+
# or if no network is available
|
36
|
+
# let's return nil
|
37
|
+
rescue Exception => e
|
38
|
+
# normally it happen if the machine has no ipv6 support
|
39
|
+
# let's return nil
|
40
|
+
end
|
41
|
+
retval
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/ganddyn.rb
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
# Add the directory containing this file to the start of the load path if it isn't there already.
|
2
|
+
#$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'ganddyn/ipresolver'
|
5
|
+
require 'ganddyn/client'
|
@@ -0,0 +1,559 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ganddyn'
|
4
|
+
|
5
|
+
describe Ganddyn::Client do
|
6
|
+
before :each do
|
7
|
+
@input = StringIO.new
|
8
|
+
@output = StringIO.new
|
9
|
+
@terminal = HighLine.new(@input, @output)
|
10
|
+
|
11
|
+
@ipv4 = '12.34.56.78'
|
12
|
+
|
13
|
+
@cur_ver = 123
|
14
|
+
@other_ver = 456
|
15
|
+
@vers = [@cur_ver, @other_ver]
|
16
|
+
|
17
|
+
@mash = Hashie::Mash.new({:version => @cur_ver, :versions => [@cur_ver, @other_ver]})
|
18
|
+
@domain = 'domain.com'
|
19
|
+
@name = 'rspec'
|
20
|
+
|
21
|
+
@host = { :hostname => 'rspec.domain.com' }
|
22
|
+
@key = { :api_key => 'FAKEAPIKEY' }
|
23
|
+
@cfg = { :config_file => '/etc/ganddyn.yaml' }
|
24
|
+
@param = @host.merge(@key).merge(@cfg)
|
25
|
+
allow(YAML).to receive(:load_file).and_return({:ipv4 => @ipv4})
|
26
|
+
|
27
|
+
@api = double('api_gandi')
|
28
|
+
allow(@api).to receive(:domain).and_return(@api)
|
29
|
+
allow(@api).to receive(:zone).and_return(@api)
|
30
|
+
allow(@api).to receive(:info).and_return(@mash)
|
31
|
+
|
32
|
+
allow(@api).to receive(:version).and_return(@api)
|
33
|
+
allow(@api).to receive(:new_version).and_return(3)
|
34
|
+
|
35
|
+
allow(@api).to receive(:record).and_return(@api)
|
36
|
+
allow(@api).to receive(:add).and_return([])
|
37
|
+
allow(@api).to receive(:delete).and_return(1)
|
38
|
+
allow(@api).to receive(:update).and_return([])
|
39
|
+
allow(@api).to receive(:list).and_return([])
|
40
|
+
|
41
|
+
allow(@api).to receive(:version).and_return(@api)
|
42
|
+
allow(@api).to receive(:set).and_return(true)
|
43
|
+
|
44
|
+
allow_any_instance_of(Ganddyn::Client).to receive(:update_config_file).and_return(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#initialize' do
|
48
|
+
before :each do
|
49
|
+
end
|
50
|
+
it 'takes 1 parameters' do
|
51
|
+
Ganddyn::Client.new( @param ).should be_a Ganddyn::Client
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises an error if parameter opts is not a Hash' do
|
55
|
+
expect { Ganddyn::Client.new('rspec.domain.com') }.to raise_error(ArgumentError, /opts is not a Hash/)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'raises an error if parameter opts does not contain :hostname key' do
|
59
|
+
expect { Ganddyn::Client.new( {:host_name => 'rspec.domain.com'}.merge(@key).merge(@cfg)) }.to raise_error(ArgumentError, /opts does not contain key :hostname/)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'raises an error if parameter opts does not contain :api_key key' do
|
63
|
+
expect { Ganddyn::Client.new({:apikey => 'FAKEAPIKEY'}.merge(@host).merge(@cfg)) }.to raise_error(ArgumentError, /opts does not contain key :api_key/)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'raises an error if parameter opts does not contain :config_file key' do
|
67
|
+
expect { Ganddyn::Client.new({ :configfile => '/etc/ganddyn.yaml'}.merge(@host).merge(@key)) }.to raise_error(ArgumentError, /opts does not contain key :config_file/)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'loads the config file' do
|
71
|
+
expect(YAML).to receive(:load_file).with(@cfg[:config_file]).and_return({:ipv4 => @ipv4})
|
72
|
+
Ganddyn::Client.new( @param )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#update' do
|
77
|
+
before :each do
|
78
|
+
allow_any_instance_of(Ganddyn::IpResolver).to receive(:get_ipv4).and_return('11.22.33.44')
|
79
|
+
@gand = Ganddyn::Client.new( @param.merge({:terminal => @terminal}) )
|
80
|
+
allow(@gand).to receive(:gandi_api).and_return(@api)
|
81
|
+
allow(@gand).to receive(:system).and_return(true)
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when yaml config file is not found' do
|
85
|
+
before :each do
|
86
|
+
expect(YAML).to receive(:load_file).and_return(false)
|
87
|
+
@gand = Ganddyn::Client.new( @param.merge({:terminal => @terminal}) )
|
88
|
+
allow(@gand).to receive(:gandi_api).and_return(@api)
|
89
|
+
allow(@gand).to receive(:system).and_return(true)
|
90
|
+
allow(@gand).to receive(:get_record)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'asks gandi for ipv4' do
|
94
|
+
expect(@gand).to receive(:get_record).with('A').and_return(@ipv4)
|
95
|
+
expect(@gand.update).to be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when yaml config file exist' do
|
101
|
+
it 'does not ask Gandi' do
|
102
|
+
allow(@gand).to receive(:get_record)
|
103
|
+
expect(@gand.update).to be_true
|
104
|
+
expect(@gand).to_not have_received(:get_record)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when no network' do
|
109
|
+
before :each do
|
110
|
+
allow(@gand).to receive(:system).and_return(false)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'does not ask for IPv4 information' do
|
114
|
+
expect_any_instance_of(Ganddyn::IpResolver).to_not receive(:get_ipv4)
|
115
|
+
expect(@gand.update).to be_nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when no ip to update' do
|
120
|
+
before :each do
|
121
|
+
allow_any_instance_of(Ganddyn::IpResolver).to receive(:get_ipv4).and_return('')
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns true' do
|
125
|
+
expect(@gand.update).to be_true
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'does not modify the yaml config file' do
|
129
|
+
expect(@gand).to_not receive(:update_config_file)
|
130
|
+
@gand.update
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when ip to update' do
|
135
|
+
it 'updates the ip' do
|
136
|
+
expect(@gand.update).to be_true
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'updates the yaml config file content' do
|
140
|
+
expect(@gand).to receive(:update_config_file).and_return(true)
|
141
|
+
@gand.update
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
describe_internally Ganddyn::Client do
|
149
|
+
before :each do
|
150
|
+
@input = StringIO.new
|
151
|
+
@output = StringIO.new
|
152
|
+
@terminal = HighLine.new(@input, @output)
|
153
|
+
|
154
|
+
@ipv4 = '12.34.56.78'
|
155
|
+
|
156
|
+
@cur_ver = 123
|
157
|
+
@other_ver = 456
|
158
|
+
@vers = [@cur_ver, @other_ver]
|
159
|
+
|
160
|
+
@mash = Hashie::Mash.new({:version => @cur_ver, :versions => @vers})
|
161
|
+
@domain = 'domain.com'
|
162
|
+
@name = 'rspec'
|
163
|
+
|
164
|
+
@host = { :hostname => 'rspec.domain.com' }
|
165
|
+
@key = { :api_key => 'FAKEAPIKEY' }
|
166
|
+
@cfg = { :config_file => '/etc/ganddyn.yaml' }
|
167
|
+
@param = @host.merge(@key).merge(@cfg)
|
168
|
+
allow(YAML).to receive(:load_file).and_return({:ipv4 => @ipv4})
|
169
|
+
|
170
|
+
@api = double('api_gandi')
|
171
|
+
allow(@api).to receive(:domain).and_return(@api)
|
172
|
+
allow(@api).to receive(:zone).and_return(@api)
|
173
|
+
allow(@api).to receive(:info).and_return(@mash)
|
174
|
+
|
175
|
+
allow(@api).to receive(:version).and_return(@api)
|
176
|
+
allow(@api).to receive(:new_version).and_return(3)
|
177
|
+
|
178
|
+
allow(@api).to receive(:record).and_return(@api)
|
179
|
+
allow(@api).to receive(:add).and_return([])
|
180
|
+
allow(@api).to receive(:delete).and_return(1)
|
181
|
+
allow(@api).to receive(:update).and_return([])
|
182
|
+
allow(@api).to receive(:list).and_return([])
|
183
|
+
|
184
|
+
allow(@api).to receive(:version).and_return(@api)
|
185
|
+
allow(@api).to receive(:set).and_return(true)
|
186
|
+
|
187
|
+
@gand = Ganddyn::Client.new( @param.merge({:terminal => @terminal}) )
|
188
|
+
allow(@gand).to receive(:gandi_api).and_return(@api)
|
189
|
+
allow(@gand).to receive(:system).and_return(true)
|
190
|
+
|
191
|
+
allow_any_instance_of(Ganddyn::Client).to receive(:update_config_file).and_return(true)
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#get_zone_id' do
|
195
|
+
it 'returns the current zone id' do
|
196
|
+
@api.should_receive(:info).with(@domain).and_return(Hashie::Mash.new({:zone_id => 666}))
|
197
|
+
@gand.get_zone_id.should == 666
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'calls gandi api only once' do
|
201
|
+
@api.should_receive(:info).once.with(@domain).and_return(Hashie::Mash.new({:zone_id => 666}))
|
202
|
+
@gand.get_zone_id.should == 666
|
203
|
+
@gand.get_zone_id.should == 666
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#create_new_zone_version' do
|
208
|
+
pending 'not really necessary'
|
209
|
+
end
|
210
|
+
|
211
|
+
describe '#get_zone_version' do
|
212
|
+
it 'returns 2 values' do
|
213
|
+
version, versions = @gand.get_zone_version
|
214
|
+
version.should == @cur_ver
|
215
|
+
versions.should == @vers
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe '#get_record' do
|
220
|
+
before :each do
|
221
|
+
@type = 'A'
|
222
|
+
@filter = { :name => @name, :type => @type }
|
223
|
+
@res = Hashie::Mash.new({:value => @ipv4})
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'takes 1 parameter as argument' do
|
227
|
+
@api.should_receive(:list).with(anything(), 0, @filter).and_return([@res])
|
228
|
+
@gand.get_record('A')
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'when Gandi returns a record' do
|
232
|
+
before :each do
|
233
|
+
expect(@api).to receive(:list).with(anything(), 0, @filter).and_return([@res])
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'returns the IPv4 address' do
|
237
|
+
@gand.get_record('A').should == @ipv4
|
238
|
+
end
|
239
|
+
|
240
|
+
# it 'output record found with value' do
|
241
|
+
# @gand.get_record('A')
|
242
|
+
# expect(@output.string).to match /record found: '#{@name}' => #{@ipv4}/
|
243
|
+
# end
|
244
|
+
end
|
245
|
+
|
246
|
+
context 'when Gandi returns no record' do
|
247
|
+
before :each do
|
248
|
+
expect(@api).to receive(:list).with(anything(), 0, @filter).and_return([])
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'returns an empty string if Gandi returns no record' do
|
252
|
+
expect(@gand.get_record('A')).to eq ''
|
253
|
+
end
|
254
|
+
|
255
|
+
# it 'output "record not found"' do
|
256
|
+
# @gand.get_record('A')
|
257
|
+
# expect(@output.string).to match /record not found: '#{@name}'/
|
258
|
+
# end
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'raises an error if type is not A' do
|
262
|
+
expect { @gand.get_record('AA') }.to raise_error(ArgumentError, /type is not 'A'/)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe '#get_gandi_ipv4' do
|
267
|
+
before :each do
|
268
|
+
@type = 'A'
|
269
|
+
@filter = { :name => @name, :type => @type }
|
270
|
+
@res = Hashie::Mash.new({:value => @ipv4})
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'retrieve the record A from Gandi DNS' do
|
274
|
+
expect(@api).to receive(:list).with(anything(), 0, @filter).and_return([@res])
|
275
|
+
expect(@gand.get_gandi_ipv4).to eq @ipv4
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'returns nil if IPv4 network is not available' do
|
279
|
+
allow(@gand).to receive(:get_record).and_return('1.2.3.4')
|
280
|
+
expect(@gand).to receive(:ipv4_available?).and_return(false)
|
281
|
+
expect(@gand.get_gandi_ipv4).to be_nil
|
282
|
+
expect(@gand).to_not have_received(:get_record)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe '#update_record' do
|
287
|
+
before :each do
|
288
|
+
@type = 'A'
|
289
|
+
@rec = { :name => @name, :type => @type, :value => @ipv4, :ttl => Ganddyn::Client::TTL }
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'takes 3 arguments' do
|
294
|
+
expect(@gand.update_record( @cur_ver, @ipv4, 'A')).to be_true
|
295
|
+
end
|
296
|
+
|
297
|
+
context 'when input ip is empty' do
|
298
|
+
it 'returns false' do
|
299
|
+
expect(@gand.update_record( @cur_ver, '', 'A')).to be_false
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
context 'when record does not exist yet' do
|
304
|
+
it 'adds a new record' do
|
305
|
+
expect(@gand.update_record( @cur_ver, @ipv4, 'A')).to be_true
|
306
|
+
expect(@api).to have_received(:add).with(anything, @cur_ver, @rec )
|
307
|
+
expect(@api).to_not have_received(:update)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context 'when record already exist' do
|
312
|
+
before :each do
|
313
|
+
@filter = { :name => @name, :type => @type }
|
314
|
+
@res = Hashie::Mash.new({:value => @ipv4})
|
315
|
+
expect(@api).to receive(:list).with(anything(), @cur_ver, @filter).and_return([@res])
|
316
|
+
end
|
317
|
+
|
318
|
+
context 'and ip is different' do
|
319
|
+
it 'updates the record' do
|
320
|
+
@gand.update_record( @cur_ver, "#{@ipv4}1", 'A')
|
321
|
+
expect(@api).to_not have_received(:add)
|
322
|
+
expect(@api).to have_received(:update).once
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'returns true' do
|
326
|
+
expect(@gand.update_record( @cur_ver, "#{@ipv4}1", 'A')).to be_true
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context 'and ip is the same' do
|
331
|
+
it 'does not update the record' do
|
332
|
+
@gand.update_record( @cur_ver, @ipv4, 'A')
|
333
|
+
expect(@api).to_not have_received(:add)
|
334
|
+
expect(@api).to_not have_received(:update)
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'returns false' do
|
338
|
+
expect(@gand.update_record( @cur_ver, @ipv4, 'A')).to be_false
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
describe '#clone_zone_version' do
|
345
|
+
before :each do
|
346
|
+
@src = 111
|
347
|
+
@src_ret = [ 123, 456 ].map do |id|
|
348
|
+
Hashie::Mash.new({ :id => id,
|
349
|
+
:name => "name#{id}",
|
350
|
+
:type => "type#{id}",
|
351
|
+
:value => "value#{id}",
|
352
|
+
:ttl => "ttl#{id}" })
|
353
|
+
end
|
354
|
+
@dest = 222
|
355
|
+
@dest_ret = [ 123, 789 ].map do |id|
|
356
|
+
Hashie::Mash.new({ :id => id,
|
357
|
+
:name => "name#{id}",
|
358
|
+
:type => "type#{id}",
|
359
|
+
:value => "value#{id}-dest",
|
360
|
+
:ttl => "ttl#{id}-dest" })
|
361
|
+
end
|
362
|
+
allow(@api).to receive(:list).with(anything(), @src).and_return(@src_ret)
|
363
|
+
allow(@api).to receive(:list).with(anything(), @dest).and_return(@dest_ret)
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'get the information from source zone' do
|
367
|
+
expect(@api).to receive(:list).with(anything(), @src).and_return(@src_ret)
|
368
|
+
@gand.clone_zone_version @src, @dest
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'get the information from destination zone' do
|
372
|
+
expect(@api).to receive(:list).with(anything(), @dest).and_return(@dest_ret)
|
373
|
+
@gand.clone_zone_version @src, @dest
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'deletes the record existing in destination zone but not in source zone' do
|
377
|
+
expect(@api).to receive(:delete).with(anything(), @dest, {:id => 789})
|
378
|
+
@gand.clone_zone_version @src, @dest
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'update the record existing in both zone' do
|
382
|
+
ret = @src_ret[0].to_hash.inject({}) { |acc,(k,v)| acc[k.to_sym] = v if k != 'id'; acc }
|
383
|
+
expect(@api).to receive(:update).with(anything(), @dest, {:id => 123}, ret)
|
384
|
+
@gand.clone_zone_version @src, @dest
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'create the record existing only in source zone' do
|
388
|
+
ret = @src_ret[1].to_hash.inject({}) { |acc,(k,v)| acc[k.to_sym] = v if k != 'id'; acc }
|
389
|
+
expect(@api).to receive(:add).with(anything(), @dest, ret)
|
390
|
+
@gand.clone_zone_version @src, @dest
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
describe '#activate_updated_version' do
|
395
|
+
context 'when activation succeeded' do
|
396
|
+
it 'returns true' do
|
397
|
+
expect(@gand.activate_updated_version(2)).to be_true
|
398
|
+
end
|
399
|
+
|
400
|
+
# it 'output activation of zone version successful' do
|
401
|
+
# @gand.activate_updated_version(2)
|
402
|
+
# expect(@output.string).to match /activation of zone version successful/
|
403
|
+
# end
|
404
|
+
end
|
405
|
+
|
406
|
+
context 'when activation failed' do
|
407
|
+
before :each do
|
408
|
+
expect(@api).to receive(:set).with(anything(), 9).and_return(false)
|
409
|
+
end
|
410
|
+
it 'returns false' do
|
411
|
+
expect(@gand.activate_updated_version(9)).to be_false
|
412
|
+
end
|
413
|
+
|
414
|
+
# it 'output activation of zone version failed' do
|
415
|
+
# @gand.activate_updated_version(9)
|
416
|
+
# expect(@output.string).to match /activation of zone version failed/
|
417
|
+
# end
|
418
|
+
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
describe '#update_ipv4' do
|
423
|
+
# pending 'not necessary'
|
424
|
+
end
|
425
|
+
|
426
|
+
|
427
|
+
describe '#update_ips' do
|
428
|
+
it 'take 1 input as parameter' do
|
429
|
+
expect(@gand.update_ips('')).to be_true
|
430
|
+
end
|
431
|
+
|
432
|
+
context 'when input is not a String' do
|
433
|
+
it 'raise an error' do
|
434
|
+
expect{ @gand.update_ips(['1.2.3.4']) }.to raise_error(ArgumentError, /update is not a String/)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
context 'when no ip to update' do
|
439
|
+
it 'returns true without doing anything' do
|
440
|
+
allow(@gand).to receive(:get_zone_version)
|
441
|
+
allow(@gand).to receive(:create_new_zone_version)
|
442
|
+
allow(@gand).to receive(:clone_zone_version)
|
443
|
+
|
444
|
+
expect(@gand.update_ips('')).to be_true
|
445
|
+
|
446
|
+
expect(@gand).to_not have_received(:get_zone_version)
|
447
|
+
expect(@gand).to_not have_received(:create_new_zone_version)
|
448
|
+
expect(@gand).to_not have_received(:clone_zone_version)
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'output no update needed' do
|
452
|
+
@gand.update_ips('')
|
453
|
+
expect(@output.string).to match /no update needed/
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context 'when ipv4 to update' do
|
458
|
+
|
459
|
+
it 'updates ipv4 record' do
|
460
|
+
expect(@gand).to receive(:update_ipv4).and_return(true)
|
461
|
+
@gand.update_ips '1.2.3.4'
|
462
|
+
end
|
463
|
+
|
464
|
+
context 'when activation of new zone version is OK' do
|
465
|
+
it 'returns true' do
|
466
|
+
expect(@gand.update_ips('1.2.3.4')).to be_true
|
467
|
+
end
|
468
|
+
|
469
|
+
it 'output update done' do
|
470
|
+
@gand.update_ips('1.2.3.4')
|
471
|
+
expect(@output.string).to match /update done/
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
context 'when activation of new zone version is KO' do
|
476
|
+
before :each do
|
477
|
+
expect(@gand).to receive(:get_zone_version).and_return([666, [666]])
|
478
|
+
expect(@api).to receive(:set).with(anything(), 3).and_return(false)
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'returns false' do
|
482
|
+
expect(@gand.update_ips('1.2.3.4')).to be_false
|
483
|
+
end
|
484
|
+
|
485
|
+
it 'output update FAILED' do
|
486
|
+
@gand.update_ips('1.2.3.4')
|
487
|
+
expect(@output.string).to match /update FAILED/
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'activate the new zone version' do
|
492
|
+
expect(@gand).to receive(:activate_updated_version).and_return(true)
|
493
|
+
@gand.update_ips '1.2.3.4'
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context 'when only 1 zone version exists' do
|
498
|
+
it 'creates a new version for the zone from current active version' do
|
499
|
+
allow(@gand).to receive(:clone_zone_version)
|
500
|
+
mash = Hashie::Mash.new({:version => 1, :versions => [1]})
|
501
|
+
allow(@api).to receive(:info).and_return(mash)
|
502
|
+
expect(@api).to receive(:new_version).and_return(99)
|
503
|
+
|
504
|
+
@gand.update_ips('1.2.3.4')
|
505
|
+
|
506
|
+
expect(@gand).to_not receive(:clone_zone_version)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
context 'when more than 1 zone version exist' do
|
511
|
+
it 'clone the last non active version of the zone' do
|
512
|
+
expect(@gand).to receive(:clone_zone_version)
|
513
|
+
@gand.update_ips('1.2.3.4')
|
514
|
+
expect(@api).to_not receive(:new_version)
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
describe '#ipv4_available' do
|
520
|
+
context 'when network is not available' do
|
521
|
+
before :each do
|
522
|
+
allow(@gand).to receive(:system).and_return(false)
|
523
|
+
end
|
524
|
+
|
525
|
+
it 'returns false' do
|
526
|
+
expect(@gand.ipv4_available?).to be false
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'output IPv4 network not available' do
|
530
|
+
@gand.ipv4_available?
|
531
|
+
expect(@output.string).to match /IPv4 network not available/
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
context 'when network is available' do
|
536
|
+
it 'returns true' do
|
537
|
+
expect(@gand.ipv4_available?).to be true
|
538
|
+
end
|
539
|
+
|
540
|
+
it 'output IPv4 network available' do
|
541
|
+
@gand.ipv4_available?
|
542
|
+
expect(@output.string).to match /IPv4 network available/
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
context 'on windows platform' do
|
547
|
+
it 'redirect output to > NUL' do
|
548
|
+
pending
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
context 'on Linux platform' do
|
553
|
+
it 'redirect output to > /dev/null 2>&1' do
|
554
|
+
pending
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ganddyn'
|
4
|
+
|
5
|
+
describe Ganddyn::IpResolver do
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@ip = Ganddyn::IpResolver.new( { :ipv4 => 'http://v4.rspec.domain.com', :ipv6 => 'http://v6.rspec.domain.com' } )
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#intialize' do
|
12
|
+
it 'takes 0 parameter' do
|
13
|
+
Ganddyn::IpResolver.new.should be_a Ganddyn::IpResolver
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'take 1 parameter' do
|
17
|
+
Ganddyn::IpResolver.new({}).should be_a Ganddyn::IpResolver
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises an error if parameter is not Hash' do
|
21
|
+
expect { Ganddyn::IpResolver.new([]) }.to raise_error(ArgumentError, /urls is not a Hash/)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context '#get_ipv4' do
|
26
|
+
it 'returns the ipv4' do
|
27
|
+
stub_request(:get, 'v4.rspec.domain.com').to_return(:body => '1.2.3.4', :status => 200, :headers => { 'Content-Length' => 3 })
|
28
|
+
@ip.get_ipv4.should == '1.2.3.4'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns empty string if client has no ipv4' do
|
32
|
+
stub_request(:get, 'v4.rspec.domain.com').to_raise(Exception)
|
33
|
+
@ip.get_ipv4.should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns nil if server is not reachable' do
|
37
|
+
stub_request(:get, 'v4.rspec.domain.com').to_raise(SocketError)
|
38
|
+
@ip.get_ipv4.should be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns nil if url is incorrect' do
|
42
|
+
stub_request(:get, 'v4.rspec.domain.com').to_return(:body => '<html><head>
|
43
|
+
<title>404 Not Found</title>
|
44
|
+
</head><body>
|
45
|
+
<h1>Not Found</h1>
|
46
|
+
<p>The requested URL xxx was not found on this server.</p>
|
47
|
+
<hr>
|
48
|
+
</body></html>', :status => 404, :headers => { 'Content-Length' => 3 })
|
49
|
+
@ip.get_ipv4.should be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context '#get_ipv6' do
|
54
|
+
it 'returns the ipv6' do
|
55
|
+
stub_request(:get, 'v6.rspec.domain.com').to_return(:body => '3b12:d24:1e6a:8e81:b6a2:6f67:5627:bf44', :status => 200, :headers => { 'Content-Length' => 3 })
|
56
|
+
@ip.get_ipv6.should == '3b12:d24:1e6a:8e81:b6a2:6f67:5627:bf44'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns nil if client has no ipv6' do
|
60
|
+
stub_request(:get, 'v6.rspec.domain.com').to_raise(Exception)
|
61
|
+
@ip.get_ipv6.should be_nil
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns nil if server is not reachable' do
|
66
|
+
stub_request(:get, 'v6.rspec.domain.com').to_raise(SocketError)
|
67
|
+
@ip.get_ipv6.should be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns nil if url is incorrect' do
|
71
|
+
stub_request(:get, 'v6.rspec.domain.com').to_return(:body => '<html><head>
|
72
|
+
<title>404 Not Found</title>
|
73
|
+
</head><body>
|
74
|
+
<h1>Not Found</h1>
|
75
|
+
<p>The requested URL xxx was not found on this server.</p>
|
76
|
+
<hr>
|
77
|
+
</body></html>', :status => 404, :headers => { 'Content-Length' => 3 })
|
78
|
+
@ip.get_ipv6.should be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
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
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'webmock/rspec'
|
20
|
+
|
21
|
+
# enable to test private method of a class
|
22
|
+
def describe_internally *args, &block
|
23
|
+
example = describe *args, &block
|
24
|
+
cur_class = args[0]
|
25
|
+
if cur_class.is_a? Class
|
26
|
+
saved_private_instance_methods = cur_class.private_instance_methods
|
27
|
+
example.before do
|
28
|
+
cur_class.class_eval { public *saved_private_instance_methods }
|
29
|
+
end
|
30
|
+
example.after do
|
31
|
+
cur_class.class_eval { private *saved_private_instance_methods }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ganddyn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Guillaume Virlet
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 10.3.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 10.3.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.14.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.14.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.17.4
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.17.4
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: gandi
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.0.10
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.0.10
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: highline
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.6.21
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.6.21
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: certified
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.1.2
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.1.2
|
111
|
+
description: |-
|
112
|
+
This gem allows to update your GANDI DNS zone with the current external IPv4 of your machine.
|
113
|
+
It duplicate current zone information in the last inactive version of the zone (or a newly
|
114
|
+
created one if only one version exist). It updates the IPv4 for the name requested and activate
|
115
|
+
this version.
|
116
|
+
email:
|
117
|
+
- github@virlet.org
|
118
|
+
executables:
|
119
|
+
- ganddyn
|
120
|
+
extensions: []
|
121
|
+
extra_rdoc_files: []
|
122
|
+
files:
|
123
|
+
- .gitignore
|
124
|
+
- .rspec
|
125
|
+
- .travis.yml
|
126
|
+
- Gemfile
|
127
|
+
- LICENSE.txt
|
128
|
+
- README.md
|
129
|
+
- Rakefile
|
130
|
+
- bin/ganddyn
|
131
|
+
- ganddyn.gemspec
|
132
|
+
- lib/ganddyn.rb
|
133
|
+
- lib/ganddyn/client.rb
|
134
|
+
- lib/ganddyn/ipresolver.rb
|
135
|
+
- lib/ganddyn/version.rb
|
136
|
+
- spec/lib/ganddyn/client_spec.rb
|
137
|
+
- spec/lib/ganddyn/ipresolver_spec.rb
|
138
|
+
- spec/spec_helper.rb
|
139
|
+
homepage: https://github.com/doc75/ganddyn
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata: {}
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - '>='
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 2.1.9
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: Update GANDI DNS zone IPv4
|
163
|
+
test_files:
|
164
|
+
- spec/lib/ganddyn/client_spec.rb
|
165
|
+
- spec/lib/ganddyn/ipresolver_spec.rb
|
166
|
+
- spec/spec_helper.rb
|