smart_proxy_dns_powerdns 0.1.0 → 0.2.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 +26 -8
- data/lib/smart_proxy_dns_powerdns/backend/dummy.rb +24 -0
- data/lib/smart_proxy_dns_powerdns/backend/mysql.rb +49 -0
- data/lib/smart_proxy_dns_powerdns/backend/postgresql.rb +42 -0
- data/lib/smart_proxy_dns_powerdns/dependencies.rb +15 -0
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_configuration_validator.rb +32 -0
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb +38 -85
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb +8 -3
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb +1 -1
- data/test/integration/integration_test.rb +83 -0
- data/test/unit/dns_powerdns_configuration_validator_test.rb +78 -0
- data/test/unit/dns_powerdns_record_mysql_test.rb +77 -0
- data/test/unit/dns_powerdns_record_postgresql_test.rb +20 -0
- data/test/unit/dns_powerdns_record_test.rb +115 -0
- metadata +39 -14
- data/test/dns_powerdns_record_test.rb +0 -121
- data/test/integration_tests.py +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0bc4acefdd0c176ea6f1d9fcbb4037f36da5606
|
4
|
+
data.tar.gz: 4386be7b8b5d77463f91fd005a35735893fa64f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a4143021040bf61c3de7509fe63db0c3951cc3bc3a3f42ab8ce3f9e95777228282063801cae652de32b7e8d51e5fe9f18a61b66e5061021fc0934cf05d692b3
|
7
|
+
data.tar.gz: 6ef93f4db7399b45c31937ba71a5a5fbb208880a5ec503c4a3c82ff7255167850f78ce9a6f7951dc4beb6dc7e612105ac5531116da6be32c6a33140f26e2d237
|
data/README.md
CHANGED
@@ -7,7 +7,15 @@ This plugin adds a new DNS provider for managing records in PowerDNS.
|
|
7
7
|
See [How_to_Install_a_Smart-Proxy_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin)
|
8
8
|
for how to install Smart Proxy plugins
|
9
9
|
|
10
|
-
This plugin is compatible with Smart Proxy 1.
|
10
|
+
This plugin is compatible with Smart Proxy 1.11 or higher.
|
11
|
+
|
12
|
+
When installing using "gem", make sure to install the bundle file:
|
13
|
+
|
14
|
+
echo "gem 'smart_proxy_dns_powerdns'" > /usr/share/foreman-proxy/bundler.d/dns_powerdns.rb
|
15
|
+
|
16
|
+
## Upgrading
|
17
|
+
|
18
|
+
Per version 0.2.0 the backend is a required parameter.
|
11
19
|
|
12
20
|
## Configuration
|
13
21
|
|
@@ -15,13 +23,25 @@ To enable this DNS provider, edit `/etc/foreman-proxy/settings.d/dns.yml` and se
|
|
15
23
|
|
16
24
|
:use_provider: dns_powerdns
|
17
25
|
|
18
|
-
Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dns_powerdns.yml
|
26
|
+
Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dns_powerdns.yml`.
|
27
|
+
|
28
|
+
### MySQL
|
19
29
|
|
30
|
+
To use MySQL, set the following parameters:
|
31
|
+
|
32
|
+
:powerdns_backend: 'mysql'
|
20
33
|
:powerdns_mysql_hostname: 'localhost'
|
21
34
|
:powerdns_mysql_username: 'powerdns'
|
22
35
|
:powerdns_mysql_password: ''
|
23
36
|
:powerdns_mysql_database: 'powerdns'
|
24
37
|
|
38
|
+
### PostgreSQL
|
39
|
+
|
40
|
+
To use PostgreSQL, set the following parameters:
|
41
|
+
|
42
|
+
:powerdns_backend: 'postgresql'
|
43
|
+
:powerdns_postgresql_connection: 'host=localhost user=powerdns password=mypassword dbname=powerdns'
|
44
|
+
|
25
45
|
### DNSSEC
|
26
46
|
|
27
47
|
In case you've enabled DNSSEC (as you should), a rectify-zone is required after every zone change. The pdnssec command is configurable:
|
@@ -38,22 +58,20 @@ Fork and send a Pull Request. Thanks!
|
|
38
58
|
|
39
59
|
### Running the integration tests
|
40
60
|
|
41
|
-
|
42
|
-
|
43
|
-
First you need to run the smart proxy on `http://localhost:8000` and a powerdns instance on `127.0.0.1:5300` or change it in the fixtures.
|
61
|
+
First you need to run the smart proxy on `http://localhost:8000` and a powerdns instance on `127.0.0.1:5300`.
|
44
62
|
|
45
63
|
It is assumed the powerdns instance has both the `example.com` and `in-addr.arpa` domains configured. If not, create them:
|
46
64
|
|
47
65
|
INSERT INTO domains (name, type) VALUES ('example.com', 'master'), ('in-addr.arpa', 'master');
|
48
66
|
INSERT INTO records (domain_id, name, type, content) SELECT id domain_id, name, 'SOA', 'ns1.example.com hostmaster.example.com. 0 3600 1800 1209600 3600' FROM domains WHERE NOT EXISTS (SELECT 1 FROM records WHERE records.domain_id=domains.id AND records.name=domains.name AND type='SOA');
|
49
67
|
|
50
|
-
Then
|
68
|
+
Then run the tests:
|
51
69
|
|
52
|
-
|
70
|
+
bundle exec rake test:integration
|
53
71
|
|
54
72
|
## Copyright
|
55
73
|
|
56
|
-
Copyright (c) 2015 Ewoud Kohl van Wijngaarden
|
74
|
+
Copyright (c) 2015 - 2016 Ewoud Kohl van Wijngaarden
|
57
75
|
|
58
76
|
This program is free software: you can redistribute it and/or modify
|
59
77
|
it under the terms of the GNU General Public License as published by
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Proxy::Dns::Powerdns::Backend
|
2
|
+
class Dummy < ::Proxy::Dns::Powerdns::Record
|
3
|
+
|
4
|
+
def initialize(a_server = nil, a_ttl = nil)
|
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, ttl, content, type
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete_record domain_id, name, type
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,49 @@
|
|
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 = nil, a_ttl = nil)
|
9
|
+
@hostname = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_hostname || 'localhost'
|
10
|
+
@username = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_username
|
11
|
+
@password = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_password
|
12
|
+
@database = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_database
|
13
|
+
|
14
|
+
super(a_server, a_ttl)
|
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. 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) VALUES (#{domain_id}, '#{name}', #{ttl.to_i}, '#{content}', '#{type}')")
|
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
|
+
connection.affected_rows == 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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 = nil, a_ttl = nil)
|
9
|
+
@connection_str = Proxy::Dns::Powerdns::Plugin.settings.powerdns_postgresql_connection
|
10
|
+
|
11
|
+
super(a_server, a_ttl)
|
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. 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) VALUES ($1::int, $2, $3::int, $4, $5)", [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
|
+
result.cmdtuples == 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'dns_common/dependency_injection/dependencies'
|
2
|
+
|
3
|
+
class Proxy::Dns::DependencyInjection::Dependencies
|
4
|
+
case Proxy::Dns::Powerdns::Plugin.settings.powerdns_backend
|
5
|
+
when 'mysql'
|
6
|
+
require 'smart_proxy_dns_powerdns/backend/mysql'
|
7
|
+
dependency :dns_provider, Proxy::Dns::Powerdns::Backend::Mysql
|
8
|
+
when 'postgresql'
|
9
|
+
require 'smart_proxy_dns_powerdns/backend/postgresql'
|
10
|
+
dependency :dns_provider, Proxy::Dns::Powerdns::Backend::Postgresql
|
11
|
+
when 'dummy'
|
12
|
+
require 'smart_proxy_dns_powerdns/backend/dummy'
|
13
|
+
dependency :dns_provider, Proxy::Dns::Powerdns::Backend::Dummy
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
2
|
+
|
3
|
+
module Proxy::Dns::Powerdns
|
4
|
+
class ConfigurationValidator
|
5
|
+
def validate_settings!(settings)
|
6
|
+
validate_choice(settings, :powerdns_backend, ['mysql', 'postgresql', 'dummy'])
|
7
|
+
|
8
|
+
case settings.powerdns_backend
|
9
|
+
when 'mysql'
|
10
|
+
validate_presence(settings, [:powerdns_mysql_username, :powerdns_mysql_password, :powerdns_mysql_database])
|
11
|
+
when 'postgresql'
|
12
|
+
validate_presence(settings, [:powerdns_postgresql_connection])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_choice(settings, setting, choices)
|
17
|
+
value = settings.send(setting)
|
18
|
+
unless choices.include?(value)
|
19
|
+
raise ::Proxy::Error::ConfigurationError, "Parameter '#{setting}' is expected to be one of #{choices.join(",")}"
|
20
|
+
end
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_presence(settings, names)
|
25
|
+
names.each do |name|
|
26
|
+
value = settings.send(name)
|
27
|
+
raise ::Proxy::Error::ConfigurationError, "Parameter '#{name}' is expected to have a non-empty value" if value.nil? || value.to_s.empty?
|
28
|
+
end
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,120 +1,73 @@
|
|
1
1
|
require 'dns/dns'
|
2
|
+
require 'dns_common/dns_common'
|
2
3
|
require 'ipaddr'
|
3
|
-
require 'mysql2'
|
4
4
|
|
5
5
|
module Proxy::Dns::Powerdns
|
6
6
|
class Record < ::Proxy::Dns::Record
|
7
7
|
include Proxy::Log
|
8
8
|
include Proxy::Util
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :pdnssec
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
:powerdns_mysql_username => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_username,
|
16
|
-
:powerdns_mysql_password => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_password,
|
17
|
-
:powerdns_mysql_database => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_database,
|
18
|
-
:powerdns_pdnssec => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_pdnssec
|
19
|
-
))
|
12
|
+
def initialize(a_server = nil, a_ttl = nil)
|
13
|
+
@pdnssec = Proxy::Dns::Powerdns::Plugin.settings.powerdns_pdnssec
|
14
|
+
super(a_server, a_ttl || Proxy::Dns::Plugin.settings.dns_ttl)
|
20
15
|
end
|
21
16
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
raise "dns_powerdns provider needs 'powerdns_mysql_password' option" unless options[:powerdns_mysql_password]
|
26
|
-
raise "dns_powerdns provider needs 'powerdns_mysql_database' option" unless options[:powerdns_mysql_database]
|
27
|
-
@mysql_connection = Mysql2::Client.new(
|
28
|
-
:host => options[:powerdns_mysql_hostname],
|
29
|
-
:username => options[:powerdns_mysql_username],
|
30
|
-
:password => options[:powerdns_mysql_password],
|
31
|
-
:database => options[:powerdns_mysql_database]
|
32
|
-
)
|
33
|
-
|
34
|
-
@powerdns_pdnssec = options[:powerdns_pdnssec] || false
|
35
|
-
|
36
|
-
# Normalize the somewhat weird PTR API spec to name / content
|
37
|
-
case options[:type]
|
38
|
-
when "PTR"
|
39
|
-
if options[:value] =~ /\.(in-addr|ip6)\.arpa$/
|
40
|
-
@name = options[:value]
|
41
|
-
else
|
42
|
-
@name = IPAddr.new(options[:value]).reverse
|
43
|
-
end
|
44
|
-
@content = options[:fqdn]
|
45
|
-
else
|
46
|
-
@name = options[:fqdn]
|
47
|
-
@content = options[:value]
|
17
|
+
def create_a_record(fqdn, ip)
|
18
|
+
if found = dns_find(fqdn)
|
19
|
+
raise Proxy::Dns::Collision, "#{fqdn} is already in use by #{ip}" unless found == ip
|
48
20
|
end
|
49
21
|
|
50
|
-
|
22
|
+
do_create(fqdn, ip, "A")
|
51
23
|
end
|
52
24
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if ip = dns_find(domain_row['id'], @name)
|
58
|
-
raise Proxy::Dns::Collision, "#{@name} is already in use by #{ip}"
|
25
|
+
def create_ptr_record(fqdn, ip)
|
26
|
+
if found = dns_find(ip)
|
27
|
+
raise Proxy::Dns::Collision, "#{ip} is already in use by #{found}" unless found == fqdn
|
59
28
|
end
|
60
29
|
|
61
|
-
|
62
|
-
|
63
|
-
rectify_zone(domain_row['name'])
|
30
|
+
name = IPAddr.new(ip).reverse
|
31
|
+
do_create(name, fqdn, "PTR")
|
64
32
|
end
|
65
33
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
delete_record(domain_row['id'], @name, @type)
|
71
|
-
|
72
|
-
rectify_zone(domain_row['name'])
|
34
|
+
def do_create(name, value, type)
|
35
|
+
zone = get_zone(name)
|
36
|
+
create_record(zone['id'], name, type, value) and rectify_zone(zone['name'])
|
73
37
|
end
|
74
38
|
|
75
|
-
|
76
|
-
|
77
|
-
|
39
|
+
def remove_a_record(fqdn)
|
40
|
+
do_remove(fqdn, "A")
|
41
|
+
end
|
78
42
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
43
|
+
def remove_ptr_record(ip)
|
44
|
+
name = ip # Note ip is already in-addr.arpa
|
45
|
+
do_remove(name, "PTR")
|
46
|
+
end
|
83
47
|
|
84
|
-
|
48
|
+
def do_remove(name, type)
|
49
|
+
zone = get_zone(name)
|
50
|
+
delete_record(zone['id'], name, type) and rectify_zone(zone['name'])
|
85
51
|
end
|
86
52
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
key = mysql_connection.escape(key)
|
91
|
-
mysql_connection.query("SELECT content FROM records WHERE domain_id=#{domain_id} AND name = '#{key}' LIMIT 1").each do |row|
|
92
|
-
value = row["content"]
|
93
|
-
end
|
94
|
-
value || false
|
53
|
+
def get_zone(fqdn)
|
54
|
+
# TODO: backend specific
|
55
|
+
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS."
|
95
56
|
end
|
96
57
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
content = mysql_connection.escape(content)
|
101
|
-
type = mysql_connection.escape(type)
|
102
|
-
mysql_connection.query("INSERT INTO records (domain_id, name, ttl, content, type) VALUES (#{domain_id}, '#{name}', #{ttl.to_i}, '#{content}', '#{type}')")
|
103
|
-
true
|
58
|
+
def create_record(domain_id, name, type, content)
|
59
|
+
# TODO: backend specific
|
60
|
+
false
|
104
61
|
end
|
105
62
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
type = mysql_connection.escape(type)
|
110
|
-
mysql_connection.query("DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{name}' AND type='#{type}'")
|
111
|
-
true
|
63
|
+
def delete_record(domain_id, name, type)
|
64
|
+
# TODO: backend specific
|
65
|
+
false
|
112
66
|
end
|
113
67
|
|
114
|
-
private
|
115
68
|
def rectify_zone domain
|
116
|
-
if @
|
117
|
-
%x(#{@
|
69
|
+
if @pdnssec
|
70
|
+
%x(#{@pdnssec} rectify-zone "#{domain}")
|
118
71
|
|
119
72
|
$?.exitstatus == 0
|
120
73
|
else
|
@@ -2,13 +2,18 @@ require 'smart_proxy_dns_powerdns/dns_powerdns_version'
|
|
2
2
|
|
3
3
|
module Proxy::Dns::Powerdns
|
4
4
|
class Plugin < ::Proxy::Provider
|
5
|
-
plugin :dns_powerdns, ::Proxy::Dns::Powerdns::VERSION
|
6
|
-
:factory => proc { |attrs| ::Proxy::Dns::Powerdns::Record.record(attrs) }
|
5
|
+
plugin :dns_powerdns, ::Proxy::Dns::Powerdns::VERSION
|
7
6
|
|
8
|
-
requires :dns, '>= 1.
|
7
|
+
requires :dns, '>= 1.11'
|
8
|
+
|
9
|
+
validate_presence :powerdns_backend
|
9
10
|
|
10
11
|
after_activation do
|
12
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_configuration_validator'
|
13
|
+
::Proxy::Dns::Powerdns::ConfigurationValidator.new.validate_settings!(settings)
|
14
|
+
|
11
15
|
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
16
|
+
require 'smart_proxy_dns_powerdns/dependencies'
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
class DnsPowerdnsIntegrationTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_forward_dns
|
9
|
+
data = {'fqdn' => fqdn, 'value' => ip, 'type' => 'A'}
|
10
|
+
|
11
|
+
uri = URI(smart_proxy_url)
|
12
|
+
|
13
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
14
|
+
request = Net::HTTP::Post.new(smart_proxy_url + 'dns/')
|
15
|
+
request.form_data = data
|
16
|
+
response = http.request request
|
17
|
+
assert_equal(200, response.code.to_i)
|
18
|
+
|
19
|
+
addresses = resolver.getaddresses(data['fqdn'])
|
20
|
+
assert_equal([Resolv::IPv4.create(data['value'])], addresses, "#{data['fqdn']} should resolve to #{data['value']}")
|
21
|
+
|
22
|
+
request = Net::HTTP::Delete.new(smart_proxy_url + 'dns/' + data['fqdn'])
|
23
|
+
response = http.request request
|
24
|
+
assert_equal(200, response.code.to_i)
|
25
|
+
|
26
|
+
assert(purge_cache data['fqdn'])
|
27
|
+
|
28
|
+
addresses = resolver.getaddresses(data['fqdn'])
|
29
|
+
assert_equal([], addresses)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_reverse_dns
|
34
|
+
data = {'fqdn' => fqdn, 'value' => ip, 'type' => 'PTR'}
|
35
|
+
|
36
|
+
uri = URI(smart_proxy_url)
|
37
|
+
|
38
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
39
|
+
request = Net::HTTP::Post.new(smart_proxy_url + 'dns/')
|
40
|
+
request.form_data = data
|
41
|
+
response = http.request request
|
42
|
+
assert_equal(200, response.code.to_i)
|
43
|
+
|
44
|
+
name = Resolv::IPv4.create(data['value']).to_name.to_s
|
45
|
+
|
46
|
+
addresses = resolver.getnames(data['value'])
|
47
|
+
assert_equal([Resolv::DNS::Name.create(data['fqdn'] + '.')], addresses, "#{data['value']} should reverse to #{data['fqdn']}")
|
48
|
+
|
49
|
+
request = Net::HTTP::Delete.new(smart_proxy_url + 'dns/' + name)
|
50
|
+
response = http.request request
|
51
|
+
assert_equal(200, response.code.to_i)
|
52
|
+
|
53
|
+
assert(purge_cache name)
|
54
|
+
|
55
|
+
addresses = resolver.getnames(data['value'])
|
56
|
+
assert_equal([], addresses)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def resolver
|
63
|
+
Resolv::DNS.new(:nameserver_port => [['127.0.0.1', 5300]])
|
64
|
+
end
|
65
|
+
|
66
|
+
def smart_proxy_url
|
67
|
+
'http://localhost:8000/'
|
68
|
+
end
|
69
|
+
|
70
|
+
def fqdn
|
71
|
+
set = ('a' .. 'z').to_a + ('0' .. '9').to_a
|
72
|
+
10.times.collect {|i| set[rand(set.size)] }.join + '.example.com'
|
73
|
+
end
|
74
|
+
|
75
|
+
def ip
|
76
|
+
IPAddr.new(rand(2 ** 32), Socket::AF_INET).to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
def purge_cache name
|
80
|
+
%x{#{ENV['PDNS_CONTROL'] || "pdns_control"} purge "#{name}"}
|
81
|
+
$? == 0
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
5
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_configuration_validator'
|
6
|
+
|
7
|
+
class DnsPowerdnsConfigurationValidatorTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@config_validator = Proxy::Dns::Powerdns::ConfigurationValidator.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_initialize_missing_backend
|
13
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => nil)
|
14
|
+
|
15
|
+
assert_raise Proxy::Error::ConfigurationError do
|
16
|
+
@config_validator.validate_settings!(settings)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_initialize_invalid_backend
|
21
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'invalid')
|
22
|
+
|
23
|
+
assert_raise Proxy::Error::ConfigurationError do
|
24
|
+
@config_validator.validate_settings!(settings)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_initialize_dummy_with_settings
|
29
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'dummy')
|
30
|
+
|
31
|
+
assert_nothing_raised do
|
32
|
+
@config_validator.validate_settings!(settings)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_initialize_mysql_without_settings
|
37
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'mysql')
|
38
|
+
|
39
|
+
assert_raise Proxy::Error::ConfigurationError do
|
40
|
+
@config_validator.validate_settings!(settings)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_initialize_mysql_with_settings
|
45
|
+
settings = OpenStruct.new(
|
46
|
+
:dns_provider => 'powerdns',
|
47
|
+
:powerdns_backend => 'mysql',
|
48
|
+
:powerdns_mysql_hostname => 'localhost',
|
49
|
+
:powerdns_mysql_username => 'username',
|
50
|
+
:powerdns_mysql_password => 'password',
|
51
|
+
:powerdns_mysql_database => 'powerdns'
|
52
|
+
)
|
53
|
+
|
54
|
+
assert_nothing_raised do
|
55
|
+
@config_validator.validate_settings!(settings)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_initialize_postgresql_without_settings
|
60
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'postgresql')
|
61
|
+
|
62
|
+
assert_raise Proxy::Error::ConfigurationError do
|
63
|
+
@config_validator.validate_settings!(settings)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_initialize_postgresql_with_settings
|
68
|
+
settings = OpenStruct.new(
|
69
|
+
:dns_provider => 'powerdns',
|
70
|
+
:powerdns_backend => 'postgresql',
|
71
|
+
:powerdns_postgresql_connection => 'dbname=powerdns'
|
72
|
+
)
|
73
|
+
|
74
|
+
assert_nothing_raised do
|
75
|
+
@config_validator.validate_settings!(settings)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
require 'smart_proxy_dns_powerdns/backend/mysql'
|
6
|
+
|
7
|
+
class DnsPowerdnsBackendMysqlTest < Test::Unit::TestCase
|
8
|
+
# Test that correct initialization works
|
9
|
+
def test_initialize_dummy_with_settings
|
10
|
+
Proxy::Dns::Powerdns::Plugin.load_test_settings(
|
11
|
+
:powerdns_mysql_hostname => 'db.example.com',
|
12
|
+
:powerdns_mysql_username => 'the_user',
|
13
|
+
:powerdns_mysql_password => 'something_secure',
|
14
|
+
:powerdns_mysql_database => 'db_pdns'
|
15
|
+
)
|
16
|
+
provider = klass.new
|
17
|
+
assert_equal 'db.example.com', provider.hostname
|
18
|
+
assert_equal 'the_user', provider.username
|
19
|
+
assert_equal 'something_secure', provider.password
|
20
|
+
assert_equal 'db_pdns', provider.database
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_get_zone_with_existing_zone
|
24
|
+
instance = klass.new
|
25
|
+
|
26
|
+
connection = mock()
|
27
|
+
instance.stubs(:connection).returns(connection)
|
28
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
29
|
+
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'}])
|
30
|
+
|
31
|
+
assert_equal(instance.get_zone('test.example.com'), {'id' => 1, 'name' => 'example.com'})
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_zone_without_existing_zone
|
35
|
+
instance = klass.new
|
36
|
+
|
37
|
+
connection = mock()
|
38
|
+
instance.stubs(:connection).returns(connection)
|
39
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
40
|
+
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([])
|
41
|
+
|
42
|
+
assert_raise(Proxy::Dns::Error) { instance.get_zone('test.example.com') }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_create_record
|
46
|
+
instance = klass.new
|
47
|
+
|
48
|
+
connection = mock()
|
49
|
+
instance.stubs(:connection).returns(connection)
|
50
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
51
|
+
connection.expects(:escape).with('A').returns('A')
|
52
|
+
connection.expects(:escape).with('10.1.1.1').returns('10.1.1.1')
|
53
|
+
connection.expects(:query).with("INSERT INTO records (domain_id, name, ttl, content, type) VALUES (1, 'test.example.com', 86400, '10.1.1.1', 'A')")
|
54
|
+
connection.expects(:affected_rows).returns(1)
|
55
|
+
|
56
|
+
assert instance.create_record(1, 'test.example.com', 'A', '10.1.1.1')
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_delete_record
|
60
|
+
instance = klass.new
|
61
|
+
|
62
|
+
connection = mock()
|
63
|
+
instance.stubs(:connection).returns(connection)
|
64
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
65
|
+
connection.expects(:escape).with('A').returns('A')
|
66
|
+
connection.expects(:query).with("DELETE FROM records WHERE domain_id=1 AND name='test.example.com' AND type='A'")
|
67
|
+
connection.expects(:affected_rows).returns(1)
|
68
|
+
|
69
|
+
assert instance.delete_record(1, 'test.example.com', 'A')
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def klass
|
75
|
+
Proxy::Dns::Powerdns::Backend::Mysql
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
require 'smart_proxy_dns_powerdns/backend/postgresql'
|
6
|
+
|
7
|
+
class DnsPowerdnsBackendPostgresqlTest < Test::Unit::TestCase
|
8
|
+
# Test that correct initialization works
|
9
|
+
def test_initialize_dummy_with_settings
|
10
|
+
Proxy::Dns::Powerdns::Plugin.load_test_settings(:powerdns_postgresql_connection => 'dbname=powerdns')
|
11
|
+
provider = klass.new
|
12
|
+
assert_equal 'dbname=powerdns', provider.connection_str
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def klass
|
18
|
+
Proxy::Dns::Powerdns::Backend::Postgresql
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
|
6
|
+
class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
7
|
+
# Test that correct initialization works
|
8
|
+
def test_initialize_dummy_with_settings
|
9
|
+
Proxy::Dns::Powerdns::Plugin.load_test_settings(:powerdns_pdnssec => 'sudo pdnssec')
|
10
|
+
provider = klass.new
|
11
|
+
assert_equal 'sudo pdnssec', provider.pdnssec
|
12
|
+
end
|
13
|
+
|
14
|
+
# Test A record creation
|
15
|
+
def test_create_a
|
16
|
+
instance = klass.new
|
17
|
+
|
18
|
+
instance.expects(:dns_find).with('test.example.com').returns(false)
|
19
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
20
|
+
instance.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
21
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
22
|
+
|
23
|
+
assert instance.create_a_record(fqdn, ip)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Test A record creation fails if the record exists
|
27
|
+
def test_create_a_conflict
|
28
|
+
instance = klass.new
|
29
|
+
|
30
|
+
instance.expects(:dns_find).with('test.example.com').returns('192.168.1.1')
|
31
|
+
|
32
|
+
assert_raise(Proxy::Dns::Collision) { instance.create_a_record(fqdn, ip) }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Test PTR record creation
|
36
|
+
def test_create_ptr
|
37
|
+
instance = klass.new
|
38
|
+
|
39
|
+
instance.expects(:dns_find).with('10.1.1.1').returns(false)
|
40
|
+
instance.expects(:get_zone).with('1.1.1.10.in-addr.arpa').returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
41
|
+
instance.expects(:create_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR', 'test.example.com').returns(true)
|
42
|
+
instance.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
43
|
+
|
44
|
+
assert instance.create_ptr_record(fqdn, ip)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Test PTR record creation fails if the record exists
|
48
|
+
def test_create_ptr_conflict
|
49
|
+
instance = klass.new
|
50
|
+
|
51
|
+
instance.expects(:dns_find).with('10.1.1.1').returns('test2.example.com')
|
52
|
+
|
53
|
+
assert_raise(Proxy::Dns::Collision) { instance.create_ptr_record(fqdn, ip) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Test A record removal
|
57
|
+
def test_remove_a
|
58
|
+
instance = klass.new
|
59
|
+
|
60
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
61
|
+
instance.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
62
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
63
|
+
|
64
|
+
assert instance.remove_a_record(fqdn)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Test PTR record removal
|
68
|
+
def test_remove_ptr
|
69
|
+
instance = klass.new
|
70
|
+
|
71
|
+
instance.expects(:get_zone).with('1.1.1.10.in-addr.arpa').returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
72
|
+
instance.expects(:delete_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR').returns(true)
|
73
|
+
instance.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
74
|
+
|
75
|
+
assert instance.remove_ptr_record(reverse_ip)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_do_create
|
79
|
+
instance = klass.new
|
80
|
+
|
81
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
82
|
+
instance.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
83
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
84
|
+
|
85
|
+
assert instance.do_create('test.example.com', '10.1.1.1', 'A')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_do_remove
|
89
|
+
instance = klass.new
|
90
|
+
|
91
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
92
|
+
instance.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
93
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
94
|
+
|
95
|
+
assert instance.do_remove('test.example.com', 'A')
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def klass
|
101
|
+
Proxy::Dns::Powerdns::Record
|
102
|
+
end
|
103
|
+
|
104
|
+
def fqdn
|
105
|
+
'test.example.com'
|
106
|
+
end
|
107
|
+
|
108
|
+
def ip
|
109
|
+
'10.1.1.1'
|
110
|
+
end
|
111
|
+
|
112
|
+
def reverse_ip
|
113
|
+
'1.1.1.10.in-addr.arpa'
|
114
|
+
end
|
115
|
+
end
|
metadata
CHANGED
@@ -1,55 +1,69 @@
|
|
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: 0.2.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: 2016-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mocha
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mysql2
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
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
|
+
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
55
69
|
description: PowerDNS DNS provider plugin for Foreman's smart proxy
|
@@ -64,12 +78,20 @@ files:
|
|
64
78
|
- bundler.d/dns_powerdns.rb
|
65
79
|
- config/dns_powerdns.yml
|
66
80
|
- 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/dependencies.rb
|
85
|
+
- lib/smart_proxy_dns_powerdns/dns_powerdns_configuration_validator.rb
|
67
86
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb
|
68
87
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb
|
69
88
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb
|
70
|
-
- test/
|
71
|
-
- test/integration_tests.py
|
89
|
+
- test/integration/integration_test.rb
|
72
90
|
- test/test_helper.rb
|
91
|
+
- test/unit/dns_powerdns_configuration_validator_test.rb
|
92
|
+
- test/unit/dns_powerdns_record_mysql_test.rb
|
93
|
+
- test/unit/dns_powerdns_record_postgresql_test.rb
|
94
|
+
- test/unit/dns_powerdns_record_test.rb
|
73
95
|
homepage: https://github.com/theforeman/smart_proxy_dns_powerdns
|
74
96
|
licenses:
|
75
97
|
- GPLv3
|
@@ -80,12 +102,12 @@ require_paths:
|
|
80
102
|
- lib
|
81
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
82
104
|
requirements:
|
83
|
-
- -
|
105
|
+
- - ">="
|
84
106
|
- !ruby/object:Gem::Version
|
85
107
|
version: '0'
|
86
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
109
|
requirements:
|
88
|
-
- -
|
110
|
+
- - ">="
|
89
111
|
- !ruby/object:Gem::Version
|
90
112
|
version: '0'
|
91
113
|
requirements: []
|
@@ -96,5 +118,8 @@ specification_version: 4
|
|
96
118
|
summary: PowerDNS DNS provider plugin for Foreman's smart proxy
|
97
119
|
test_files:
|
98
120
|
- test/test_helper.rb
|
99
|
-
- test/
|
100
|
-
- test/
|
121
|
+
- test/integration/integration_test.rb
|
122
|
+
- test/unit/dns_powerdns_record_postgresql_test.rb
|
123
|
+
- test/unit/dns_powerdns_configuration_validator_test.rb
|
124
|
+
- test/unit/dns_powerdns_record_mysql_test.rb
|
125
|
+
- test/unit/dns_powerdns_record_test.rb
|
@@ -1,121 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
4
|
-
|
5
|
-
class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
6
|
-
# Test that a missing :powerdns_mysql_hostname throws an error
|
7
|
-
def test_initialize_without_settings
|
8
|
-
assert_raise(RuntimeError) do
|
9
|
-
klass.new(settings.delete_if { |k,v| k == :powerdns_mysql_hostname })
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# Test that correct initialization works
|
14
|
-
def test_initialize_with_settings
|
15
|
-
assert_nothing_raised do
|
16
|
-
mock_mysql
|
17
|
-
|
18
|
-
klass.new(settings)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Test A record creation
|
23
|
-
def test_create_a
|
24
|
-
mock_mysql
|
25
|
-
|
26
|
-
instance = klass.new(settings)
|
27
|
-
|
28
|
-
instance.expects(:domain).returns({'id' => 1})
|
29
|
-
instance.expects(:dns_find).with(1, 'test.example.com').returns(false)
|
30
|
-
instance.expects(:create_record).with(1, 'test.example.com', 84600, '10.1.1.1', 'A').returns(true)
|
31
|
-
|
32
|
-
assert instance.create
|
33
|
-
end
|
34
|
-
|
35
|
-
# Test A record creation fails if the record exists
|
36
|
-
def test_create_a_conflict
|
37
|
-
mock_mysql
|
38
|
-
|
39
|
-
instance = klass.new(settings)
|
40
|
-
|
41
|
-
instance.expects(:domain).returns({'id' => 1})
|
42
|
-
instance.expects(:dns_find).with(1, 'test.example.com').returns('192.168.1.1')
|
43
|
-
|
44
|
-
assert_raise(Proxy::Dns::Collision) { instance.create }
|
45
|
-
end
|
46
|
-
|
47
|
-
# Test PTR record creation
|
48
|
-
def test_create_ptr
|
49
|
-
mock_mysql
|
50
|
-
|
51
|
-
instance = klass.new(settings.merge(:type => 'PTR'))
|
52
|
-
|
53
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => 'example.com'})
|
54
|
-
instance.expects(:dns_find).with(1, '1.1.1.10.in-addr.arpa').returns(false)
|
55
|
-
instance.expects(:create_record).with(1, '1.1.1.10.in-addr.arpa', 84600, 'test.example.com', 'PTR').returns(true)
|
56
|
-
instance.expects(:rectify_zone).with('example.com').returns(true)
|
57
|
-
|
58
|
-
assert instance.create
|
59
|
-
end
|
60
|
-
|
61
|
-
# Test PTR record creation fails if the record exists
|
62
|
-
def test_create_ptr_conflict
|
63
|
-
mock_mysql
|
64
|
-
|
65
|
-
instance = klass.new(settings.merge(:type => 'PTR'))
|
66
|
-
|
67
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
68
|
-
instance.expects(:dns_find).with(1, '1.1.1.10.in-addr.arpa').returns('test2.example.com')
|
69
|
-
|
70
|
-
assert_raise(Proxy::Dns::Collision) { instance.create }
|
71
|
-
end
|
72
|
-
|
73
|
-
# Test A record removal
|
74
|
-
def test_remove_a
|
75
|
-
mock_mysql
|
76
|
-
|
77
|
-
instance = klass.new(settings)
|
78
|
-
|
79
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => 'example.com'})
|
80
|
-
instance.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
81
|
-
instance.expects(:rectify_zone).with('example.com').returns(true)
|
82
|
-
|
83
|
-
assert instance.remove
|
84
|
-
end
|
85
|
-
|
86
|
-
# Test PTR record removal
|
87
|
-
def test_remove_ptr
|
88
|
-
mock_mysql
|
89
|
-
|
90
|
-
instance = klass.new(settings.merge(:type => 'PTR'))
|
91
|
-
|
92
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
93
|
-
instance.expects(:delete_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR').returns(true)
|
94
|
-
instance.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
95
|
-
|
96
|
-
assert instance.remove
|
97
|
-
end
|
98
|
-
|
99
|
-
def mock_mysql
|
100
|
-
Mysql2::Client.expects(:new).with(:host => 'localhost', :username => 'username', :password => 'password', :database => 'powerdns').returns(false)
|
101
|
-
end
|
102
|
-
|
103
|
-
private
|
104
|
-
|
105
|
-
def klass
|
106
|
-
Proxy::Dns::Powerdns::Record
|
107
|
-
end
|
108
|
-
|
109
|
-
def settings
|
110
|
-
{
|
111
|
-
:powerdns_mysql_hostname => 'localhost',
|
112
|
-
:powerdns_mysql_username => 'username',
|
113
|
-
:powerdns_mysql_password => 'password',
|
114
|
-
:powerdns_mysql_database => 'powerdns',
|
115
|
-
:fqdn => 'test.example.com',
|
116
|
-
:value => '10.1.1.1',
|
117
|
-
:type => 'A',
|
118
|
-
:ttl => 84600,
|
119
|
-
}
|
120
|
-
end
|
121
|
-
end
|
data/test/integration_tests.py
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
import dns.resolver
|
3
|
-
import dns.reversename
|
4
|
-
import pytest
|
5
|
-
import random
|
6
|
-
import requests
|
7
|
-
import socket
|
8
|
-
import string
|
9
|
-
import struct
|
10
|
-
import subprocess
|
11
|
-
|
12
|
-
|
13
|
-
@pytest.fixture
|
14
|
-
def resolver():
|
15
|
-
"""
|
16
|
-
Return a resolver object against the configured powerdns server
|
17
|
-
"""
|
18
|
-
resolver = dns.resolver.Resolver(configure=False)
|
19
|
-
resolver.nameservers = ['127.0.0.1']
|
20
|
-
resolver.port = 5300
|
21
|
-
|
22
|
-
return resolver
|
23
|
-
|
24
|
-
|
25
|
-
@pytest.fixture
|
26
|
-
def smart_proxy_url():
|
27
|
-
return 'http://localhost:8000/'
|
28
|
-
|
29
|
-
|
30
|
-
@pytest.fixture
|
31
|
-
def fqdn():
|
32
|
-
return ''.join(random.sample(string.lowercase, 10)) + '.' + 'example.com'
|
33
|
-
|
34
|
-
|
35
|
-
@pytest.fixture
|
36
|
-
def ip():
|
37
|
-
return socket.inet_ntoa(struct.pack("!I", random.randint(1, 2 ** 32)))
|
38
|
-
|
39
|
-
|
40
|
-
def purge_cache(name):
|
41
|
-
subprocess.check_output(['sudo', 'pdns_control', 'purge', name])
|
42
|
-
|
43
|
-
|
44
|
-
def test_forward_dns(resolver, smart_proxy_url, fqdn, ip):
|
45
|
-
response = requests.post(smart_proxy_url + 'dns/',
|
46
|
-
data={'fqdn': fqdn, 'value': ip, 'type': 'A'})
|
47
|
-
response.raise_for_status()
|
48
|
-
|
49
|
-
answer = resolver.query(fqdn, 'A')
|
50
|
-
assert len(answer.rrset.items) == 1
|
51
|
-
assert answer.rrset.items[0].address == ip
|
52
|
-
|
53
|
-
response = requests.delete(smart_proxy_url + 'dns/' + fqdn)
|
54
|
-
response.raise_for_status()
|
55
|
-
|
56
|
-
purge_cache(fqdn)
|
57
|
-
|
58
|
-
with pytest.raises(dns.resolver.NXDOMAIN):
|
59
|
-
resolver.query(fqdn, 'A')
|
60
|
-
|
61
|
-
|
62
|
-
def test_reverse_dns(resolver, smart_proxy_url, fqdn, ip):
|
63
|
-
response = requests.post(smart_proxy_url + 'dns/',
|
64
|
-
data={'fqdn': fqdn, 'value': ip, 'type': 'PTR'})
|
65
|
-
response.raise_for_status()
|
66
|
-
|
67
|
-
name = dns.reversename.from_address(ip)
|
68
|
-
|
69
|
-
answer = resolver.query(name, 'PTR')
|
70
|
-
assert len(answer.rrset.items) == 1
|
71
|
-
assert answer.rrset.items[0].target.to_text() == fqdn + '.'
|
72
|
-
|
73
|
-
response = requests.delete(smart_proxy_url + 'dns/' + name.to_text().rstrip('.'))
|
74
|
-
response.raise_for_status()
|
75
|
-
|
76
|
-
purge_cache(name.to_text())
|
77
|
-
|
78
|
-
with pytest.raises(dns.resolver.NXDOMAIN):
|
79
|
-
resolver.query(name, 'PTR')
|