smart_proxy_dns_powerdns 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +7 -48
- data/config/dns_powerdns.yml +2 -5
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_configuration.rb +7 -52
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb +76 -34
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb +2 -1
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb +1 -1
- data/test/unit/dns_powerdns_configuration_test.rb +3 -96
- data/test/unit/dns_powerdns_record_test.rb +44 -28
- data/test/unit/internal_api_test.rb +4 -4
- metadata +2 -42
- data/lib/smart_proxy_dns_powerdns/backend/dummy.rb +0 -24
- data/lib/smart_proxy_dns_powerdns/backend/mysql.rb +0 -58
- data/lib/smart_proxy_dns_powerdns/backend/postgresql.rb +0 -51
- data/lib/smart_proxy_dns_powerdns/backend/rest.rb +0 -97
- data/test/unit/dns_powerdns_record_dummy_test.rb +0 -26
- data/test/unit/dns_powerdns_record_mysql_test.rb +0 -114
- data/test/unit/dns_powerdns_record_postgresql_test.rb +0 -125
- data/test/unit/dns_powerdns_record_rest_test.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc2eb76a5e55de4041e57fd29bd4172673c8a643d89a56a0b264c31e4e245662
|
4
|
+
data.tar.gz: '08bcd7294a28f78ad53ff55f58dd4ab86556073125a5bbe7c9178680084a9393'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c8184da940ba90aed1a4d0c5680fdcc87a8daef6647b961f4c9f87540a95e5d3e66095534b8bcf96fc7cffae2099ce69106e781961de5a60b5a85d1f0741dda
|
7
|
+
data.tar.gz: 3ab24a84d7a51006275d8425609b0cb0c4ba82f011b54ad3c18f768e8b0425b01fce0afcf605f91d6cc206f5bdbbe2170ae4017ebd1815bc5538a8a741c06db0
|
data/README.md
CHANGED
@@ -19,6 +19,10 @@ When installing using "gem", make sure to install the bundle file:
|
|
19
19
|
|
20
20
|
## Upgrading
|
21
21
|
|
22
|
+
### 0.5.0
|
23
|
+
|
24
|
+
* The multiple backends have been dropped and only REST is still supported.
|
25
|
+
|
22
26
|
### 0.4.0
|
23
27
|
|
24
28
|
* The minimum Smart Proxy version is now 1.15
|
@@ -51,56 +55,11 @@ To use the REST backend, set the following parameters:
|
|
51
55
|
|
52
56
|
**Note** only API v1 from PowerDNS 4.x is supported. The v0 API from 3.x is unsupported.
|
53
57
|
|
54
|
-
###
|
55
|
-
|
56
|
-
Domains in PowerDNS need a rectify action after modification. In the past this was done using pdnsutil (which can still be set) but since PowerDNS 4.1.0 the API can do this automatically. The [domain metadata API-RECTIFY](https://doc.powerdns.com/authoritative/domainmetadata.html#metadata-api-rectify) needs to be set to `1`. When it's unset, the config variable [default-api-rectify](https://doc.powerdns.com/authoritative/settings.html#setting-default-api-rectify) will be used. PowerDNS 4.2.0 started to default to true. When this is used, the value for `:powerdns_pdnssec` in this plugin should be empty (default).
|
57
|
-
|
58
|
-
### MySQL
|
59
|
-
|
60
|
-
To use MySQL, set the following parameters:
|
61
|
-
|
62
|
-
:powerdns_backend: 'mysql'
|
63
|
-
:powerdns_mysql_hostname: 'localhost'
|
64
|
-
:powerdns_mysql_username: 'powerdns'
|
65
|
-
:powerdns_mysql_password: ''
|
66
|
-
:powerdns_mysql_database: 'powerdns'
|
67
|
-
|
68
|
-
**Note** use of this backend is deprecated. REST should be used.
|
69
|
-
|
70
|
-
### PostgreSQL
|
71
|
-
|
72
|
-
To use PostgreSQL, set the following parameters:
|
73
|
-
|
74
|
-
:powerdns_backend: 'postgresql'
|
75
|
-
:powerdns_postgresql_connection: 'host=localhost user=powerdns password=mypassword dbname=powerdns'
|
76
|
-
|
77
|
-
**Note** use of this backend is deprecated. REST should be used.
|
78
|
-
|
79
|
-
### DNSSEC with MySQL and PostgreSQL
|
80
|
-
|
81
|
-
In case you've enabled DNSSEC (as you should), all database backends require a rectify-zone after every zone change. The REST backend ignores this setting. The pdnssec command is configurable:
|
82
|
-
|
83
|
-
:powerdns_pdnssec: 'pdnsutil'
|
84
|
-
|
85
|
-
Or a more complex example:
|
86
|
-
|
87
|
-
:powerdns_pdnssec: 'sudo pdnsutil --config-name=myconfig'
|
88
|
-
|
89
|
-
Note that PowerDNS 3.x used `pdnssec` rather than `pdnsutil` which explains the naming of the option.
|
90
|
-
|
91
|
-
### SOA autoserial with MySQL and PostgreSQL
|
92
|
-
|
93
|
-
PowerDNS (>= 3.3) provides a feature called `autoserial` that takes care of managing the serial of `SOA` records.
|
94
|
-
|
95
|
-
There are many options available regarding how PowerDNS generates the serial and details can be found looking for the `SOA-EDIT` option in PowerDNS.
|
96
|
-
|
97
|
-
One option is to let the PowerDNS backend determine the `SOA` serial using the biggest `change_date` of the records associated with the DNS domain.
|
98
|
-
`smart_proxy_dns_powerdns` uses this approach and updates the `change_date` field of changed records, setting them to the current timestamp of the database server, represented as **the number of seconds since EPOCH**.
|
58
|
+
### Domain rectification
|
99
59
|
|
100
|
-
|
101
|
-
* when a record is deleted, the `change_date` of the `SOA` record for the domain is updated
|
60
|
+
Domains in PowerDNS need a rectify action after modification. In the past this was done using pdnsutil but since PowerDNS 4.1.0 the API can do this automatically. The [domain metadata API-RECTIFY](https://doc.powerdns.com/authoritative/domainmetadata.html#metadata-api-rectify) needs to be set to `1`. When it's unset, the config variable [default-api-rectify](https://doc.powerdns.com/authoritative/settings.html#setting-default-api-rectify) will be used. PowerDNS 4.2.0 started to default to true.
|
102
61
|
|
103
|
-
### Updating the SOA serial
|
62
|
+
### Updating the SOA serial
|
104
63
|
|
105
64
|
When using the REST backend, the `change_date` of records isn't modified by this plugin. To automatically increment the serial number of a zone, you can configure the [SOA-EDIT-API](https://doc.powerdns.com/authoritative/domainmetadata.html#soa-edit-api) zone metadata. For example:
|
106
65
|
|
data/config/dns_powerdns.yml
CHANGED
@@ -3,8 +3,5 @@
|
|
3
3
|
# Configuration file for 'dns_powerdns' dns provider
|
4
4
|
#
|
5
5
|
|
6
|
-
|
7
|
-
#:
|
8
|
-
#:powerdns_mysql_username:
|
9
|
-
#:powerdns_mysql_password:
|
10
|
-
#:powerdns_mysql_database:
|
6
|
+
#:powerdns_rest_url: "http://localhost:8081/api/v1/servers/localhost"
|
7
|
+
#:powerdns_rest_api_key: ""
|
@@ -6,59 +6,14 @@ module ::Proxy::Dns::Powerdns
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def load_dependency_injection_wirings(container_instance, settings)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
begin
|
16
|
-
require "smart_proxy_dns_powerdns/backend/#{backend}"
|
17
|
-
rescue LoadError, e
|
18
|
-
raise ::Proxy::Error::ConfigurationError.new("Failed to load backend #{backend}: #{e}")
|
19
|
-
end
|
20
|
-
|
21
|
-
case backend
|
22
|
-
when 'mysql'
|
23
|
-
container_instance.dependency :dns_provider, (lambda do
|
24
|
-
Proxy::Dns::Powerdns::Backend::Mysql.new(
|
25
|
-
settings[:dns_server],
|
26
|
-
settings[:dns_ttl],
|
27
|
-
settings[:powerdns_pdnssec],
|
28
|
-
settings[:powerdns_mysql_hostname] || 'localhost',
|
29
|
-
settings[:powerdns_mysql_username] || 'powerdns',
|
30
|
-
settings[:powerdns_mysql_password] || '',
|
31
|
-
settings[:powerdns_mysql_database] || 'pdns',
|
32
|
-
)
|
33
|
-
end)
|
34
|
-
when 'postgresql'
|
35
|
-
container_instance.dependency :dns_provider, (lambda do
|
36
|
-
Proxy::Dns::Powerdns::Backend::Postgresql.new(
|
37
|
-
settings[:dns_server],
|
38
|
-
settings[:dns_ttl],
|
39
|
-
settings[:powerdns_pdnssec],
|
40
|
-
settings[:powerdns_postgresql_connection] || 'dbname=pdns',
|
9
|
+
container_instance.dependency :dns_provider, (lambda do
|
10
|
+
Proxy::Dns::Powerdns::Record.new(
|
11
|
+
settings[:dns_server],
|
12
|
+
settings[:dns_ttl],
|
13
|
+
settings[:powerdns_rest_url],
|
14
|
+
settings[:powerdns_rest_api_key],
|
41
15
|
)
|
42
|
-
|
43
|
-
when 'rest'
|
44
|
-
raise ::Proxy::Error::ConfigurationError.new("Setting powerdns_rest_api_key must be non-empty") unless settings[:powerdns_rest_api_key]
|
45
|
-
|
46
|
-
container_instance.dependency :dns_provider, (lambda do
|
47
|
-
Proxy::Dns::Powerdns::Backend::Rest.new(
|
48
|
-
settings[:dns_server],
|
49
|
-
settings[:dns_ttl],
|
50
|
-
settings[:powerdns_rest_url] || 'http://localhost:8081/api/v1/servers/localhost',
|
51
|
-
settings[:powerdns_rest_api_key],
|
52
|
-
)
|
53
|
-
end)
|
54
|
-
when 'dummy'
|
55
|
-
container_instance.dependency :dns_provider, (lambda do
|
56
|
-
Proxy::Dns::Powerdns::Backend::Dummy.new(
|
57
|
-
settings[:dns_server],
|
58
|
-
settings[:dns_ttl],
|
59
|
-
)
|
60
|
-
end)
|
61
|
-
end
|
16
|
+
end)
|
62
17
|
end
|
63
18
|
end
|
64
19
|
end
|
@@ -6,18 +6,18 @@ module Proxy::Dns::Powerdns
|
|
6
6
|
include Proxy::Log
|
7
7
|
include Proxy::Util
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :url, :api_key
|
10
|
+
|
11
|
+
def initialize(a_server, a_ttl, url, api_key)
|
12
|
+
@url = url
|
13
|
+
@api_key = api_key
|
10
14
|
|
11
|
-
def initialize(a_server, a_ttl, pdnssec = nil)
|
12
|
-
@pdnssec = pdnssec
|
13
15
|
super(a_server, a_ttl)
|
14
16
|
end
|
15
17
|
|
16
18
|
def do_create(name, value, type)
|
17
19
|
zone = get_zone(name)
|
18
|
-
|
19
|
-
raise Proxy::Dns::Error.new("Failed to rectify zone #{zone['name']}") unless rectify_zone(zone['name'])
|
20
|
-
else
|
20
|
+
unless create_record(zone['id'], name, type, value)
|
21
21
|
raise Proxy::Dns::Error.new("Failed to insert record #{name} #{type} #{value}")
|
22
22
|
end
|
23
23
|
true
|
@@ -25,43 +25,85 @@ module Proxy::Dns::Powerdns
|
|
25
25
|
|
26
26
|
def do_remove(name, type)
|
27
27
|
zone = get_zone(name)
|
28
|
-
|
29
|
-
raise Proxy::Dns::Error.new("Failed to rectify zone #{name}") unless rectify_zone(zone['name'])
|
30
|
-
end
|
31
|
-
true
|
28
|
+
delete_record(zone['id'], name, type)
|
32
29
|
end
|
33
30
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
def get_zone name
|
32
|
+
fqdn = Resolv::DNS::Name.create(name)
|
33
|
+
fqdn = Resolv::DNS::Name.create(name + '.') unless fqdn.absolute?
|
34
|
+
uri = URI("#{@url}/zones")
|
35
|
+
|
36
|
+
result = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
37
|
+
request = Net::HTTP::Get.new uri
|
38
|
+
request['X-API-Key'] = @api_key
|
39
|
+
response = http.request request
|
40
|
+
zones = JSON.parse(response.body) rescue []
|
41
|
+
|
42
|
+
zones.select { |zone|
|
43
|
+
domain = Resolv::DNS::Name.create(zone['name'])
|
44
|
+
domain == fqdn or fqdn.subdomain_of?(domain)
|
45
|
+
}.max_by { |zone| zone['name'].length }
|
46
|
+
end
|
47
|
+
|
48
|
+
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless result
|
49
|
+
|
50
|
+
result
|
38
51
|
end
|
39
52
|
|
40
|
-
def create_record
|
41
|
-
|
42
|
-
|
53
|
+
def create_record domain_id, name, type, content
|
54
|
+
content += '.' if ['PTR', 'CNAME'].include?(type)
|
55
|
+
rrset = {
|
56
|
+
:name => name + '.',
|
57
|
+
:type => type,
|
58
|
+
:ttl => @ttl.to_i,
|
59
|
+
:changetype => :REPLACE,
|
60
|
+
:records => [
|
61
|
+
{
|
62
|
+
:content => content,
|
63
|
+
:disabled => false
|
64
|
+
}
|
65
|
+
]
|
66
|
+
}
|
67
|
+
|
68
|
+
patch_records domain_id, rrset
|
43
69
|
end
|
44
70
|
|
45
|
-
def delete_record
|
46
|
-
|
47
|
-
|
71
|
+
def delete_record domain_id, name, type
|
72
|
+
rrset = {
|
73
|
+
:name => name + '.',
|
74
|
+
:type => type,
|
75
|
+
:changetype => :DELETE,
|
76
|
+
:records => []
|
77
|
+
}
|
78
|
+
|
79
|
+
patch_records domain_id, rrset
|
48
80
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def patch_records domain_id, rrset
|
85
|
+
uri = URI("#{@url}/zones/#{domain_id}")
|
86
|
+
|
87
|
+
data = { :rrsets => [rrset] }
|
88
|
+
|
89
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
90
|
+
request = Net::HTTP::Patch.new uri
|
91
|
+
request['X-API-Key'] = @api_key
|
92
|
+
request['Content-Type'] = 'application/json'
|
93
|
+
request.body = data.to_json
|
94
|
+
response = http.request request
|
95
|
+
unless response.is_a?(Net::HTTPSuccess)
|
96
|
+
begin
|
97
|
+
content = JSON.parse(response.body)
|
98
|
+
rescue
|
99
|
+
logger.debug "Failed to pach records for #{domain_id} with '#{rrset}': #{response.body}"
|
100
|
+
raise Proxy::Dns::Error.new("Failed to patch records")
|
101
|
+
end
|
102
|
+
raise Proxy::Dns::Error.new("Failed to patch records: #{content['error']}")
|
61
103
|
end
|
62
|
-
else
|
63
|
-
true
|
64
104
|
end
|
105
|
+
|
106
|
+
true
|
65
107
|
end
|
66
108
|
end
|
67
109
|
end
|
@@ -7,7 +7,8 @@ module Proxy::Dns::Powerdns
|
|
7
7
|
|
8
8
|
requires :dns, '>= 1.15'
|
9
9
|
|
10
|
-
validate_presence :
|
10
|
+
validate_presence :powerdns_rest_api_key
|
11
|
+
default_settings :powerdns_rest_url => 'http://localhost:8081/api/v1/servers/localhost'
|
11
12
|
|
12
13
|
load_classes ::Proxy::Dns::Powerdns::PluginConfiguration
|
13
14
|
load_dependency_injection_wirings ::Proxy::Dns::Powerdns::PluginConfiguration
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'webmock/test_unit'
|
2
3
|
|
3
4
|
require 'smart_proxy_dns_powerdns/dns_powerdns_configuration'
|
4
5
|
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
@@ -10,110 +11,16 @@ class DnsPowerdnsProductionWiringTest < Test::Unit::TestCase
|
|
10
11
|
@config = ::Proxy::Dns::Powerdns::PluginConfiguration.new
|
11
12
|
end
|
12
13
|
|
13
|
-
def test_dns_provider_initialization_mysql_backend_minimal_settings
|
14
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
15
|
-
:powerdns_backend => 'mysql')
|
16
|
-
|
17
|
-
provider = @container.get_dependency(:dns_provider)
|
18
|
-
|
19
|
-
assert_not_nil provider
|
20
|
-
assert_equal 999, provider.ttl
|
21
|
-
assert_equal 'localhost', provider.hostname
|
22
|
-
assert_equal 'powerdns', provider.username
|
23
|
-
assert_equal '', provider.password
|
24
|
-
assert_equal 'pdns', provider.database
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_dns_provider_initialization_mysql_backend_full_settings
|
28
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
29
|
-
:powerdns_backend => 'mysql',
|
30
|
-
:powerdns_mysql_hostname => 'db.example.com',
|
31
|
-
:powerdns_mysql_username => 'user',
|
32
|
-
:powerdns_mysql_password => 'super secret',
|
33
|
-
:powerdns_mysql_database => 'the_db',
|
34
|
-
)
|
35
|
-
|
36
|
-
provider = @container.get_dependency(:dns_provider)
|
37
|
-
|
38
|
-
assert_not_nil provider
|
39
|
-
assert_equal 999, provider.ttl
|
40
|
-
assert_equal 'db.example.com', provider.hostname
|
41
|
-
assert_equal 'user', provider.username
|
42
|
-
assert_equal 'super secret', provider.password
|
43
|
-
assert_equal 'the_db', provider.database
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_dns_provider_initialization_postgresql_backend_minimal_settings
|
47
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
48
|
-
:powerdns_backend => 'postgresql')
|
49
|
-
|
50
|
-
provider = @container.get_dependency(:dns_provider)
|
51
|
-
|
52
|
-
assert_not_nil provider
|
53
|
-
assert_equal 999, provider.ttl
|
54
|
-
assert_equal 'dbname=pdns', provider.connection_str
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_dns_provider_initialization_postgresql_backend_full_settings
|
58
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
59
|
-
:powerdns_backend => 'postgresql',
|
60
|
-
:powerdns_postgresql_connection => 'dbname=powerdns')
|
61
|
-
|
62
|
-
provider = @container.get_dependency(:dns_provider)
|
63
|
-
|
64
|
-
assert_not_nil provider
|
65
|
-
assert_equal 999, provider.ttl
|
66
|
-
assert_equal 'dbname=powerdns', provider.connection_str
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_dns_provider_initialization_rest_backend_invalid_settings
|
70
|
-
assert_raise Proxy::Error::ConfigurationError do
|
71
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
72
|
-
:powerdns_backend => 'rest')
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def test_dns_provider_initialization_rest_backend_minimal_settings
|
77
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
78
|
-
:powerdns_backend => 'rest',
|
79
|
-
:powerdns_rest_api_key => 'apikey')
|
80
|
-
|
81
|
-
provider = @container.get_dependency(:dns_provider)
|
82
|
-
|
83
|
-
assert_not_nil provider
|
84
|
-
assert_equal 999, provider.ttl
|
85
|
-
assert_equal 'http://localhost:8081/api/v1/servers/localhost', provider.url
|
86
|
-
assert_equal 'apikey', provider.api_key
|
87
|
-
end
|
88
|
-
|
89
14
|
def test_dns_provider_initialization_rest_backend_full_settings
|
90
15
|
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
91
|
-
:
|
92
|
-
:powerdns_rest_url => 'http://apiserver',
|
16
|
+
:powerdns_rest_url => 'http://apiserver.example.com',
|
93
17
|
:powerdns_rest_api_key => 'apikey')
|
94
18
|
|
95
19
|
provider = @container.get_dependency(:dns_provider)
|
96
20
|
|
97
21
|
assert_not_nil provider
|
98
22
|
assert_equal 999, provider.ttl
|
99
|
-
assert_equal 'http://apiserver', provider.url
|
23
|
+
assert_equal 'http://apiserver.example.com', provider.url
|
100
24
|
assert_equal 'apikey', provider.api_key
|
101
25
|
end
|
102
|
-
|
103
|
-
def test_dns_provider_initialization_dummy_backend
|
104
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
105
|
-
:powerdns_backend => 'dummy')
|
106
|
-
|
107
|
-
provider = @container.get_dependency(:dns_provider)
|
108
|
-
|
109
|
-
assert_not_nil provider
|
110
|
-
assert_equal 999, provider.ttl
|
111
|
-
end
|
112
|
-
|
113
|
-
def test_dns_provider_initialization_invalid_backend
|
114
|
-
assert_raise Proxy::Error::ConfigurationError do
|
115
|
-
@config.load_dependency_injection_wirings(@container, :dns_ttl => 999,
|
116
|
-
:powerdns_backend => 'invalid')
|
117
|
-
end
|
118
|
-
end
|
119
26
|
end
|
@@ -4,18 +4,20 @@ require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
|
4
4
|
|
5
5
|
class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
@provider = Proxy::Dns::Powerdns::Record.new('localhost', 86400,
|
7
|
+
@provider = Proxy::Dns::Powerdns::Record.new('localhost', 86400,
|
8
|
+
'http://localhost:8081/api/v1/servers/localhost',
|
9
|
+
'apikey')
|
8
10
|
end
|
9
11
|
|
10
12
|
def test_initialize
|
11
13
|
assert_equal 86400, @provider.ttl
|
12
|
-
assert_equal '
|
14
|
+
assert_equal 'http://localhost:8081/api/v1/servers/localhost', @provider.url
|
15
|
+
assert_equal 'apikey', @provider.api_key
|
13
16
|
end
|
14
17
|
|
15
18
|
def test_do_create_success
|
16
19
|
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
17
20
|
@provider.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
18
|
-
@provider.expects(:rectify_zone).with('example.com').returns(true)
|
19
21
|
|
20
22
|
assert @provider.do_create('test.example.com', '10.1.1.1', 'A')
|
21
23
|
end
|
@@ -29,44 +31,58 @@ class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
def test_do_create_failure_in_rectify
|
33
|
-
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
34
|
-
@provider.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
35
|
-
@provider.expects(:rectify_zone).with('example.com').returns(false)
|
36
|
-
|
37
|
-
assert_raise(Proxy::Dns::Error) do
|
38
|
-
@provider.do_create('test.example.com', '10.1.1.1', 'A')
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
34
|
def test_do_remove
|
43
35
|
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
44
36
|
@provider.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
45
|
-
@provider.expects(:rectify_zone).with('example.com').returns(true)
|
46
37
|
|
47
38
|
assert @provider.do_remove('test.example.com', 'A')
|
48
39
|
end
|
49
40
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
41
|
+
def test_get_zone_with_existing_zone
|
42
|
+
stub_request(:get, "http://localhost:8081/api/v1/servers/localhost/zones").
|
43
|
+
with(:headers => {'X-Api-Key' => 'apikey'}).
|
44
|
+
to_return(:body => '[{"id": "example.com.", "name": "example.com."}]')
|
45
|
+
assert_equal @provider.get_zone('test.example.com'), {'id' => 'example.com.', 'name' => 'example.com.'}
|
54
46
|
end
|
55
47
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@provider.
|
48
|
+
def test_get_zone_with_existing_zone_absolute_record
|
49
|
+
stub_request(:get, "http://localhost:8081/api/v1/servers/localhost/zones").
|
50
|
+
with(:headers => {'X-Api-Key' => 'apikey'}).
|
51
|
+
to_return(:body => '[{"id": "example.com.", "name": "example.com."}]')
|
52
|
+
assert_equal @provider.get_zone('test.example.com.'), {'id' => 'example.com.', 'name' => 'example.com.'}
|
53
|
+
end
|
61
54
|
|
62
|
-
|
55
|
+
def test_get_zone_without_existing_zone
|
56
|
+
stub_request(:get, "http://localhost:8081/api/v1/servers/localhost/zones").
|
57
|
+
with(:headers => {'X-Api-Key' => 'apikey'}).
|
58
|
+
to_return(:body => '[]')
|
59
|
+
assert_raise(Proxy::Dns::Error) { @provider.get_zone('test.example.com') }
|
63
60
|
end
|
64
61
|
|
65
|
-
def
|
66
|
-
|
62
|
+
def test_create_a_record
|
63
|
+
stub_request(:patch, "http://localhost:8081/api/v1/servers/localhost/zones/example.com.").
|
64
|
+
with(
|
65
|
+
:headers => {'X-Api-Key' => 'apikey', 'Content-Type' => 'application/json'},
|
66
|
+
:body => '{"rrsets":[{"name":"test.example.com.","type":"A","ttl":86400,"changetype":"REPLACE","records":[{"content":"10.1.1.1","disabled":false}]}]}'
|
67
|
+
)
|
68
|
+
assert @provider.create_record('example.com.', 'test.example.com', 'A', '10.1.1.1')
|
69
|
+
end
|
67
70
|
|
68
|
-
|
71
|
+
def test_create_ptr_record
|
72
|
+
stub_request(:patch, "http://localhost:8081/api/v1/servers/localhost/zones/example.com.").
|
73
|
+
with(
|
74
|
+
:headers => {'X-Api-Key' => 'apikey', 'Content-Type' => 'application/json'},
|
75
|
+
:body => '{"rrsets":[{"name":"1.1.1.10.in-addr.arpa.","type":"PTR","ttl":86400,"changetype":"REPLACE","records":[{"content":"test.example.com.","disabled":false}]}]}'
|
76
|
+
)
|
77
|
+
assert @provider.create_record('example.com.', '1.1.1.10.in-addr.arpa', 'PTR', 'test.example.com')
|
78
|
+
end
|
69
79
|
|
70
|
-
|
80
|
+
def test_delete_record
|
81
|
+
stub_request(:patch, "http://localhost:8081/api/v1/servers/localhost/zones/example.com.").
|
82
|
+
with(
|
83
|
+
:headers => {'X-Api-Key' => 'apikey', 'Content-Type' => 'application/json'},
|
84
|
+
:body => '{"rrsets":[{"name":"test.example.com.","type":"A","changetype":"DELETE","records":[]}]}'
|
85
|
+
)
|
86
|
+
assert @provider.delete_record('example.com.', 'test.example.com', 'A')
|
71
87
|
end
|
72
88
|
end
|
@@ -26,11 +26,11 @@ class InternalApiTest < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def setup
|
29
|
-
@server = Proxy::Dns::Powerdns::Record.new('localhost', 3600)
|
29
|
+
@server = Proxy::Dns::Powerdns::Record.new('localhost', 3600, 'http://powerdns.example.com:8081/', 'apikey')
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_create_a_record
|
33
|
-
name = "
|
33
|
+
name = "sub.example.com"
|
34
34
|
value = "192.168.33.33"
|
35
35
|
type = "A"
|
36
36
|
@server.expects(:do_create).with(name, value, type)
|
@@ -39,7 +39,7 @@ class InternalApiTest < Test::Unit::TestCase
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_create_ptr_record
|
42
|
-
name = "
|
42
|
+
name = "sub.example.com"
|
43
43
|
value = "33.33.168.192.in-addr.arpa"
|
44
44
|
type = "PTR"
|
45
45
|
@server.expects(:do_create).with(value, name, type)
|
@@ -48,7 +48,7 @@ class InternalApiTest < Test::Unit::TestCase
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def test_delete_a_record
|
51
|
-
name = "
|
51
|
+
name = "sub.example.com"
|
52
52
|
@server.expects(:do_remove).with(name, "A")
|
53
53
|
delete name
|
54
54
|
assert last_response.ok?, "Last response was not ok: #{last_response.status} #{last_response.body}"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dns_powerdns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ewoud Kohl van Wijngaarden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -38,34 +38,6 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: mysql2
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pg
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
41
|
description: PowerDNS DNS provider plugin for Foreman's smart proxy
|
70
42
|
email:
|
71
43
|
- ewoud@kohlvanwijngaarden.nl
|
@@ -78,10 +50,6 @@ files:
|
|
78
50
|
- bundler.d/dns_powerdns.rb
|
79
51
|
- config/dns_powerdns.yml
|
80
52
|
- lib/smart_proxy_dns_powerdns.rb
|
81
|
-
- lib/smart_proxy_dns_powerdns/backend/dummy.rb
|
82
|
-
- lib/smart_proxy_dns_powerdns/backend/mysql.rb
|
83
|
-
- lib/smart_proxy_dns_powerdns/backend/postgresql.rb
|
84
|
-
- lib/smart_proxy_dns_powerdns/backend/rest.rb
|
85
53
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_configuration.rb
|
86
54
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb
|
87
55
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb
|
@@ -89,10 +57,6 @@ files:
|
|
89
57
|
- test/integration/integration_test.rb
|
90
58
|
- test/test_helper.rb
|
91
59
|
- test/unit/dns_powerdns_configuration_test.rb
|
92
|
-
- test/unit/dns_powerdns_record_dummy_test.rb
|
93
|
-
- test/unit/dns_powerdns_record_mysql_test.rb
|
94
|
-
- test/unit/dns_powerdns_record_postgresql_test.rb
|
95
|
-
- test/unit/dns_powerdns_record_rest_test.rb
|
96
60
|
- test/unit/dns_powerdns_record_test.rb
|
97
61
|
- test/unit/internal_api_test.rb
|
98
62
|
homepage: https://github.com/theforeman/smart_proxy_dns_powerdns
|
@@ -119,12 +83,8 @@ signing_key:
|
|
119
83
|
specification_version: 4
|
120
84
|
summary: PowerDNS DNS provider plugin for Foreman's smart proxy
|
121
85
|
test_files:
|
122
|
-
- test/unit/dns_powerdns_record_rest_test.rb
|
123
86
|
- test/unit/dns_powerdns_record_test.rb
|
124
|
-
- test/unit/dns_powerdns_record_mysql_test.rb
|
125
87
|
- test/unit/dns_powerdns_configuration_test.rb
|
126
88
|
- test/unit/internal_api_test.rb
|
127
|
-
- test/unit/dns_powerdns_record_postgresql_test.rb
|
128
|
-
- test/unit/dns_powerdns_record_dummy_test.rb
|
129
89
|
- test/test_helper.rb
|
130
90
|
- test/integration/integration_test.rb
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module Proxy::Dns::Powerdns::Backend
|
2
|
-
class Dummy < ::Proxy::Dns::Powerdns::Record
|
3
|
-
|
4
|
-
def initialize(a_server, a_ttl)
|
5
|
-
super(a_server, a_ttl)
|
6
|
-
end
|
7
|
-
|
8
|
-
def get_zone name
|
9
|
-
{
|
10
|
-
'id' => 1,
|
11
|
-
'name' => name.partition('.')[2]
|
12
|
-
}
|
13
|
-
end
|
14
|
-
|
15
|
-
def create_record domain_id, name, content, type
|
16
|
-
false
|
17
|
-
end
|
18
|
-
|
19
|
-
def delete_record domain_id, name, type
|
20
|
-
false
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'mysql2'
|
2
|
-
|
3
|
-
module Proxy::Dns::Powerdns::Backend
|
4
|
-
class Mysql < ::Proxy::Dns::Powerdns::Record
|
5
|
-
|
6
|
-
attr_reader :hostname, :username, :password, :database
|
7
|
-
|
8
|
-
def initialize(a_server, a_ttl, pdnssec, hostname, username, password, database)
|
9
|
-
@hostname = hostname
|
10
|
-
@username = username
|
11
|
-
@password = password
|
12
|
-
@database = database
|
13
|
-
|
14
|
-
super(a_server, a_ttl, pdnssec)
|
15
|
-
end
|
16
|
-
|
17
|
-
def connection
|
18
|
-
@connection ||= Mysql2::Client.new(:host => hostname, :username => username, :password => password, :database => database)
|
19
|
-
end
|
20
|
-
|
21
|
-
def get_zone name
|
22
|
-
domain = nil
|
23
|
-
|
24
|
-
name = connection.escape(name)
|
25
|
-
connection.query("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE '#{name}' LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1").each do |row|
|
26
|
-
domain = row
|
27
|
-
end
|
28
|
-
|
29
|
-
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless domain
|
30
|
-
|
31
|
-
domain
|
32
|
-
end
|
33
|
-
|
34
|
-
def create_record domain_id, name, type, content
|
35
|
-
name = connection.escape(name)
|
36
|
-
content = connection.escape(content)
|
37
|
-
type = connection.escape(type)
|
38
|
-
connection.query("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES (#{domain_id}, '#{name}', #{ttl.to_i}, '#{content}', '#{type}', UNIX_TIMESTAMP())")
|
39
|
-
connection.affected_rows == 1
|
40
|
-
end
|
41
|
-
|
42
|
-
def delete_record domain_id, name, type
|
43
|
-
name = connection.escape(name)
|
44
|
-
type = connection.escape(type)
|
45
|
-
connection.query("DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{name}' AND type='#{type}'")
|
46
|
-
return false if connection.affected_rows == 0
|
47
|
-
|
48
|
-
connection.query("UPDATE records SET change_date=UNIX_TIMESTAMP() WHERE domain_id=#{domain_id} AND type='SOA'")
|
49
|
-
affected_rows = connection.affected_rows
|
50
|
-
if affected_rows > 1
|
51
|
-
logger.warning("Updated multiple SOA records (host=#{name}, domain_id=#{domain_id}). Check your zone records for duplicate SOA entries.")
|
52
|
-
elsif affected_rows == 0
|
53
|
-
logger.info("No SOA record updated (host=#{name}, domain_id=#{domain_id}). This can be caused by either a missing SOA record for the zone or consecutive updates of the same zone during the same second.")
|
54
|
-
end
|
55
|
-
true
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'pg'
|
2
|
-
|
3
|
-
module Proxy::Dns::Powerdns::Backend
|
4
|
-
class Postgresql < ::Proxy::Dns::Powerdns::Record
|
5
|
-
|
6
|
-
attr_reader :connection_str
|
7
|
-
|
8
|
-
def initialize(a_server, a_ttl, pdnssec, connection)
|
9
|
-
@connection_str = connection
|
10
|
-
|
11
|
-
super(a_server, a_ttl, pdnssec)
|
12
|
-
end
|
13
|
-
|
14
|
-
def connection
|
15
|
-
@connection ||= PG.connect(connection_str)
|
16
|
-
end
|
17
|
-
|
18
|
-
def get_zone name
|
19
|
-
domain = nil
|
20
|
-
|
21
|
-
connection.exec_params("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE $1 LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1", [name]) do |result|
|
22
|
-
result.each do |row|
|
23
|
-
domain = row
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless domain
|
28
|
-
|
29
|
-
domain
|
30
|
-
end
|
31
|
-
|
32
|
-
def create_record domain_id, name, type, content
|
33
|
-
result = connection.exec_params("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES ($1::int, $2, $3::int, $4, $5, extract(epoch from now()))", [domain_id, name, ttl, content, type])
|
34
|
-
result.cmdtuples == 1
|
35
|
-
end
|
36
|
-
|
37
|
-
def delete_record domain_id, name, type
|
38
|
-
result = connection.exec_params("DELETE FROM records WHERE domain_id=$1::int AND name=$2 AND type=$3", [domain_id, name, type])
|
39
|
-
return false if result.cmdtuples == 0
|
40
|
-
|
41
|
-
result = connection.exec_params("UPDATE records SET change_date=extract(epoch from now()) WHERE domain_id=$1::int AND type='SOA'", [domain_id])
|
42
|
-
affected_rows = result.cmdtuples
|
43
|
-
if affected_rows > 1
|
44
|
-
logger.warning("Updated multiple SOA records (host=#{name}, domain_id=#{domain_id}). Check your zone records for duplicate SOA entries.")
|
45
|
-
elsif affected_rows == 0
|
46
|
-
logger.info("No SOA record updated (host=#{name}, domain_id=#{domain_id}). This can be caused by either a missing SOA record for the zone or consecutive updates of the same zone during the same second.")
|
47
|
-
end
|
48
|
-
true
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'net/http'
|
3
|
-
require 'resolv'
|
4
|
-
|
5
|
-
module Proxy::Dns::Powerdns::Backend
|
6
|
-
extend ::Proxy::Log
|
7
|
-
|
8
|
-
class Rest < ::Proxy::Dns::Powerdns::Record
|
9
|
-
|
10
|
-
attr_reader :url, :api_key
|
11
|
-
|
12
|
-
def initialize(a_server, a_ttl, url, api_key)
|
13
|
-
@url = url
|
14
|
-
@api_key = api_key
|
15
|
-
|
16
|
-
super(a_server, a_ttl)
|
17
|
-
end
|
18
|
-
|
19
|
-
def get_zone name
|
20
|
-
fqdn = Resolv::DNS::Name.create(name)
|
21
|
-
fqdn = Resolv::DNS::Name.create(name + '.') unless fqdn.absolute?
|
22
|
-
uri = URI("#{@url}/zones")
|
23
|
-
|
24
|
-
result = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
25
|
-
request = Net::HTTP::Get.new uri
|
26
|
-
request['X-API-Key'] = @api_key
|
27
|
-
response = http.request request
|
28
|
-
zones = JSON.parse(response.body) rescue []
|
29
|
-
|
30
|
-
zones.select { |zone|
|
31
|
-
domain = Resolv::DNS::Name.create(zone['name'])
|
32
|
-
domain == fqdn or fqdn.subdomain_of?(domain)
|
33
|
-
}.max_by { |zone| zone['name'].length }
|
34
|
-
end
|
35
|
-
|
36
|
-
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless result
|
37
|
-
|
38
|
-
result
|
39
|
-
end
|
40
|
-
|
41
|
-
def create_record domain_id, name, type, content
|
42
|
-
content += '.' if ['PTR', 'CNAME'].include?(type)
|
43
|
-
rrset = {
|
44
|
-
:name => name + '.',
|
45
|
-
:type => type,
|
46
|
-
:ttl => @ttl.to_i,
|
47
|
-
:changetype => :REPLACE,
|
48
|
-
:records => [
|
49
|
-
{
|
50
|
-
:content => content,
|
51
|
-
:disabled => false
|
52
|
-
}
|
53
|
-
]
|
54
|
-
}
|
55
|
-
|
56
|
-
patch_records domain_id, rrset
|
57
|
-
end
|
58
|
-
|
59
|
-
def delete_record domain_id, name, type
|
60
|
-
rrset = {
|
61
|
-
:name => name + '.',
|
62
|
-
:type => type,
|
63
|
-
:changetype => :DELETE,
|
64
|
-
:records => []
|
65
|
-
}
|
66
|
-
|
67
|
-
patch_records domain_id, rrset
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def patch_records domain_id, rrset
|
73
|
-
uri = URI("#{@url}/zones/#{domain_id}")
|
74
|
-
|
75
|
-
data = { :rrsets => [rrset] }
|
76
|
-
|
77
|
-
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
78
|
-
request = Net::HTTP::Patch.new uri
|
79
|
-
request['X-API-Key'] = @api_key
|
80
|
-
request['Content-Type'] = 'application/json'
|
81
|
-
request.body = data.to_json
|
82
|
-
response = http.request request
|
83
|
-
unless response.is_a?(Net::HTTPSuccess)
|
84
|
-
begin
|
85
|
-
content = JSON.parse(response.body)
|
86
|
-
rescue
|
87
|
-
logger.debug "Failed to pach records for #{domain_id} with '#{rrset}': #{response.body}"
|
88
|
-
raise Proxy::Dns::Error.new("Failed to patch records")
|
89
|
-
end
|
90
|
-
raise Proxy::Dns::Error.new("Failed to patch records: #{content['error']}")
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
true
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
4
|
-
require 'smart_proxy_dns_powerdns/backend/dummy'
|
5
|
-
|
6
|
-
class DnsPowerdnsBackendDummyTest < Test::Unit::TestCase
|
7
|
-
def setup
|
8
|
-
@provider = Proxy::Dns::Powerdns::Backend::Dummy.new('localhost', 86400)
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_initialize
|
12
|
-
assert_equal 86400, @provider.ttl
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_get_zone
|
16
|
-
assert_equal @provider.get_zone('test.example.com'), {'id' => 1, 'name' => 'example.com'}
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_create_record
|
20
|
-
assert_false @provider.create_record(1, 'test.example.com', '10.1.2.3', 'A')
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_delete_record
|
24
|
-
assert_false @provider.delete_record(1, 'test.example.com', 'A')
|
25
|
-
end
|
26
|
-
end
|
@@ -1,114 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
4
|
-
require 'smart_proxy_dns_powerdns/backend/mysql'
|
5
|
-
|
6
|
-
class DnsPowerdnsBackendMysqlTest < Test::Unit::TestCase
|
7
|
-
def setup
|
8
|
-
@provider = Proxy::Dns::Powerdns::Backend::Mysql.new('localhost', 86400, 'sudo pdnssec',
|
9
|
-
'db.example.com', 'the_user',
|
10
|
-
'something_secure', 'db_pdns')
|
11
|
-
@connection = mock()
|
12
|
-
@provider.stubs(:connection).returns(@connection)
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_initialize
|
16
|
-
assert_equal 86400, @provider.ttl
|
17
|
-
assert_equal 'sudo pdnssec', @provider.pdnssec
|
18
|
-
assert_equal 'db.example.com', @provider.hostname
|
19
|
-
assert_equal 'the_user', @provider.username
|
20
|
-
assert_equal 'something_secure', @provider.password
|
21
|
-
assert_equal 'db_pdns', @provider.database
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_get_zone_with_existing_zone
|
25
|
-
@connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
26
|
-
@connection.expects(:query).with("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE 'test.example.com' LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1").returns([{'id' => 1, 'name' => 'example.com'}])
|
27
|
-
|
28
|
-
assert_equal(@provider.get_zone('test.example.com'), {'id' => 1, 'name' => 'example.com'})
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_get_zone_without_existing_zone
|
32
|
-
@connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
33
|
-
@connection.expects(:query).with("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE 'test.example.com' LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1").returns([])
|
34
|
-
|
35
|
-
assert_raise(Proxy::Dns::Error) { @provider.get_zone('test.example.com') }
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_create_record
|
39
|
-
@connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
40
|
-
@connection.expects(:escape).with('A').returns('A')
|
41
|
-
@connection.expects(:escape).with('10.1.1.1').returns('10.1.1.1')
|
42
|
-
@connection.expects(:query).with("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES (1, 'test.example.com', 86400, '10.1.1.1', 'A', UNIX_TIMESTAMP())")
|
43
|
-
@connection.expects(:affected_rows).returns(1)
|
44
|
-
|
45
|
-
assert @provider.create_record(1, 'test.example.com', 'A', '10.1.1.1')
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_delete_record
|
49
|
-
mock_escapes(fqdn, 'A')
|
50
|
-
@connection.expects(:query).with(query_delete)
|
51
|
-
@connection.expects(:query).with(query_update_soa)
|
52
|
-
@connection.expects(:affected_rows).twice.returns(1)
|
53
|
-
assert @provider.delete_record(domain_id, fqdn, 'A')
|
54
|
-
end
|
55
|
-
|
56
|
-
def test_delete_no_record
|
57
|
-
mock_escapes(fqdn, 'A')
|
58
|
-
@connection.expects(:query).with(query_delete)
|
59
|
-
@connection.expects(:affected_rows).returns(0)
|
60
|
-
|
61
|
-
assert_false @provider.delete_record(domain_id, fqdn, 'A')
|
62
|
-
end
|
63
|
-
|
64
|
-
def test_delete_record_no_soa
|
65
|
-
mock_escapes(fqdn, 'A')
|
66
|
-
@connection.expects(:query).with(query_delete)
|
67
|
-
@connection.expects(:query).with(query_update_soa)
|
68
|
-
@connection.expects(:affected_rows).twice.returns(1, 0)
|
69
|
-
logger = mock()
|
70
|
-
logger.expects(:info)
|
71
|
-
@provider.stubs(:logger).returns(logger)
|
72
|
-
|
73
|
-
assert @provider.delete_record(domain_id, fqdn, 'A')
|
74
|
-
end
|
75
|
-
|
76
|
-
def test_delete_record_multiple_soa
|
77
|
-
mock_escapes(fqdn, 'A')
|
78
|
-
@connection.expects(:query).with(query_delete)
|
79
|
-
@connection.expects(:query).with(query_update_soa)
|
80
|
-
@connection.expects(:affected_rows).twice.returns(1, 2)
|
81
|
-
logger = mock()
|
82
|
-
logger.expects(:warning)
|
83
|
-
@provider.stubs(:logger).returns(logger)
|
84
|
-
|
85
|
-
assert @provider.delete_record(domain_id, fqdn, 'A')
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def mock_escapes(*elts)
|
91
|
-
elts.each { |e| @connection.expects(:escape).with(e).returns(e) }
|
92
|
-
end
|
93
|
-
|
94
|
-
def domain
|
95
|
-
'example.com'
|
96
|
-
end
|
97
|
-
|
98
|
-
def fqdn
|
99
|
-
"test.#{domain}"
|
100
|
-
end
|
101
|
-
|
102
|
-
def domain_id
|
103
|
-
1
|
104
|
-
end
|
105
|
-
|
106
|
-
def query_delete(type='A')
|
107
|
-
"DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{fqdn}' AND type='#{type}'"
|
108
|
-
end
|
109
|
-
|
110
|
-
def query_update_soa
|
111
|
-
"UPDATE records SET change_date=UNIX_TIMESTAMP() WHERE domain_id=#{domain_id} AND type='SOA'"
|
112
|
-
end
|
113
|
-
|
114
|
-
end
|
@@ -1,125 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
4
|
-
require 'smart_proxy_dns_powerdns/backend/postgresql'
|
5
|
-
|
6
|
-
class DnsPowerdnsBackendPostgresqlTest < Test::Unit::TestCase
|
7
|
-
def setup
|
8
|
-
@provider = Proxy::Dns::Powerdns::Backend::Postgresql.new('localhost', 86400, 'sudo pdnssec',
|
9
|
-
'dbname=powerdns')
|
10
|
-
@connection = mock()
|
11
|
-
@provider.stubs(:connection).returns(@connection)
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_initialize
|
15
|
-
assert_equal 86400, @provider.ttl
|
16
|
-
assert_equal 'sudo pdnssec', @provider.pdnssec
|
17
|
-
assert_equal 'dbname=powerdns', @provider.connection_str
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_get_zone_with_existing_zone
|
21
|
-
@connection.expects(:exec_params).
|
22
|
-
with("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE $1 LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1", ['test.example.com']).
|
23
|
-
yields([{'id' => 1, 'name' => 'example.com'}])
|
24
|
-
|
25
|
-
assert_equal(@provider.get_zone('test.example.com'), {'id' => 1, 'name' => 'example.com'})
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_get_zone_without_existing_zone
|
29
|
-
@connection.expects(:exec_params).
|
30
|
-
with("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE $1 LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1", ['test.example.com']).
|
31
|
-
yields([])
|
32
|
-
|
33
|
-
assert_raise(Proxy::Dns::Error) { @provider.get_zone('test.example.com') }
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_create_record
|
37
|
-
@connection.expects(:exec_params).
|
38
|
-
with("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES ($1::int, $2, $3::int, $4, $5, extract(epoch from now()))", [1, 'test.example.com', 86400, '10.1.1.1', 'A']).
|
39
|
-
returns(mock(:cmdtuples => 1))
|
40
|
-
|
41
|
-
assert_true @provider.create_record(1, 'test.example.com', 'A', '10.1.1.1')
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_delete_record_no_records
|
45
|
-
mock_delete_tuples(0)
|
46
|
-
assert_false run_delete_record
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_delete_record_single_record
|
50
|
-
mock_delete_tuples(1)
|
51
|
-
mock_update_soa_tuples(1)
|
52
|
-
|
53
|
-
assert_true run_delete_record
|
54
|
-
end
|
55
|
-
|
56
|
-
def test_delete_record_multiple_records
|
57
|
-
mock_delete_tuples(2)
|
58
|
-
mock_update_soa_tuples(1)
|
59
|
-
|
60
|
-
assert_true run_delete_record
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_delete_record_no_soa
|
64
|
-
mock_delete_tuples(1)
|
65
|
-
mock_update_soa_tuples(0)
|
66
|
-
logger = mock()
|
67
|
-
logger.expects(:info)
|
68
|
-
@provider.stubs(:logger).returns(logger)
|
69
|
-
|
70
|
-
assert_true run_delete_record
|
71
|
-
end
|
72
|
-
|
73
|
-
def test_delete_record_multiple_soa
|
74
|
-
mock_delete_tuples(1)
|
75
|
-
mock_update_soa_tuples(2)
|
76
|
-
logger = mock()
|
77
|
-
logger.expects(:warning)
|
78
|
-
@provider.stubs(:logger).returns(logger)
|
79
|
-
|
80
|
-
assert_true run_delete_record
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def mock_delete_tuples(cmdtuples)
|
86
|
-
@connection.expects(:exec_params).
|
87
|
-
with(query_delete, [domain_id, fqdn, record_type]).
|
88
|
-
returns(mock(:cmdtuples => cmdtuples))
|
89
|
-
end
|
90
|
-
|
91
|
-
def mock_update_soa_tuples(cmdtuples)
|
92
|
-
@connection.expects(:exec_params).
|
93
|
-
with(query_update_soa, [domain_id]).
|
94
|
-
returns(mock(:cmdtuples => cmdtuples))
|
95
|
-
end
|
96
|
-
|
97
|
-
def run_delete_record
|
98
|
-
@provider.delete_record(domain_id, fqdn, record_type)
|
99
|
-
end
|
100
|
-
|
101
|
-
def domain
|
102
|
-
'example.com'
|
103
|
-
end
|
104
|
-
|
105
|
-
def fqdn
|
106
|
-
"test.#{domain}"
|
107
|
-
end
|
108
|
-
|
109
|
-
def domain_id
|
110
|
-
1
|
111
|
-
end
|
112
|
-
|
113
|
-
def record_type
|
114
|
-
'A'
|
115
|
-
end
|
116
|
-
|
117
|
-
def query_delete
|
118
|
-
"DELETE FROM records WHERE domain_id=$1::int AND name=$2 AND type=$3"
|
119
|
-
end
|
120
|
-
|
121
|
-
def query_update_soa
|
122
|
-
"UPDATE records SET change_date=extract(epoch from now()) WHERE domain_id=$1::int AND type='SOA'"
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'webmock/test_unit'
|
3
|
-
|
4
|
-
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
-
require 'smart_proxy_dns_powerdns/backend/rest'
|
6
|
-
|
7
|
-
class DnsPowerdnsBackendRestTest < Test::Unit::TestCase
|
8
|
-
def setup
|
9
|
-
@provider = Proxy::Dns::Powerdns::Backend::Rest.new('localhost', 86400,
|
10
|
-
'http://localhost:8081/api/v1/servers/localhost',
|
11
|
-
'apikey')
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_initialize
|
15
|
-
assert_equal 86400, @provider.ttl
|
16
|
-
assert_equal 'http://localhost:8081/api/v1/servers/localhost', @provider.url
|
17
|
-
assert_equal 'apikey', @provider.api_key
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_get_zone_with_existing_zone
|
21
|
-
stub_request(:get, "http://localhost:8081/api/v1/servers/localhost/zones").
|
22
|
-
with(:headers => {'X-Api-Key' => 'apikey'}).
|
23
|
-
to_return(:body => '[{"id": "example.com.", "name": "example.com."}]')
|
24
|
-
assert_equal @provider.get_zone('test.example.com'), {'id' => 'example.com.', 'name' => 'example.com.'}
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_get_zone_with_existing_zone_absolute_record
|
28
|
-
stub_request(:get, "http://localhost:8081/api/v1/servers/localhost/zones").
|
29
|
-
with(:headers => {'X-Api-Key' => 'apikey'}).
|
30
|
-
to_return(:body => '[{"id": "example.com.", "name": "example.com."}]')
|
31
|
-
assert_equal @provider.get_zone('test.example.com.'), {'id' => 'example.com.', 'name' => 'example.com.'}
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_get_zone_without_existing_zone
|
35
|
-
stub_request(:get, "http://localhost:8081/api/v1/servers/localhost/zones").
|
36
|
-
with(:headers => {'X-Api-Key' => 'apikey'}).
|
37
|
-
to_return(:body => '[]')
|
38
|
-
assert_raise(Proxy::Dns::Error) { @provider.get_zone('test.example.com') }
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_create_a_record
|
42
|
-
stub_request(:patch, "http://localhost:8081/api/v1/servers/localhost/zones/example.com.").
|
43
|
-
with(
|
44
|
-
:headers => {'X-Api-Key' => 'apikey', 'Content-Type' => 'application/json'},
|
45
|
-
:body => '{"rrsets":[{"name":"test.example.com.","type":"A","ttl":86400,"changetype":"REPLACE","records":[{"content":"10.1.1.1","disabled":false}]}]}'
|
46
|
-
)
|
47
|
-
assert @provider.create_record('example.com.', 'test.example.com', 'A', '10.1.1.1')
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_create_ptr_record
|
51
|
-
stub_request(:patch, "http://localhost:8081/api/v1/servers/localhost/zones/example.com.").
|
52
|
-
with(
|
53
|
-
:headers => {'X-Api-Key' => 'apikey', 'Content-Type' => 'application/json'},
|
54
|
-
:body => '{"rrsets":[{"name":"1.1.1.10.in-addr.arpa.","type":"PTR","ttl":86400,"changetype":"REPLACE","records":[{"content":"test.example.com.","disabled":false}]}]}'
|
55
|
-
)
|
56
|
-
assert @provider.create_record('example.com.', '1.1.1.10.in-addr.arpa', 'PTR', 'test.example.com')
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_delete_record
|
60
|
-
stub_request(:patch, "http://localhost:8081/api/v1/servers/localhost/zones/example.com.").
|
61
|
-
with(
|
62
|
-
:headers => {'X-Api-Key' => 'apikey', 'Content-Type' => 'application/json'},
|
63
|
-
:body => '{"rrsets":[{"name":"test.example.com.","type":"A","changetype":"DELETE","records":[]}]}'
|
64
|
-
)
|
65
|
-
assert @provider.delete_record('example.com.', 'test.example.com', 'A')
|
66
|
-
end
|
67
|
-
end
|