Skip to content

Commit

Permalink
Merge branch 'release/v1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
qdii committed Sep 13, 2023
2 parents ca9da58 + cbeddaf commit 068d009
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# OVH Reconciler

Updates a DNS zone managed by OVH, using OVH API.

The source of truth is a plain-text file containing one DNS record per line.
If a difference is found between the contents of the file and the current
records, then the OVH API is queried to add or delete records until they match
what is defined in the file.

## Usage

```shell
./ovh_reconciler.py \
--input=dns-zones.txt \
--application_secret=186f21790a66a1c873efa4a1e7720c45c \
--application_key=1b0c24317eba8cdb \
--consumer_key=9f953cd64e5d32233192730ad1cdaaf1 \
--dns_zone=myzone.fr
```

Where dns-zones.txt is a text file containing one record per line, for instance:

```
blog IN A 18.204.249.102
ng IN CNAME nginx
ftp IN CNAME @
ovh IN AAAA 2001:41d0:402:3300::1d20
_dmarc IN TXT ( "v=DMARC1; p=none" )
```

The application secret, application key and consumer key are issued by OVH
when [creating an API token](https://help.ovhcloud.com/csm/en-ca-api-getting-started-ovhcloud-api?id=kb_article_view&sysparm_article=KB0029722#create-your-app-keys).

This script requires the following permissions on `/domain/zone/myzone.fr`: GET
to fetch the current records and compare them with the intent, POST to create
new records and DELETE to remove records.

## Flags

- `--input`: Can be either a path towards a file containing the source of truth
for the DNS zones, or '-' to read from stdio.

- `--verbosity`: Set to 1 to log extra debugging information

- `--dry_run `: Do not add/remove DNS zones, only print to screen what would
be done. Note that OVH API is still queried, but only to read the existing
records.

- `--application_secret`, `--application_key`, `--consumer_key`: values
provided by OVH API upon creating a new token.

## Limitations

Only records of type A, AAAA, CNAME and TXT are supported. Other record types
are ignored.
6 changes: 3 additions & 3 deletions src/ovh_reconciler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
# This regex matches either a double-quote delimited string, or the same
# but wrapped inside parenthesis.
RE_TXT = r'(?:"(?P<txt1>[^"]*)"|\(\s*"(?P<txt2>[^"]*)"\s*\))'
RE_SUBDOMAIN = r'([-.@|a-zA-Z0-9_]+)'
RE_SUBDOMAIN = r'([-.@|a-zA-Z0-9_]+)*'
RE_RECORD_A = r'^\s*' + RE_SUBDOMAIN + r'\s+IN\s+A\s+' + RE_IPV4 + r'\s*$'
RE_RECORD_AAAA = r'^\s*' + RE_SUBDOMAIN + r'\s+IN\s+AAAA\s+' + RE_IPV6 + r'\s*$'
RE_RECORD_CNAME = r'^\s*' + RE_SUBDOMAIN + r'\s+IN\s+CNAME\s+' + RE_SUBDOMAIN + r'\s*$' # pylint: disable=line-too-long
Expand Down Expand Up @@ -173,7 +173,7 @@ def parse_txt_record(line: str) -> Record | None:
target = (result.group('txt1') or '') + (result.group('txt2') or '')
return Record(
type=Type.TXT,
subdomain=result[1],
subdomain=result[1] or '',
target=target,
id=0)

Expand Down Expand Up @@ -247,7 +247,7 @@ def add_record(record: Record, client: ovh.Client) -> int:
fieldType=record.type.name,
subDomain=record.subdomain,
target=record.target)
return record.id
return record['id']


def delete_record(record: Record, client: ovh.Client) -> None:
Expand Down
7 changes: 7 additions & 0 deletions src/ovh_reconciler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def testParseValidLine_ProducesValidTXTRecord(self, line, subdomain, target):
self.assertEqual(record.subdomain, subdomain)
self.assertEqual(record.target, target)

def testParseLineWithNoSubdomain_ProducesValidTXTRecord(self):
"""Tests that a simple line of DNS record produces the right output."""
record = ovh_reconciler.parse_line(' IN TXT "foobar"')
self.assertEqual(record.type, ovh_reconciler.Type.TXT)
self.assertEqual(record.subdomain, '')
self.assertEqual(record.target, 'foobar')

@parameterized.expand([
'', ' ', '\t', '# A 10.0.0.1', 'A 10.0.0.1',
'muffin IN CNAME 10.0.0.1',
Expand Down

0 comments on commit 068d009

Please sign in to comment.