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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - 1.9.3
7
+ - 1.9.2
8
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ganddyn.gemspec
4
+ gemspec
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
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
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
@@ -0,0 +1,3 @@
1
+ module Ganddyn
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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