esgob 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c771929d5f34833c2a7b647e05c11686e0ecd9db
4
- data.tar.gz: c0c648e1341855edd956c83d29b373e107e8db8e
3
+ metadata.gz: f029cb699d12ea2f9ebc3d62538b301ac22c4ce2
4
+ data.tar.gz: 878e694007c2d41a71ed940fc5f7cf9215a4ad23
5
5
  SHA512:
6
- metadata.gz: 6650464bd90d58172139131df815d59f7b8601c9f75cd5a2b214ed9bded0b393e81ff4252e782f03ee10cfce2679a927bcfeba9dd655acb81740269c976a556b
7
- data.tar.gz: 35b9700a1ec0f86df74720e404ea5ebb5ee08e33ecb0a7885d1f8cb5874d369d68a7af7029dd3e8d4e263259af0b2941c900a862be3e4f2edb3e62b8f484c07e
6
+ metadata.gz: 48559b9d9ac0b9a5def0e60cd6ed4242d90e28a2ec617d71fb673c0fedadd4e6aee7a32a81c6db88d649d539a59df04ab8e83e8317c5bfe65f442a13929159f5
7
+ data.tar.gz: 5ff773471a48fc33e975ae314aec3519f9d7422b8526301bebf66f5328955f886bf9c9a8d0fb03838766d2321df230988108810aca80091a00a9d17079945202
data/.gitignore CHANGED
@@ -11,4 +11,5 @@
11
11
  *.so
12
12
  *.o
13
13
  *.a
14
+ .ruby-version
14
15
  mkmf.log
data/README.md CHANGED
@@ -52,12 +52,12 @@ Here is an example of what can be done in an IRB session:
52
52
 
53
53
  $ irb -resgob
54
54
  irb(main):001:0> esgob = Esgob::Client.new
55
- => #<Esgob::Client:0x007fd2e3b13420>
55
+ => #<Esgob::Client account=myacct>
56
56
  irb(main):002:0> esgob.domains_slaves_list
57
57
  => {"example.com"=>"192.168.0.1", "example.uk"=>"192.168.0.1"}
58
58
  irb(main):003:0> esgob.domains_slaves_list.keys
59
59
  => ["example.com", "example.uk"]
60
- irb(main):004:0>
60
+ irb(main):004:0>
61
61
 
62
62
  See the [API documentation] for full details.
63
63
 
data/bin/esgob ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require "rubygems"
6
+ require "esgob"
7
+
8
+ Esgob::CLI.start
data/esgob.gemspec CHANGED
@@ -4,35 +4,37 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'esgob/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "esgob"
7
+ spec.name = 'esgob'
8
8
  spec.version = Esgob::VERSION
9
- spec.authors = ["Nicholas Humfrey"]
10
- spec.email = ["njh@aelius.com"]
11
- spec.summary = %q{Client library for talking to the Esgob anycast DNS API.}
12
- #spec.description = %q{TODO: Write a longer description. Optional.}
13
- spec.homepage = ""
14
- spec.license = "MIT"
9
+ spec.authors = ['Nicholas Humfrey']
10
+ spec.email = ['njh@aelius.com']
11
+ spec.summary = 'Client library for talking to the Esgob anycast DNS API.'
12
+ # spec.description = 'TODO: Write a longer description. Optional.'
13
+ spec.homepage = 'http://github.com/njh/ruby-esgob'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
-
22
- spec.add_dependency "json", "~> 1.8"
21
+ spec.add_dependency 'json', '~> 1.8'
22
+ spec.add_dependency 'thor', '~> 0.19.1'
23
23
 
24
24
  if Gem.ruby_version > Gem::Version.new('1.9')
25
25
  spec.add_development_dependency 'bundler', '>= 1.5.0'
26
26
  spec.add_development_dependency 'rake', '>= 0.10.0'
27
27
  spec.add_development_dependency 'yard', '>= 0.8.0'
28
28
  spec.add_development_dependency 'fakeweb', '~> 1.3.0'
29
+ spec.add_development_dependency 'mocha', '~> 1.1.0'
29
30
  spec.add_development_dependency 'simplecov'
30
31
  elsif Gem.ruby_version > Gem::Version.new('1.8')
31
32
  spec.add_development_dependency 'bundler', '>= 1.1.0'
32
33
  spec.add_development_dependency 'rake', '~> 0.9.0'
33
34
  spec.add_development_dependency 'yard', '~> 0.8.0'
34
- spec.add_development_dependency 'minitest', '~> 5.5.0'
35
35
  spec.add_development_dependency 'fakeweb', '~> 1.3.0'
36
+ spec.add_development_dependency 'minitest', '~> 5.5.0'
37
+ spec.add_development_dependency 'mocha', '~> 1.1.0'
36
38
  else
37
39
  raise "#{Gem.ruby_version} is an unsupported version of ruby"
38
40
  end
data/lib/esgob/cli.rb ADDED
@@ -0,0 +1,134 @@
1
+ require "thor"
2
+
3
+ class Esgob::CLI < Thor
4
+ class_option :account,
5
+ :type => :string,
6
+ :aliases => '-a',
7
+ :banner => 'Account Name'
8
+
9
+ class_option :key,
10
+ :type => :string,
11
+ :aliases => '-k',
12
+ :banner => 'API Key'
13
+
14
+ class_option :verbose,
15
+ :type => :boolean,
16
+ :default => false,
17
+ :aliases => '-v'
18
+
19
+ def self.start(args = ARGV, config = {})
20
+ config[:shell] ||= Thor::Base.shell.new
21
+ begin
22
+ super(args, config)
23
+ rescue Esgob::ServerError => err
24
+ $stderr.puts config[:shell].set_color("=> Error: #{err.message} [#{err.code}]", :red, :bold)
25
+ end
26
+ end
27
+
28
+ desc "account", "Display account info"
29
+ def account
30
+ client.accounts_get.each_pair do |k, v|
31
+ say sprintf("%8s: %s\n", k, v)
32
+ end
33
+ end
34
+
35
+ desc "domains", "List all domains"
36
+ def domains
37
+ print_table(
38
+ [['Domain', 'Type']] +
39
+ [['------', '----']] +
40
+ client.domains_list.map { |h| [h[:domain], h[:type]] }
41
+ )
42
+ end
43
+
44
+ desc "slaves", "List slave domains"
45
+ def slaves
46
+ print_table(
47
+ [['Domain', 'Master IP']] +
48
+ [['------', '---------']] +
49
+ client.domains_slaves_list.to_a
50
+ )
51
+ end
52
+
53
+ desc "slaves-add DOMAIN MASTERIP", "Add new slave domain"
54
+ def slaves_add(domain, masterip)
55
+ check_action do
56
+ client.domains_slaves_add(domain, masterip)
57
+ end
58
+ end
59
+
60
+ desc "slaves-delete DOMAIN", "Delete a slave domain"
61
+ def slaves_delete(domain)
62
+ check_action do
63
+ client.domains_slaves_delete(domain)
64
+ end
65
+ end
66
+
67
+ desc "slaves-transfer DOMAIN",
68
+ "Force transfer from master of a slave domain"
69
+ def slaves_transfer(domain)
70
+ check_action do
71
+ client.domains_slaves_forcetransfer(domain)
72
+ end
73
+ end
74
+
75
+ desc "slaves-update DOMAIN MASTERIP",
76
+ "Updates the master IP of a slave domain"
77
+ def slaves_update(domain, masterip)
78
+ check_action do
79
+ client.domains_slaves_updatemasterip(domain, masterip)
80
+ end
81
+ end
82
+
83
+ desc "slaves-sync FILE MASTERIP",
84
+ "Synronises list of slave domains in a file"
85
+ def slaves_sync(filename, masterip)
86
+ domains = []
87
+ File.foreach(filename) do |line|
88
+ domains << line.strip.split(/\s+/).first
89
+ end
90
+
91
+ check_action do
92
+ client.domains_slaves_sync(domains, masterip)
93
+ end
94
+ end
95
+
96
+ desc "soacheck DOMAIN",
97
+ "Fetch domain SOA serial number for all nodes"
98
+ def soacheck(domain)
99
+ response = client.domains_tools_soacheck(domain)
100
+ print_table(
101
+ [['Identifier', 'Type', 'Country', 'SOA', 'Response']] +
102
+ [['----------', '----', '-------', '---', '--------']] +
103
+ response[:responses][:masters].map do |node|
104
+ [node[:ip], "master", '', node[:soa], node[:response]]
105
+ end +
106
+ response[:responses][:anycastnodes].map do |node|
107
+ [node[:ref], 'anycast', node[:country], node[:soa], node[:response]]
108
+ end
109
+ )
110
+ end
111
+
112
+ desc "version", "Show Esgob Ruby Client version"
113
+ def version
114
+ say "Esgob Ruby Client version #{Esgob::VERSION}"
115
+ end
116
+ map "--version" => "version"
117
+
118
+ private ######################################################################
119
+
120
+ def client
121
+ @client ||= Esgob::Client.new(options[:account], options[:key])
122
+ end
123
+
124
+ def check_action
125
+ results = yield
126
+ results = [results] unless results.is_a?(Array)
127
+ results.each do |result|
128
+ unless result[:action].nil?
129
+ say "#{result[:domain]} " + set_color("=> #{result[:action]}", :green, :bold)
130
+ end
131
+ end
132
+ end
133
+
134
+ end
data/lib/esgob/client.rb CHANGED
@@ -10,19 +10,27 @@ class Esgob::Client
10
10
  DEFAULT_API_ENDPOINT = "https://api.esgob.com/1.0/".freeze
11
11
 
12
12
  def initialize(*args)
13
- if args.first.kind_of?(Hash)
14
- args.first.each_pair { |k,v| send("#{k}=", v) }
13
+ if args.first.is_a?(Hash)
14
+ args.first.each_pair { |k, v| send("#{k}=", v) }
15
15
  else
16
16
  self.account = args[0]
17
17
  self.api_key = args[1]
18
18
  end
19
19
 
20
+ self.account ||= ENV['ESGOB_ACCOUNT']
21
+ self.api_key ||= ENV['ESGOB_API_KEY']
20
22
  self.endpoint ||= DEFAULT_API_ENDPOINT
21
- self.account ||= ENV['ESGOB_ACCOUNT']
22
- self.api_key ||= ENV['ESGOB_API_KEY']
23
+
24
+ if account.nil? or account.empty?
25
+ raise(ArgumentError, "No account name configured for Esgob")
26
+ end
27
+
28
+ if api_key.nil? or api_key.empty?
29
+ raise(ArgumentError, "No API key configured for Esgob")
30
+ end
23
31
  end
24
32
 
25
- def call(function_name, arguments={})
33
+ def call(function_name, arguments = {})
26
34
  uri = URI(endpoint + function_name)
27
35
  uri.query = build_query(default_arguments.merge(arguments))
28
36
 
@@ -32,22 +40,28 @@ class Esgob::Client
32
40
  http.request(req)
33
41
  end
34
42
 
35
- if res.code =~ /^2/
36
- if res.content_type == 'application/json'
37
- symbolize_keys! JSON.parse(res.body)
43
+ if res.content_type == 'application/json'
44
+ data = symbolize_keys! JSON.parse(res.body)
45
+ if data.key?(:error)
46
+ raise Esgob::ServerError.new(
47
+ data[:error][:message],
48
+ data[:error][:code].to_s
49
+ )
50
+ elsif res.code !~ /^2/
51
+ raise Esgob::ServerError.new(res.message, res.code)
38
52
  else
39
- raise "HTTP response from ESGOB is not of type JSON"
53
+ return data
40
54
  end
41
55
  else
42
- # The badly named method throws an Net::HTTP exception
43
- res.value
56
+ raise "HTTP response from ESGOB is not of type JSON"
44
57
  end
45
-
46
58
  end
47
59
 
48
60
  # Return account status; credit balance, etc
49
61
  def accounts_get
50
- call('accounts.get')
62
+ account = call('accounts.get')
63
+ account[:added] = Time.at(account[:added]) if account[:added].is_a?(Fixnum)
64
+ account
51
65
  end
52
66
 
53
67
  # Returns all hosted domains
@@ -56,7 +70,7 @@ class Esgob::Client
56
70
  end
57
71
 
58
72
  # Returns all hosted slave domains as a hash
59
- #
73
+ #
60
74
  def domains_slaves_list
61
75
  Hash[
62
76
  call('domains.slaves.list')[:domains].map do |item|
@@ -67,32 +81,44 @@ class Esgob::Client
67
81
 
68
82
  # Adds a new slave domain
69
83
  def domains_slaves_add(domain, masterip)
70
- call('domains.slaves.add', :domain => domain, :masterip => masterip)
84
+ result = call('domains.slaves.add', :domain => domain, :masterip => masterip)
85
+ result[:domain] ||= domain
86
+ result
71
87
  end
72
88
 
73
89
  # Deletes a slave domain
74
90
  def domains_slaves_delete(domain)
75
- call('domains.slaves.delete', :domain => domain)
91
+ result = call('domains.slaves.delete', :domain => domain)
92
+ result[:domain] ||= domain
93
+ result
76
94
  end
77
95
 
78
96
  # Force AXFR / transfer from master of a slave domain
79
97
  def domains_slaves_forcetransfer(domain)
80
- call('domains.slaves.forcetransfer', :domain => domain)
98
+ result = call('domains.slaves.forcetransfer', :domain => domain)
99
+ result[:domain] ||= domain
100
+ result
81
101
  end
82
102
 
83
103
  # Updates the master IP of a slave domain
84
104
  def domains_slaves_updatemasterip(domain, masterip)
85
- call('domains.slaves.updatemasterip', :domain => domain, :masterip => masterip)
105
+ result = call('domains.slaves.updatemasterip', :domain => domain, :masterip => masterip)
106
+ result[:domain] ||= domain
107
+ result
86
108
  end
87
109
 
88
110
  # Add a host allowed to AXFR out
89
111
  def domains_slaves_axfrout_add(domain, axfrip)
90
- call('domains.slaves.axfrout.add', :domain => domain, :axfrip => axfrip)
112
+ result = call('domains.slaves.axfrout.add', :domain => domain, :axfrip => axfrip)
113
+ result[:domain] ||= domain
114
+ result
91
115
  end
92
116
 
93
117
  # Account Delete a host allowed to AXFR out
94
118
  def domains_slaves_axfrout_delete(domain, axfrip)
95
- call('domains.slaves.axfrout.delete', :domain => domain, :axfrip => axfrip)
119
+ result = call('domains.slaves.axfrout.delete', :domain => domain, :axfrip => axfrip)
120
+ result[:domain] ||= domain
121
+ result
96
122
  end
97
123
 
98
124
  # Retrieve the domain SOA serial number from the master and each anycast node
@@ -100,6 +126,44 @@ class Esgob::Client
100
126
  call('domains.tools.soacheck', :domain => domain)
101
127
  end
102
128
 
129
+ # Given a list of domains and a master IP, add and delete domains
130
+ # so that the Esgob account matches the local list
131
+ def domains_slaves_sync(domains, masterip)
132
+ existing_domains = domains_slaves_list
133
+
134
+ # Add any missing domains
135
+ responses = []
136
+ domains.each do |domain|
137
+ unless existing_domains.include?(domain)
138
+ response = domains_slaves_add(domain, masterip)
139
+ response[:domain] ||= domain
140
+ responses << response
141
+ end
142
+ end
143
+
144
+ # Now check the existing domains
145
+ existing_domains.keys.sort.each do |domain|
146
+ if domains.include?(domain)
147
+ # Update the masterip if it isn't correct
148
+ if existing_domains[domain] != masterip
149
+ response = domains_slaves_updatemasterip(domain, masterip)
150
+ response[:domain] ||= domain
151
+ responses << response
152
+ end
153
+ else
154
+ # Delete domain; not on list
155
+ response = domains_slaves_delete(domain)
156
+ response[:domain] ||= domain
157
+ responses << response
158
+ end
159
+ end
160
+
161
+ responses
162
+ end
163
+
164
+ def inspect
165
+ "\#<#{self.class} account=#{@account}>"
166
+ end
103
167
 
104
168
  protected
105
169
 
@@ -111,10 +175,10 @@ class Esgob::Client
111
175
  when Hash
112
176
  symbolize_keys!(hash[ks])
113
177
  when Array
114
- hash[ks].each {|item| symbolize_keys!(item) if item.kind_of?(Hash)}
178
+ hash[ks].each { |item| symbolize_keys!(item) if item.is_a?(Hash) }
115
179
  end
116
180
  end
117
- return hash
181
+ hash
118
182
  end
119
183
 
120
184
  def default_arguments
@@ -126,8 +190,8 @@ class Esgob::Client
126
190
  end
127
191
 
128
192
  def build_query(hash)
129
- hash.keys.sort{|a,b| a.to_s <=> b.to_s}.map { |key|
130
- URI::escape(key.to_s) + '=' + URI::escape(hash[key].to_s)
131
- }.join('&')
193
+ hash.keys.sort { |a, b| a.to_s <=> b.to_s }.map do |key|
194
+ URI.escape(key.to_s) + '=' + URI.escape(hash[key].to_s)
195
+ end.join('&')
132
196
  end
133
197
  end
data/lib/esgob/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Esgob
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/esgob.rb CHANGED
@@ -1,4 +1,14 @@
1
1
  module Esgob
2
2
  autoload :Client, 'esgob/client'
3
- autoload :Version, 'esgob/version'
3
+ autoload :CLI, 'esgob/cli'
4
+ autoload :VERSION, 'esgob/version'
5
+
6
+ class ServerError < StandardError
7
+ attr_reader :code
8
+
9
+ def initialize(message, code = nil)
10
+ super message
11
+ @code = code
12
+ end
13
+ end
4
14
  end
@@ -0,0 +1,129 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'test_helper'
4
+ require 'esgob'
5
+
6
+ class TestCLI < MiniTest::Unit::TestCase
7
+ def setup
8
+ # Run before each test
9
+ FakeWeb.clean_registry
10
+
11
+ @client = Esgob::Client.new('acct', 'xxxx')
12
+ Esgob::Client.stubs(:new).returns(@client)
13
+
14
+ ENV['THOR_SHELL'] = 'Basic'
15
+ end
16
+
17
+ def teardown
18
+ # Reset environment variables after each test
19
+ ENV.delete('ESGOB_ACCOUNT')
20
+ ENV.delete('ESGOB_API_KEY')
21
+ end
22
+
23
+ def test_account
24
+ register_fixture('accounts.get')
25
+
26
+ output = capture(:stdout) { Esgob::CLI.start(%w(account)) }
27
+ assert_match " id: xyz\n", output
28
+ assert_match " name: Person Name\n", output
29
+ assert_match " credits: 48\n", output
30
+ end
31
+
32
+ def test_account_error
33
+ FakeWeb.register_uri(
34
+ :get, %r{^https?://api\.esgob\.com(:443)?/},
35
+ :status => ['401', 'UNAUTHORIZED'],
36
+ :content_type => 'application/json',
37
+ :body => read_fixture(:code_1003)
38
+ )
39
+
40
+ output = capture(:stderr) { Esgob::CLI.start(%w(account)) }
41
+ assert_equal "=> Error: Account and API key combination not valid [1003]\n", output
42
+ end
43
+
44
+ def test_domains
45
+ register_fixture('domains.list')
46
+
47
+ output = capture(:stdout) { Esgob::CLI.start(%w(domains)) }
48
+ assert_match "Domain Type\n", output
49
+ assert_match "------ ----\n", output
50
+ assert_match "example.com slave\n", output
51
+ assert_match "example.uk slave\n", output
52
+ end
53
+
54
+ def test_slaves
55
+ register_fixture('domains.slaves.list')
56
+
57
+ output = capture(:stdout) { Esgob::CLI.start(%w(slaves)) }
58
+ assert_match "Domain Master IP\n", output
59
+ assert_match "------ ---------\n", output
60
+ assert_match "example.com 195.177.253.166\n", output
61
+ assert_match "example.uk 195.177.253.166\n", output
62
+ end
63
+
64
+ def test_slaves_add
65
+ register_fixture('domains.slaves.add')
66
+
67
+ output = capture(:stdout) { Esgob::CLI.start(%w(slaves-add example.org 195.177.253.166)) }
68
+ assert_equal "example.org => domain added\n", output
69
+ end
70
+
71
+ def test_slaves_delete
72
+ register_fixture('domains.slaves.delete')
73
+
74
+ output = capture(:stdout) { Esgob::CLI.start(%w(slaves-delete example.org)) }
75
+ assert_equal "example.org => domain deleted\n", output
76
+ end
77
+
78
+ def test_slaves_delete_error
79
+ FakeWeb.register_uri(
80
+ :get, %r{^https?://api\.esgob\.com(:443)?/},
81
+ :status => ['403', 'FORBIDDEN'],
82
+ :content_type => 'application/json',
83
+ :body => read_fixture(:code_2007)
84
+ )
85
+
86
+ output = capture(:stderr) { Esgob::CLI.start(%w(slaves-delete example.com)) }
87
+ assert_equal "=> Error: Domain is not present in your account [2007]\n", output
88
+ end
89
+
90
+ def test_slaves_transfer
91
+ register_fixture('domains.slaves.forcetransfer')
92
+
93
+ output = capture(:stdout) { Esgob::CLI.start(%w(slaves-transfer example.org)) }
94
+ assert_equal "example.org => Domain AXFR requested from master\n", output
95
+ end
96
+
97
+ def test_slaves_update
98
+ register_fixture('domains.slaves.updatemasterip')
99
+
100
+ output = capture(:stdout) { Esgob::CLI.start(%w(slaves-update example.org 195.177.253.167)) }
101
+ assert_equal "example.org => domain master IP updated\n", output
102
+ end
103
+
104
+ def test_slaves_sync
105
+ @client.expects(:domains_slaves_list).with().returns('a.com' => '195.177.253.169', 'b.com' => '195.177.253.169')
106
+ @client.expects(:domains_slaves_delete).with('a.com').returns(:action => 'domain deleted')
107
+ @client.expects(:domains_slaves_add).with('c.com', '195.177.253.169').returns(:action => 'domain added')
108
+
109
+ output = capture(:stdout) { Esgob::CLI.start(['slaves-sync', fixture_path('sync-domain-list.txt'), '195.177.253.169']) }
110
+ assert_match "a.com => domain deleted\n", output
111
+ assert_match "c.com => domain added\n", output
112
+ end
113
+
114
+ def test_soacheck
115
+ register_fixture('domains.tools.soacheck')
116
+
117
+ output = capture(:stdout) { Esgob::CLI.start(%w(soacheck example.org)) }
118
+ assert_match "Identifier Type Country SOA Response\n", output
119
+ assert_match "---------- ---- ------- --- --------\n", output
120
+ assert_match "195.177.253.167 master fail\n", output
121
+ assert_match "4f31ad80 anycast gb fail\n", output
122
+ assert_match "fgej72a1 anycast us fail\n", output
123
+ end
124
+
125
+ def test_version
126
+ assert_match /Esgob Ruby Client version \d+.\d+\.\d+/,
127
+ capture(:stdout) { Esgob::CLI.start(%w(version)) }
128
+ end
129
+ end
@@ -44,6 +44,20 @@ class TestClient < MiniTest::Unit::TestCase
44
44
  assert_equal 'http://api.example.com/', client.endpoint
45
45
  end
46
46
 
47
+ def test_new_client_with_no_account
48
+ ENV.delete('ESGOB_ACCOUNT')
49
+ assert_raises(ArgumentError) do
50
+ Esgob::Client.new(nil, 'mykey')
51
+ end
52
+ end
53
+
54
+ def test_new_client_with_no_api_key
55
+ ENV.delete('ESGOB_API_KEY')
56
+ assert_raises(ArgumentError) do
57
+ Esgob::Client.new('acct', nil)
58
+ end
59
+ end
60
+
47
61
  def test_call_with_no_parameters
48
62
  register_fixture('accounts.get')
49
63
  response = @client.call('accounts.get')
@@ -53,7 +67,13 @@ class TestClient < MiniTest::Unit::TestCase
53
67
  FakeWeb.last_request.path
54
68
  )
55
69
  assert_equal(
56
- {:credits=>48, :users=>[], :added=>1422792434, :id=>"xyz", :name=>"Person Name"},
70
+ {
71
+ :credits => 48,
72
+ :users => [],
73
+ :added => 1422792434,
74
+ :id => 'xyz',
75
+ :name => 'Person Name'
76
+ },
57
77
  response
58
78
  )
59
79
  end
@@ -79,27 +99,38 @@ class TestClient < MiniTest::Unit::TestCase
79
99
  end
80
100
 
81
101
  def test_call_with_404_error
82
- assert_raises(Net::HTTPServerException) do
83
- FakeWeb.register_uri(
84
- :get, %r[^https?://api\.esgob\.com(:443)?/],
85
- :status => ["404", "Not Found"],
86
- :content_type => "application/json",
87
- :body => '{}'
88
- )
89
- response = @client.call('accounts.get')
90
- end
102
+ FakeWeb.register_uri(
103
+ :get, %r{^https?://api\.esgob\.com(:443)?/},
104
+ :status => ['404', 'Not Found'],
105
+ :content_type => 'application/json',
106
+ :body => '{}'
107
+ )
108
+ err = assert_raises(Esgob::ServerError) { @client.call('accounts.get') }
109
+ assert_equal 'Not Found', err.message
110
+ assert_equal '404', err.code
91
111
  end
92
112
 
93
113
  def test_call_with_non_json_reponse
94
- assert_raises(RuntimeError) do
95
- FakeWeb.register_uri(
96
- :get, %r[^https?://api\.esgob\.com(:443)?/],
97
- :status => ["200", "OK"],
98
- :content_type => "text/plain",
99
- :body => 'This is plain text'
100
- )
101
- response = @client.call('accounts.get')
102
- end
114
+ FakeWeb.register_uri(
115
+ :get, %r{^https?://api\.esgob\.com(:443)?/},
116
+ :status => ['200', 'OK'],
117
+ :content_type => 'text/plain',
118
+ :body => 'This is plain text'
119
+ )
120
+ err = assert_raises(RuntimeError) { @client.call('accounts.get') }
121
+ assert_equal 'HTTP response from ESGOB is not of type JSON', err.message
122
+ end
123
+
124
+ def test_call_domain_not_present
125
+ FakeWeb.register_uri(
126
+ :get, %r{^https?://api\.esgob\.com(:443)?/},
127
+ :status => ['403', 'FORBIDDEN'],
128
+ :content_type => 'application/json',
129
+ :body => read_fixture(:code_2007)
130
+ )
131
+ err = assert_raises(Esgob::ServerError) { @client.call('domains.slaves.delete', :domain => 'example.org') }
132
+ assert_equal 'Domain is not present in your account', err.message
133
+ assert_equal '2007', err.code
103
134
  end
104
135
 
105
136
  def test_accounts_get
@@ -111,7 +142,13 @@ class TestClient < MiniTest::Unit::TestCase
111
142
  FakeWeb.last_request.path
112
143
  )
113
144
  assert_equal(
114
- {:credits=>48, :users=>[], :added=>1422792434, :id=>"xyz", :name=>"Person Name"},
145
+ {
146
+ :credits => 48,
147
+ :users => [],
148
+ :added => Time.parse('2015-02-01 12:07:14 +0000'),
149
+ :id => 'xyz',
150
+ :name => 'Person Name'
151
+ },
115
152
  response
116
153
  )
117
154
  end
@@ -126,8 +163,8 @@ class TestClient < MiniTest::Unit::TestCase
126
163
  )
127
164
  assert_equal(
128
165
  [
129
- {:domain => "example.com", :type => "slave"},
130
- {:domain => "example.uk", :type => "slave"}
166
+ { :domain => 'example.com', :type => 'slave' },
167
+ { :domain => 'example.uk', :type => 'slave' }
131
168
  ],
132
169
  response
133
170
  )
@@ -142,7 +179,7 @@ class TestClient < MiniTest::Unit::TestCase
142
179
  FakeWeb.last_request.path
143
180
  )
144
181
  assert_equal(
145
- {"example.com"=>"195.177.253.166", "example.uk"=>"195.177.253.166"},
182
+ { 'example.com' => '195.177.253.166', 'example.uk' => '195.177.253.166' },
146
183
  response
147
184
  )
148
185
  end
@@ -155,7 +192,7 @@ class TestClient < MiniTest::Unit::TestCase
155
192
  '/1.0/domains.slaves.add?account=acct&domain=example.org&f=json&key=xxxx&masterip=195.177.253.166',
156
193
  FakeWeb.last_request.path
157
194
  )
158
- assert_equal({:action=>"domain added"}, response)
195
+ assert_equal({ :action => 'domain added', :domain => 'example.org' }, response)
159
196
  end
160
197
 
161
198
  def test_domains_slaves_delete
@@ -166,7 +203,7 @@ class TestClient < MiniTest::Unit::TestCase
166
203
  '/1.0/domains.slaves.delete?account=acct&domain=example.org&f=json&key=xxxx',
167
204
  FakeWeb.last_request.path
168
205
  )
169
- assert_equal({:action=>"domain deleted"}, response)
206
+ assert_equal({ :action => 'domain deleted', :domain => 'example.org' }, response)
170
207
  end
171
208
 
172
209
  def test_domains_slaves_forcetransfer
@@ -177,7 +214,7 @@ class TestClient < MiniTest::Unit::TestCase
177
214
  '/1.0/domains.slaves.forcetransfer?account=acct&domain=example.org&f=json&key=xxxx',
178
215
  FakeWeb.last_request.path
179
216
  )
180
- assert_equal({:action=>"Domain AXFR requested from master"}, response)
217
+ assert_equal({ :action => 'Domain AXFR requested from master', :domain => 'example.org' }, response)
181
218
  end
182
219
 
183
220
  def test_domains_slaves_updatemasterip
@@ -188,7 +225,7 @@ class TestClient < MiniTest::Unit::TestCase
188
225
  '/1.0/domains.slaves.updatemasterip?account=acct&domain=example.org&f=json&key=xxxx&masterip=195.177.253.167',
189
226
  FakeWeb.last_request.path
190
227
  )
191
- assert_equal({:action=>"domain master IP updated"}, response)
228
+ assert_equal({ :action => 'domain master IP updated', :domain => 'example.org' }, response)
192
229
  end
193
230
 
194
231
  def test_domains_slaves_axfrout_add
@@ -199,7 +236,7 @@ class TestClient < MiniTest::Unit::TestCase
199
236
  '/1.0/domains.slaves.axfrout.add?account=acct&axfrip=195.177.253.1&domain=example.org&f=json&key=xxxx',
200
237
  FakeWeb.last_request.path
201
238
  )
202
- assert_equal({:action=>"domain AXFR out IPs updated"}, response)
239
+ assert_equal({ :action => 'domain AXFR out IPs updated', :domain => 'example.org' }, response)
203
240
  end
204
241
 
205
242
  def test_domains_slaves_axfrout_delete
@@ -210,7 +247,7 @@ class TestClient < MiniTest::Unit::TestCase
210
247
  '/1.0/domains.slaves.axfrout.delete?account=acct&axfrip=195.177.253.1&domain=example.org&f=json&key=xxxx',
211
248
  FakeWeb.last_request.path
212
249
  )
213
- assert_equal({:action=>"domain AXFR out IPs updated"}, response)
250
+ assert_equal({ :action => 'domain AXFR out IPs updated', :domain => 'example.org' }, response)
214
251
  end
215
252
 
216
253
  def test_domains_tools_soacheck
@@ -225,4 +262,56 @@ class TestClient < MiniTest::Unit::TestCase
225
262
  refute_empty(response[:responses])
226
263
  end
227
264
 
265
+ def test_domains_slaves_sync_noop
266
+ @client.expects(:domains_slaves_list).with().returns(
267
+ 'a.com' => '195.177.253.1', 'b.com' => '195.177.253.1'
268
+ )
269
+
270
+ responses = @client.domains_slaves_sync(['a.com', 'b.com'], '195.177.253.1')
271
+ assert_equal [], responses
272
+ end
273
+
274
+ def test_domains_slaves_sync_add_only
275
+ @client.expects(:domains_slaves_list).with().returns({})
276
+ @client.expects(:domains_slaves_add).with('a.com', '195.177.253.1').returns(:action => 'domain added')
277
+ @client.expects(:domains_slaves_add).with('b.com', '195.177.253.1').returns(:action => 'domain added')
278
+
279
+ responses = @client.domains_slaves_sync(['a.com', 'b.com'], '195.177.253.1')
280
+ assert_equal [
281
+ { :action => 'domain added', :domain => 'a.com' },
282
+ { :action => 'domain added', :domain => 'b.com' }
283
+ ], responses
284
+ end
285
+
286
+ def test_domains_slaves_sync_add_and_delete
287
+ @client.expects(:domains_slaves_list).with().returns({'a.com' => '195.177.253.1'})
288
+ @client.expects(:domains_slaves_delete).with('a.com').returns({:action => 'domain deleted'})
289
+ @client.expects(:domains_slaves_add).with('b.com', '195.177.253.1').returns(:action => 'domain added')
290
+
291
+ responses = @client.domains_slaves_sync(['b.com'], '195.177.253.1')
292
+ assert_equal [
293
+ { :action => 'domain added', :domain => 'b.com' },
294
+ { :action => 'domain deleted', :domain => 'a.com' }
295
+ ], responses
296
+ end
297
+
298
+ def test_domains_slaves_sync_add_and_delete_and_change_masterip
299
+ @client.expects(:domains_slaves_list).with().returns(
300
+ 'a.com' => '195.177.253.1', 'c.com' => '127.0.0.1'
301
+ )
302
+ @client.expects(:domains_slaves_delete).with('a.com').returns(:action => 'domain deleted')
303
+ @client.expects(:domains_slaves_add).with('b.com', '195.177.253.1').returns(:action => 'domain added')
304
+ @client.expects(:domains_slaves_updatemasterip).with('c.com', '195.177.253.1').returns(:action => 'domain master IP updated')
305
+
306
+ responses = @client.domains_slaves_sync(['b.com', 'c.com'], '195.177.253.1')
307
+ assert_equal [
308
+ { :action => 'domain added', :domain => 'b.com' },
309
+ { :action => 'domain deleted', :domain => 'a.com' },
310
+ { :action => 'domain master IP updated', :domain => 'c.com' }
311
+ ], responses
312
+ end
313
+
314
+ def test_inspect
315
+ assert_match('#<Esgob::Client account=acct>', @client.inspect)
316
+ end
228
317
  end
@@ -4,11 +4,9 @@ require 'test_helper'
4
4
  require 'esgob'
5
5
 
6
6
  class TestVersion < MiniTest::Unit::TestCase
7
-
8
7
  def test_version_number_looks_sensible
9
8
  assert_equal 'constant', defined?(Esgob::VERSION)
10
9
  assert_kind_of String, Esgob::VERSION
11
10
  assert_match /^\d{1,2}\.\d{1,2}\.\d{1,2}$/, Esgob::VERSION
12
11
  end
13
-
14
12
  end
@@ -0,0 +1,6 @@
1
+ {
2
+ "error": {
3
+ "message": "Account and API key combination not valid",
4
+ "code": 1003
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "error": {
3
+ "message": "Domain is not present in your account",
4
+ "code": 2007
5
+ }
6
+ }
@@ -0,0 +1,2 @@
1
+ b.com
2
+ c.com
data/test/test_helper.rb CHANGED
@@ -4,6 +4,7 @@ require 'rubygems'
4
4
  require 'bundler'
5
5
  Bundler.require(:default, :development)
6
6
  require 'minitest/autorun'
7
+ require 'mocha/mini_test'
7
8
 
8
9
  unless RUBY_VERSION =~ /^1\.8/
9
10
  SimpleCov.start
@@ -11,18 +12,34 @@ end
11
12
 
12
13
  FakeWeb.allow_net_connect = false
13
14
 
15
+ def fixture_path(fixture_name)
16
+ fixture_name = fixture_name.to_s
17
+ fixture_name += '.json' unless fixture_name.match(/\.\w+$/)
18
+ File.join(File.dirname(__FILE__), 'fixtures', fixture_name)
19
+ end
20
+
21
+ def read_fixture(fixture_name)
22
+ File.read fixture_path(fixture_name)
23
+ end
14
24
 
15
- def register_fixture(api_call, fixture_name=nil)
25
+ def register_fixture(api_call, fixture_name = nil)
16
26
  if fixture_name.nil?
17
27
  fixture_name = api_call.gsub(/\W+/, '_')
18
28
  end
19
29
 
20
- fixture_file = File.join(File.dirname(__FILE__), 'fixtures', fixture_name + '.json')
21
-
22
30
  FakeWeb.register_uri(
23
31
  :get, %r[^https?://api\.esgob\.com(:443)?/1.0/#{api_call}],
24
32
  :status => ["200", "OK"],
25
33
  :content_type => "application/json",
26
- :body => File.read(fixture_file)
34
+ :body => read_fixture(fixture_name)
27
35
  )
28
36
  end
37
+
38
+ def capture(stream)
39
+ original = eval "$#{stream}"
40
+ eval "$#{stream} = StringIO.new"
41
+ yield
42
+ result = eval("$#{stream}").string
43
+ eval "$#{stream} = original"
44
+ result
45
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: esgob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicholas Humfrey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-12 00:00:00.000000000 Z
11
+ date: 2015-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.19.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.19.1
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
96
  version: 1.3.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: mocha
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.1.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.1.0
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: simplecov
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -97,7 +125,8 @@ dependencies:
97
125
  description:
98
126
  email:
99
127
  - njh@aelius.com
100
- executables: []
128
+ executables:
129
+ - esgob
101
130
  extensions: []
102
131
  extra_rdoc_files: []
103
132
  files:
@@ -107,13 +136,18 @@ files:
107
136
  - LICENSE.md
108
137
  - README.md
109
138
  - Rakefile
139
+ - bin/esgob
110
140
  - esgob.gemspec
111
141
  - lib/esgob.rb
142
+ - lib/esgob/cli.rb
112
143
  - lib/esgob/client.rb
113
144
  - lib/esgob/version.rb
145
+ - test/esgob_cli_test.rb
114
146
  - test/esgob_client_test.rb
115
147
  - test/esgob_version_test.rb
116
148
  - test/fixtures/accounts_get.json
149
+ - test/fixtures/code_1003.json
150
+ - test/fixtures/code_2007.json
117
151
  - test/fixtures/domains_list.json
118
152
  - test/fixtures/domains_slaves_add.json
119
153
  - test/fixtures/domains_slaves_axfrout_add.json
@@ -123,8 +157,9 @@ files:
123
157
  - test/fixtures/domains_slaves_list.json
124
158
  - test/fixtures/domains_slaves_updatemasterip.json
125
159
  - test/fixtures/domains_tools_soacheck.json
160
+ - test/fixtures/sync-domain-list.txt
126
161
  - test/test_helper.rb
127
- homepage: ''
162
+ homepage: http://github.com/njh/ruby-esgob
128
163
  licenses:
129
164
  - MIT
130
165
  metadata: {}
@@ -149,9 +184,12 @@ signing_key:
149
184
  specification_version: 4
150
185
  summary: Client library for talking to the Esgob anycast DNS API.
151
186
  test_files:
187
+ - test/esgob_cli_test.rb
152
188
  - test/esgob_client_test.rb
153
189
  - test/esgob_version_test.rb
154
190
  - test/fixtures/accounts_get.json
191
+ - test/fixtures/code_1003.json
192
+ - test/fixtures/code_2007.json
155
193
  - test/fixtures/domains_list.json
156
194
  - test/fixtures/domains_slaves_add.json
157
195
  - test/fixtures/domains_slaves_axfrout_add.json
@@ -161,5 +199,6 @@ test_files:
161
199
  - test/fixtures/domains_slaves_list.json
162
200
  - test/fixtures/domains_slaves_updatemasterip.json
163
201
  - test/fixtures/domains_tools_soacheck.json
202
+ - test/fixtures/sync-domain-list.txt
164
203
  - test/test_helper.rb
165
204
  has_rdoc: