ganddyn 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/doc75/ganddyn.svg?branch=master)](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
|