Browse Source

Merge pull request #1289 from octodns/doc-restructuring

readthedocs config
pull/1291/head
Ross McFarland 4 months ago
committed by GitHub
parent
commit
74b8c191d0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
53 changed files with 1794 additions and 928 deletions
  1. +4
    -0
      .changelog/1088e071ae0d4bff859ade49561aad91.md
  2. +2
    -1
      .gitignore
  3. +19
    -0
      .readthedocs.yaml
  4. +1
    -1
      README.md
  5. +0
    -0
      docs/_static/.create_dir
  6. +6
    -13
      docs/conf.py
  7. +0
    -212
      docs/dynamic_records.md
  8. +0
    -33
      docs/examples/README.md
  9. +0
    -103
      docs/geo_records.md
  10. +0
    -92
      docs/index.md
  11. +0
    -59
      docs/processors.md
  12. +0
    -201
      docs/records.md
  13. +12
    -0
      docs/source/api.rst
  14. +72
    -0
      docs/source/api/processor.rst
  15. +5
    -0
      docs/source/api/provider-yaml.rst
  16. +5
    -0
      docs/source/api/provider.rst
  17. +38
    -0
      docs/source/api/records.rst
  18. +5
    -0
      docs/source/api/source.rst
  19. +0
    -0
      docs/source/assets/deploy.png
  20. +0
    -0
      docs/source/assets/noop.png
  21. +0
    -0
      docs/source/assets/octodns-logo.png
  22. +0
    -0
      docs/source/assets/pr.png
  23. +4
    -4
      docs/source/auto_arpa.md
  24. +148
    -0
      docs/source/configuration.rst
  25. +249
    -0
      docs/source/dynamic_records.rst
  26. +47
    -0
      docs/source/examples/README.rst
  27. +4
    -4
      docs/source/examples/basic/README.md
  28. +0
    -0
      docs/source/examples/basic/config/my-domain.com.yaml
  29. +0
    -0
      docs/source/examples/basic/config/octodns.yaml
  30. +0
    -0
      docs/source/examples/basic/config/unused-domain.io.yaml
  31. +0
    -0
      docs/source/examples/basic/requirements.txt
  32. +0
    -0
      docs/source/examples/basic/target/my-domain.com.yaml
  33. +0
    -0
      docs/source/examples/basic/target/unused-domain.io.yaml
  34. +0
    -0
      docs/source/examples/docker-compose.yml
  35. +0
    -0
      docs/source/examples/env.sh
  36. +11
    -11
      docs/source/examples/migrating-to-octodns/README.md
  37. +0
    -0
      docs/source/examples/migrating-to-octodns/config/octodns.yaml
  38. +0
    -0
      docs/source/examples/migrating-to-octodns/populate/my-dumpable.com.yaml
  39. +0
    -0
      docs/source/examples/migrating-to-octodns/populate/octodns.yaml
  40. +0
    -0
      docs/source/examples/migrating-to-octodns/populate/unused-dumpable.com.yaml
  41. +0
    -0
      docs/source/examples/migrating-to-octodns/requirements.txt
  42. +253
    -0
      docs/source/getting-started.rst
  43. +460
    -0
      docs/source/index.rst
  44. +243
    -0
      docs/source/records.rst
  45. +164
    -164
      octodns/provider/yaml.py
  46. +5
    -0
      octodns/source/base.py
  47. +3
    -3
      requirements-dev.txt
  48. +5
    -7
      requirements-docs.txt
  49. +1
    -1
      script/format
  50. +20
    -16
      script/generate-docs
  51. +1
    -1
      script/lint
  52. +4
    -0
      script/update-requirements
  53. +3
    -2
      setup.py

+ 4
- 0
.changelog/1088e071ae0d4bff859ade49561aad91.md View File

@ -0,0 +1,4 @@
---
type: none
---
WIP: major restructuring of docs, README/doc split

+ 2
- 1
.gitignore View File

@ -8,7 +8,8 @@
coverage.xml
dist/
docs/_build/
docs/modules/
docs/source/api/processors/
docs/source/api/records/
.env
env/
examples/migrating-to-octodns/config/


+ 19
- 0
.readthedocs.yaml View File

@ -0,0 +1,19 @@
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.13"
sphinx:
configuration: docs/conf.py
formats:
- epub
- pdf
python:
install:
- requirements: requirements.txt
- requirements: requirements-dev.txt
- requirements: requirements-doc.txt

+ 1
- 1
README.md View File

@ -1,4 +1,4 @@
<img src="https://raw.githubusercontent.com/octodns/octodns/main/docs/logos/octodns-logo.png?" alt="octoDNS Logo" height=251 width=404>
<img src="https://raw.githubusercontent.com/octodns/octodns/main/docs/source/assets/octodns-logo.png?" alt="octoDNS Logo" height=251 width=404>
## DNS as code - Tools for managing DNS across multiple providers


+ 0
- 0
docs/_static/.create_dir View File


+ 6
- 13
docs/conf.py View File

@ -9,13 +9,14 @@ from octodns.__init__ import __version__
### sphinx config ###
project = "octodns"
project = "octoDNS"
copyright = "2017-present" # noqa
author = "Ross McFarland"
release = __version__
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.coverage",
"sphinx.ext.napoleon",
"sphinx.ext.intersphinx",
@ -23,6 +24,8 @@ extensions = [
"sphinx.ext.viewcode",
"myst_parser",
"sphinx_copybutton",
"sphinxcontrib.mermaid",
"sphinx_rtd_theme",
]
@ -69,10 +72,7 @@ todo_include_todos = True
### myst ###
myst_enable_extensions = [
"colon_fence",
"deflist",
]
myst_enable_extensions = ["colon_fence", "deflist"]
myst_heading_anchors = 3
@ -84,13 +84,6 @@ templates_path = ["_templates"]
html_static_path = ["_static"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
### theme ###
# html_theme = "alabaster"
html_theme = "furo"
html_theme_options = {
"source_repository": "https://github.com/octodns/octodns/",
"source_branch": "main",
"source_directory": "docs/",
}
html_theme = "sphinx_rtd_theme"

+ 0
- 212
docs/dynamic_records.md View File

@ -1,212 +0,0 @@
# Dynamic Record Support
Dynamic records provide support for GeoDNS and weighting to records. `A` and `AAAA` are fully supported and reasonably well tested for both Dyn (via Traffic Directors) and Route53. There is preliminary support for `CNAME` records, but caution should be exercised as they have not been thoroughly tested.
Configuring GeoDNS is complex and the details of the functionality vary widely from provider to provider. octoDNS has an opinionated view mostly to give a reasonably consistent behavior across providers which is similar to the overall philosophy and approach of octoDNS itself. It may not fit your needs or use cases, in which case please open an issue for discussion. We expect this functionality to grow and evolve over time as it's more widely used.
## An Annotated Example
```yaml
---
test:
# This is a dynamic record when used with providers that support it
dynamic:
# These are the pools of records that can be referenced and thus used by rules
pools:
apac:
# An optional fallback, if all of the records in this pool fail this pool should be tried
fallback: na
# One or more values for this pool
values:
- value: 1.1.1.1
- value: 2.2.2.2
eu:
fallback: na
values:
- value: 3.3.3.3
# Weight for this value, if omitted the default is 1
weight: 2
- value: 4.4.4.4
weight: 3
na:
# Implicitly goes to the backup pool (below) if all values are failing
# health checks
values:
- value: 5.5.5.5
- value: 6.6.6.6
- value: 7.7.7.7
# Rules that assign queries to pools
rules:
- geos:
# Geos used in matching queries
- AF-ZA
- AS
- OC
# The pool to service the query from
pool: apac
- geos:
# AF-ZA was sent to apac above and the rest of AF else goes to eu here,
# sub-locations (e.g. AF-ZA) should come before their parents (AF.) If a
# more specific geo occured after a general one requests in that
# location would have already matched the previous rule. For the same
# reasons locations may not be repeated in multiple rules.
- AF
- EU
pool: eu
# No geos means match all queries, the final rule should generally be a
# "catch-all", served to any requests that didn't match the preceeding
# rules. The catch-all is the only case where a pool may be re-used.
- pool: na
ttl: 60
type: A
# These values become a non-healthchecked backup/default pool, generally it
# should be a superset of the catch-all pool and include enough capacity to
# try and serve all global requests (with degraded performance.) The main
# case they will come into play is if all dynamic healthchecks are failing,
# either on the service side or if the providers systems are expeiencing
# problems. They will also be used for when the record is pushed to a
# provider that doesn't support dynamic records.
values:
- 3.3.3.3
- 4.4.4.4
- 5.5.5.5
- 6.6.6.6
- 7.7.7.7
```
If you encounter validation errors in dynamic records suggesting best practices that you have specific reasons for not following see [records.md#Lenience](records.md#Lenience) for how to turn the errors into warnings. Doing so is taking on the burden of thoroughly testing and verifying that what you're doing behaves the way you expect. You may well encounter situations where the octoDNS providers and/or the underlying DNS services do not behave as desired.
### Visual Representation of the Rules and Pools
```mermaid
---
title: Visual Representation of the Rules and Pools
---
flowchart LR
query((Query)) --> rule_0[Rule 0<br>AF-ZA<br>AS<br>OC]
rule_0 --no match--> rule_1[Rule 1<br>AF<br>EU]
rule_1 --no match--> rule_2["Rule 2<br>(catch all)"]
rule_0 --match--> pool_apac[Pool apac<br>1.1.1.1<br>2.2.2.2]
pool_apac --fallback--> pool_na
rule_1 --match--> pool_eu["Pool eu<br>3.3.3.3 (2/5)<br>4.4.4.4 (3/5)"]
pool_eu --fallback--> pool_na
rule_2 --> pool_na[Pool na<br>5.5.5.5<br>6.6.6.6<br>7.7.7.7]
pool_na --backup--> values[values<br>3.3.3.3<br>4.4.4.4<br>5.5.5.5<br>6.6.6.6<br>7.7.7.7]
classDef queryColor fill:#3B67A8,color:#ffffff
classDef ruleColor fill:#D8F57A,color:#000000
classDef poolColor fill:#F57261,color:#000000
classDef valueColor fill:#498FF5,color:#000000
class query queryColor
class rule_0,rule_1,rule_2 ruleColor
class pool_apac,pool_eu,pool_na poolColor
class values valueColor
```
### Geo Codes
Geo codes consist of one to three parts depending on the scope of the area being targeted. Examples of these look like:
* 'NA-US-KY' - North America, United States, Kentucky
* 'NA-US' - North America, United States
* 'NA' - North America
The first portion is the continent:
* 'AF': 14, # Continental Africa
* 'AN': 17, # Continental Antarctica
* 'AS': 15, # Continental Asia
* 'EU': 13, # Continental Europe
* 'NA': 11, # Continental North America
* 'OC': 16, # Continental Australia/Oceania
* 'SA': 12, # Continental South America
The second is the two-letter ISO Country Code https://en.wikipedia.org/wiki/ISO_3166-2 and the third is the ISO Country Code Subdivision as per https://en.wikipedia.org/wiki/ISO_3166-2:US. Change the code at the end for the country you are subdividing. Note that these may not always be supported depending on the providers in use.
### Subnets
Dynamic record rules also support subnet targeting in some providers:
```
...
rules:
- geos:
- AS
- OC
subnets:
# Subnets used in matching queries
- 5.149.176.0/24
pool: apac
...
```
## Rule ordering
octoDNS has validations in place to ensure that sources have the rules ordered from the most specific match to the least specific match per the following categories:
1. Subnet-only rules
2. Subnet+Geo rules
3. Geo-only rules
4. Catch-all rule (with no subnet or geo matching)
The first 3 categories are optional, while the last one is mandatory.
Subnet targeting is considered more specific than geo targeting. This means that if there is a subnet rule match as well as a geo rule match, subnet match must take precedence. Provider implementations must ensure this behavior of targeting precedence.
## Health Checks
octoDNS will automatically configure the provider to monitor each IP and check for a 200 response for **https://<ip_address>/_dns**.
These checks can be customized via the `healthcheck` configuration options.
```yaml
---
test:
...
octodns:
healthcheck:
host: my-host-name
path: /dns-health-check
port: 443
protocol: HTTPS
...
```
| Key | Description | Default |
|--|--|--|
| host | FQDN for host header and SNI | - |
| path | path to check | _dns |
| port | port to check | 443 |
| protocol | HTTP/HTTPS/TCP | HTTPS |
Healthchecks can also be skipped for individual pool values. These values can be forced to always-serve or never-serve using the `status` flag.
`status` flag is optional and accepts one of three possible values, `up`/`down`/`obey`, with `obey` being the default:
```yaml
test:
...
dynamic:
pools:
na:
values:
- value: 1.2.3.4
status: down
- value: 2.3.4.5
status: up
- value: 3.4.5.6
# defaults to status: obey
...
```
Support matrix:
* NS1 and Azure DNS support all 3 flag values
* All other dynamic-capable providers only support the default `obey`
See "Health Check Options" in individual provider documentation for customization support.

+ 0
- 33
docs/examples/README.md View File

@ -1,33 +0,0 @@
# Examples
* Getting started with a [basic octoDNS configuration](basic/) - new to octoDNS
this is the place to start. It'll walk you through the main pieces of DNS IaC
with octoDNS including the process of planning and applying changes.
* [Migrating to octoDNS](migrating-to-octodns/) - have an existing DNS setup
you'd like to bring into octoDNS check this example out right after
[basic](basic/). It'll walk you through the steps of using `octodns-dump` to
pull the existing data out of your provider into matching YAML config files on
disk.
This is a WIP and will definitely be rough around the edges, but in the spirit
of not letting perfect get in the way of good enough, or really existing at
all. It's being uploaded as a starting point. PRs/suggestions welcome as are
ideas for other subjects to cover.
## Running PowerDNS
If you'd like to play around with running the examples in this directory
interactively you'll need a target for pushing data to.
[octodns-powerdns](https://github.com/octodns/octodns-powerdns) is the best
stand-alone option for this and the examples directory makes extensive use of
it. There is a [docker-compose.yml](docker-compose.yml) file that should get a
fully functional copy of PowerDNS backed my MySQL with the API enabled along
with other relivant functionality. For any of the examples that refer to the
local PowerDNS instance the following instructions below should get it up and
running.
1. If you haven't already [install docker compose](https://docs.docker.com/compose/install/)
1. If you don't already have a copy of octoDNS checked out run `git clone https://github.com/octodns/octodns.git`
1. In a seperate terminal window or tab
1. cd into the examples directory `cd octodns/examples`
1. Run docker-compose up `docker-compose up`, this will start up MySQL and PowerDNS running them in the foreground with their logs printing to the terminal

+ 0
- 103
docs/geo_records.md View File

@ -1,103 +0,0 @@
# Geo Record Support
Note: Geo DNS records are still supported for the time being, but it is still strongly encouraged that you look at [Dynamic Records](/docs/dynamic_records.md) instead as they are a superset of functionality.
GeoDNS is currently supported for `A` and `AAAA` records on the Dyn (via Traffic Directors) and Route53 providers. Records with geo information pushed to providers without support for them will be managed as non-geo records using the base values.
Configuring GeoDNS is complex and the details of the functionality vary widely from provider to provider. octoDNS has an opinionated view of how GeoDNS should be set up and does its best to map that to each provider's offering in a way that will result in similar behavior. It may not fit your needs or use cases, in which case please open an issue for discussion. We expect this functionality to grow and evolve over time as it's more widely used.
The following is an example of GeoDNS with three entries NA-US-CA, NA-US-NY, OC-AU. octoDNS creates another one labeled 'default' with the details for the actual A record, This default record is the failover record if the monitoring check fails.
```yaml
---
? ''
: type: TXT
value: v=spf1 -all
test:
geo:
NA-US-NY:
- 111.111.111.1
NA-US-CA:
- 111.111.111.2
OC-AU:
- 111.111.111.3
EU:
- 111.111.111.4
ttl: 300
type: A
value: 111.111.111.5
```
The geo labels breakdown based on:
1.
- 'AF': 14, # Continental Africa
- 'AN': 17, # Continental Antarctica
- 'AS': 15, # Continental Asia
- 'EU': 13, # Continental Europe
- 'NA': 11, # Continental North America
- 'OC': 16, # Continental Australia/Oceania
- 'SA': 12, # Continental South America
2. ISO Country Code https://en.wikipedia.org/wiki/ISO_3166-2
3. ISO Country Code Subdivision as per https://en.wikipedia.org/wiki/ISO_3166-2:US (change the code at the end for the country you are subdividing) * these may not always be supported depending on the provider.
So the example is saying:
- North America - United States - New York: gets served an "A" record of 111.111.111.1
- North America - United States - California: gets served an "A" record of 111.111.111.2
- Oceania - Australia: Gets served an "A" record of 111.111.111.3
- Europe: gets an "A" record of 111.111.111.4
- Everyone else gets an "A" record of 111.111.111.5
## Health Checks
octoDNS will automatically set up monitors check for a 200 response for **https://<ip_address>/_dns**.
These checks can be configured by adding a `healthcheck` configuration to the record:
```yaml
---
test:
geo:
AS:
- 1.2.3.4
EU:
- 2.3.4.5
octodns:
healthcheck:
host: my-host-name
path: /dns-health-check
port: 443
protocol: HTTPS
```
| Key | Description | Default |
|--|--|--|
| host | FQDN for host header and SNI | - |
| path | path to check | _dns |
| port | port to check | 443 |
| protocol | HTTP/HTTPS | HTTPS |
### Route53 Healtch Check Options
| Key | Description | Default |
|--|--|--|
| measure_latency | Show latency in AWS console | true |
| request_interval | Healthcheck interval [10\|30] seconds | 10 |
```yaml
---
octodns:
healthcheck:
host: my-host-name
path: /dns-health-check
port: 443
protocol: HTTPS
route53:
healthcheck:
measure_latency: false
request_interval: 30
```

+ 0
- 92
docs/index.md View File

@ -1,92 +0,0 @@
# octodns documentation
```{include} ../README.md
---
end-before: '## Table of Contents'
---
```
______________________________________________________________________
## User documentation
```{toctree}
:caption: Getting Started:
:maxdepth: 1
examples/README.md
examples/basic/README.md
examples/migrating-to-octodns/README.md
records.md
```
```{toctree}
:caption: Guides:
:maxdepth: 1
:glob:
[a-q]*
```
______________________________________________________________________
## Module documentation
```{toctree}
:caption: Providers:
:maxdepth: 2
:glob:
modules/provider/*
```
```{toctree}
:caption: Sources:
:maxdepth: 2
:glob:
modules/source/*
```
```{toctree}
:caption: Records:
:maxdepth: 2
:glob:
modules/record/*
```
```{toctree}
:caption: Processors:
:maxdepth: 2
:glob:
modules/processor/*
```
```{toctree}
:caption: Other modules:
:titlesonly:
:glob:
modules/*
modules/cmds/*
modules/secret/*
```
______________________________________________________________________
## Indices and tables
- {ref}`genindex`
- {ref}`modindex`
```{toctree}
:caption: Project info:
:titlesonly:
info/changelog.md
info/license.md
```

+ 0
- 59
docs/processors.md View File

@ -1,59 +0,0 @@
# octoDNS processors
## Available processors
These are listed in the main [`README`](../README.md#processors)
## Configuring processors
Configuring processors is done in the main config file.
### Defining a processor configuration
This is done under the top-level `processors` key in the octoDNS config file (for example `config.yaml`), as a sibling to the `manager` key.
The `processors` key contains YAML objects, where the key is the name of the processor, and the `class` value within that object refers to the processor name.
For example, to define a provider called `custom_meta` using the [`MetaProcessor`](../octodns/processor/meta.py) in order to extend the default `include_meta` behaviour:
```yaml
manager:
include_meta: false # disable default, basic `meta` records
processors:
custom_meta:
class: octodns.processor.meta.MetaProcessor
record_name: meta
include_time: true
include_uuid: true
include_provider: true
include_version: false
```
**NOTE:** the specific parameters for each processor are only documented within [the code](../octodns/processor/)
### Utilising the processor configuration
#### On **individual** domains
Each domain can utilise the processor independently by adding the name of the defined processor to a `processors` key beneath a `zone`:
```yaml
zones:
example.com.:
source:
- yaml_config
target:
- hetzner
processors:
- custom_meta
```
#### On **all** domains
To utilise the processor on **all** domains automatically, including new domains added to the `zones` config in future then you can add this to the `processors` key under the `manager` section of the configuration:
```yaml
manager:
processors:
- custom_meta
```

+ 0
- 201
docs/records.md View File

@ -1,201 +0,0 @@
# octoDNS records
## Record types
octoDNS supports the following record types:
* `A`
* `AAAA`
* `ALIAS`
* `CAA`
* `CNAME`
* `DNAME`
* `DS`
* `LOC`
* `MX`
* `NAPTR`
* `NS`
* `PTR`
* `SPF`
* `SRV`
* `SSHFP`
* `TLSA`
* `TXT`
* `URLFWD`
Underlying provider support for each of these varies and some providers have extra requirements or limitations. In cases where a record type is not supported by a provider octoDNS will ignore it there and continue to manage the record elsewhere. For example `SSHFP` is supported by Dyn, but not Route53. If your source data includes an SSHFP record octoDNS will keep it in sync on Dyn, but not consider it when evaluating the state of Route53. The best way to find out what types are supported by a provider is to look for its `supports` method. If that method exists the logic will drive which records are supported and which are ignored. If the provider does not implement the method it will fall back to `BaseProvider.supports` which indicates full support.
Adding new record types to octoDNS is relatively straightforward, but will require careful evaluation of each provider to determine whether or not it will be supported and the addition of code in each to handle and test the new type.
## Advanced Record Support (GeoDNS, Weighting)
* [Dynamic Records](/docs/dynamic_records.md) - the preferred method for configuring geo-location, weights, and healthcheck based fallback between pools of services.
* [Geo Records](/docs/geo_records.md) - the original implementation of geo-location based records, now superseded by Dynamic Records (above)
## Config (`YamlProvider`)
octoDNS records and `YamlProvider`'s schema is essentially a 1:1 match. Properties on the objects will match keys in the config.
### Names
Each top-level key in the yaml file is a record name. Two common special cases are the root record `''`, and a wildcard `'*'`.
```yaml
---
'':
type: A
values:
- 1.2.3.4
- 1.2.3.5
'*':
type: CNAME
value: www.example.com.
www:
type: A
values:
- 1.2.3.4
- 1.2.3.5
www.sub:
type: A
values:
- 1.2.3.6
- 1.2.3.7
```
The above config lays out 4 records, `A`s for `example.com.`, `www.example.com.`, and `www.sub.example.com` and a wildcard `CNAME` mapping `*.example.com.` to `www.example.com.`.
### Multiple records
In the above example each name had a single record, but there are cases where a name will need to have multiple records associated with it. This can be accomplished by using a list.
```yaml
---
'':
- type: A
values:
- 1.2.3.4
- 1.2.3.5
- type: MX
values:
- exchange: mx1.example.com.
preference: 10
- exchange: mx2.example.com.
preference: 10
```
### Record data
Each record type has a corresponding set of required data. The easiest way to determine what's required is probably to look at the record object in [`octodns/record/__init__.py`](/octodns/record/__init__.py). You may also utilize `octodns-validate` which will throw errors about what's missing when run.
`type` is required for all records. `ttl` is optional. When TTL is not specified the `YamlProvider`'s default will be used. In any situation where an array of `values` can be used you can opt to go with `value` as a single item if there's only one.
### Lenience
octoDNS is fairly strict in terms of standards compliance and is opinionated in terms of best practices. Examples of the former include SRV record naming requirements and the latter that ALIAS records are constrained to the root of zones. The strictness and support of providers varies so you may encounter existing records that fail validation when you try to dump them or you may even have use cases for which you need to create or preserve records that don't validate. octoDNS's solution to this is the `lenient` flag.
It's best to think of the `lenient` flag as "I know what I'm doing and accept any problems I run across." The main reason being is that some providers may allow the non-compliant setup and others may not. The behavior of the non-compliant records may even vary from one provider to another. Caveat emptor.
#### Record priority for AutoArpa
When multiple A or AAAA records point to the same IP, it is possible to set an optional priority on each record. The records with the lowest priority will have the highest preference when being processed by AutoArpa. The AutoArpa provider will create PTR records in order of preference, up to a set limit defined by the `max_auto_arpa` option in the provider configuration.
```yaml
test:
- type: A
value: 1.2.3.4
octodns:
auto_arpa_priority: 1
```
#### octodns-dump
If you're trying to import a zone into octoDNS config file using `octodns-dump` which fails due to validation errors you can supply the `--lenient` argument to tell octoDNS that you acknowledge that things aren't lining up with its expectations, but you'd like it to go ahead anyway. This will do its best to populate the zone and dump the results out into an octoDNS zone file and include the non-compliant bits. If you go to use that config file octoDNS will again complain about the validation problems. You can correct them in cases where that makes sense, but if you need to preserve the non-compliant records read on for options.
#### Record level lenience
When there are non-compliant records configured in Yaml you can add the following to tell octoDNS to do it's best to proceed with them anyway. If you use `--lenient` above to dump a zone and you'd like to sync it as-is you can mark the problematic records this way.
```yaml
'not-root':
octodns:
lenient: true
type: ALIAS
values: something.else.com.
```
#### Zone level lenience
If you'd like to enable lenience for a whole zone you can do so with the following, thought it's strongly encouraged to mark things at record level when possible. The most common case where things may need to be done at the zone level is when using something other than `YamlProvider` as a source, e.g. syncing from `Route53Provider` to `Ns1Provider` when there are non-compliant records in the zone in Route53.
```yaml
non-compliant-zone.com.:
lenient: true
sources:
- route53
targets:
- ns1
```
#### Restrict Record manipulations
octoDNS currently provides the ability to limit the number of updates/deletes on
DNS records by configuring a percentage of allowed operations as a provider threshold.
If left unconfigured, suitable defaults take over instead. In the below example,
the Dyn provider is configured with limits of 40% on both update and
delete operations over all the records present.
````yaml
dyn:
class: octodns.provider.dyn.DynProvider
update_pcent_threshold: 0.4
delete_pcent_threshold: 0.4
````
Additionally, thresholds can be configured at the zone level. Zone thresholds
take precedence over any provider default or explicit configuration. Zone
thresholds do not have a default.
```yaml
zones:
example.com.:
update_pcent_threshold: 0.2
delete_pcent_threshold: 0.1
```
## Provider specific record types
### Creating and registering
octoDNS has support for provider specific record types through a dynamic type registration system. This functionality is powered by `Route.register_type` and can be used as follows.
```python
class _SpecificValue(object):
...
class SomeProviderSpecificRecord(ValuesMixin, Record):
_type = 'SomeProvider/SPECIFIC'
_value_type = _SpecificValue
Record.register_type(SomeProviderSpecificRecord)
```
Have a look in [octodns.record](/octodns/record/__init__.py) for examples of how records are implemented. `NsRecord` and `_NsValue` are fairly simple examples to start with. You can also take a look at [`Route53Provider`'s `Route53Provider/ALIAS` type](https://github.com/octodns/octodns-route53/blob/main/octodns_route53/record.py).
In general this support is intended for record types that only make sense for a single provider. If multiple providers have a similar record it may make sense to implement it in octoDNS core.
### Naming
By convention the record type should be prefixed with the provider class, e.g. `Route53Provider` followed by a `/` and an all-caps record type name `ALIAS`, e.g. `Route53Provider/ALIAS`.
### YamlProvider support
Once the type is registered `YamlProvider` will automatically gain support for it and they can be included in your zone yaml files.
```yaml
alias:
type: Route53Provider/ALIAS
values:
- name: www
type: A
- name: www
type: AAAA
```

+ 12
- 0
docs/source/api.rst View File

@ -0,0 +1,12 @@
.. _api:
Developer Interface
===================
.. module:: octodns
.. toctree::
:maxdepth: 1
:glob:
/api/*

+ 72
- 0
docs/source/api/processor.rst View File

@ -0,0 +1,72 @@
Processor
=========
Defining a processor configuration
----------------------------------
This is done under the top-level ``processors`` key in the octoDNS config file
(for example ``config.yaml``), as a sibling to the ``manager`` key.
The ``processors`` key contains YAML objects, where the key is the name of the
processor, and the ``class`` value within that object refers to the processor
name.
For example, to define a provider called ``custom_meta`` using the
:py:class:`octodns.processor.meta.MetaProcessor` in order to extend the default
``include_meta`` behaviour::
manager:
include_meta: false # disable default, basic `meta` records
processors:
custom_meta:
class: octodns.processor.meta.MetaProcessor
record_name: meta
include_time: true
include_uuid: true
include_provider: true
include_version: false
Utilising the processor configuration
-------------------------------------
On **individual** domains
.........................
Each domain can utilise the processor independently by adding the name of the
defined processor to a ``processors`` key beneath a ``zone``::
zones:
example.com.:
source:
- yaml_config
target:
- hetzner
processors:
- custom_meta
On **all** domains
..................
To utilise the processor on **all** domains automatically, including new
domains added to the ``zones`` config in future then you can add this to the
``processors`` key under the ``manager`` section of the configuration::
manager:
processors:
- custom_meta
.. currentmodule:: octodns.processor
.. autosummary::
:toctree: processors
base
acme
arpa
filter
meta
ownership
restrict
spf
templating
trailing_dots

+ 5
- 0
docs/source/api/provider-yaml.rst View File

@ -0,0 +1,5 @@
YamlProvider
============
.. automodule:: octodns.provider.yaml
:inherited-members:

+ 5
- 0
docs/source/api/provider.rst View File

@ -0,0 +1,5 @@
Provider
========
.. automodule:: octodns.provider.base
:inherited-members:

+ 38
- 0
docs/source/api/records.rst View File

@ -0,0 +1,38 @@
Records
=======
.. autosummary::
:toctree: records
octodns.record.base
octodns.record.aaaa
octodns.record.alias
octodns.record.a
octodns.record.caa
octodns.record.change
octodns.record.chunked
octodns.record.cname
octodns.record.dname
octodns.record.ds
octodns.record.dynamic
octodns.record.exception
octodns.record.geo_data
octodns.record.geo
octodns.record.https
octodns.record.ip
octodns.record.loc
octodns.record.mx
octodns.record.naptr
octodns.record.ns
octodns.record.ptr
octodns.record.rr
octodns.record.spf
octodns.record.srv
octodns.record.sshfp
octodns.record.subnet
octodns.record.svcb
octodns.record.target
octodns.record.tlsa
octodns.record.txt
octodns.record.uri
octodns.record.urlfwd

+ 5
- 0
docs/source/api/source.rst View File

@ -0,0 +1,5 @@
Source
======
.. automodule:: octodns.source.base
:inherited-members:

docs/assets/deploy.png → docs/source/assets/deploy.png View File


docs/assets/noop.png → docs/source/assets/noop.png View File


docs/logos/octodns-logo.png → docs/source/assets/octodns-logo.png View File


docs/assets/pr.png → docs/source/assets/pr.png View File


docs/auto_arpa.md → docs/source/auto_arpa.md View File


+ 148
- 0
docs/source/configuration.rst View File

@ -0,0 +1,148 @@
Configuration
=============
Basics
------
This document picks up where :doc:`getting-started` and :doc:`records` leave off,
discussing details and less common scenarios.
YamlProvider
------------
:doc:`api/provider-yaml` lays out the options for configuring the most commonly
used source of record data.
Static Zone Config
------------------
In cases where finer grained control is desired and the configuration of
individual zones varies ``zones`` can be an explicit list with each configured
zone listed along with its specific setup. As exemplified below ``alias`` zones
can be useful when two zones are exact copies of each other, with the same
configuration and records. YAML anchors are also helpful to avoid duplication
where zones share config, but not records.::
---
manager:
include_meta: True
max_workers: 2
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
default_ttl: 3600
enforce_order: True
ns1:
class: octodns_ns1.Ns1Provider
api_key: env/NS1_API_KEY
route53:
class: octodns_route53.Route53Provider
access_key_id: env/AWS_ACCESS_KEY_ID
secret_access_key: env/AWS_SECRET_ACCESS_KEY
zones:
example.com.: &dual_target
sources:
- config
targets:
- ns1
- route53
# these have the same setup as example.com., but will have their own files
# in the configuration directory for records.
third.tv.: *dual_target
fourth.tv.: *dual_target
example.net.:
# example.net. is an exact copy of example.com., there will not be an
# example.net.yaml file in the config directory as `alias` includes
# duplicating the records of the aliased zone along with its config.
alias: example.com.
other.com.:
lenient: True
sources:
- config
targets:
- ns1
General Configuration Concepts
------------------------------
``class`` is a special key that tells octoDNS what python class should be
loaded. Any other keys will be passed as configuration values to that
provider. In general any sensitive or frequently rotated values should come
from environmental variables. When octoDNS sees a value that starts with
``env/`` it will look for that value in the process's environment and pass the
result along.
Further information can be found in the docstring of each source and provider
class.
The ``include_meta`` key in the ``manager`` section of the config controls the
creation of a TXT record at the root of a zone that is managed by octoDNS. If
set to ``True``, octoDNS will create a TXT record for the root of the zone with
the value ``provider=<target-provider>``. If not specified, the default value for
``include_meta`` is ``False``.
The ``max_workers`` key in the ``manager`` section of the config enables threading
to parallelize the planning portion of the sync.
``lenient``
-----------
``lenient`` mostly focuses on the details of ``Record``s and standards
compliance. When set to ``true`` octoDNS will allow non-compliant
configurations & values where possible. For example CNAME values that don't end
with a ``.``, label length restrictions, and invalid geo codes on ``dynamic``
records. When in lenient mode octoDNS will log validation problems at
``WARNING`` and try and continue with the configuration or source data as it
exists. See Lenience_ for more information on the concept and how it can be
configured.
.. _Lenience: records.rst#lenience
``strict_supports``
-------------------
``strict_supports`` is a ``Provider`` level parameter that comes into play when
a provider has been asked to create a record that it is unable to support. The
simplest case of this would be record type, e.g. ``SSHFP`` not being supported
by ``AzureProvider``. If such a record is passed to an ``AzureProvider`` as a
target the provider will take action based on the ``strict_supports``. When
``true`` it will throw an exception saying that it's unable to create the
record, when set to ``false`` it will log at ``WARNING`` with information about
what it's unable to do and how it is attempting to work around it. Other
examples of things that cannot be supported would be ``dynamic`` records on a
provider that only supports simple or the lack of support for specific geos in
a provider, e.g. Route53Provider does not support ``NA-CA-*``.
It is worth noting that these errors will happen during the plan phase of
things so that problems will be visible without having to make changes.
As of octoDNS 1.x ``strict_supports`` is on by default. You have the choice to
set ``strict_supports=false`` on a per provider basis to request that things warn
and continue in a best-effort fashion.
Configuring ``strict_supports``
...............................
The ``strict_supports`` parameter is available on all providers and can be
configured in YAML as follows::
providers:
someprovider:
class: whatever.TheProvider
...
strict_supports: true
.. _automatic-ptr-generation:
Automatic PTR generation
------------------------
octoDNS supports automatically generating PTR records from the ``A``/``AAAA``
records it manages. For more information see the :doc:`auto_arpa`
documentation.

+ 249
- 0
docs/source/dynamic_records.rst View File

@ -0,0 +1,249 @@
Dynamic Record Support
======================
Dynamic records provide support for GeoDNS and weighting to records. ``A`` and
``AAAA`` are fully supported and reasonably well tested for both Dyn (via
Traffic Directors) and Route53. There is preliminary support for ``CNAME``
records, but caution should be exercised as they have not been thoroughly
tested.
Configuring GeoDNS is complex and the details of the functionality vary widely
from provider to provider. octoDNS has an opinionated view mostly to give a
reasonably consistent behavior across providers which is similar to the overall
philosophy and approach of octoDNS itself. It may not fit your needs or use
cases, in which case please open an issue for discussion. We expect this
functionality to grow and evolve over time as it's more widely used.
An Annotated Example
--------------------
::
---
test:
# This is a dynamic record when used with providers that support it
dynamic:
# These are the pools of records that can be referenced and thus used by rules
pools:
apac:
# An optional fallback, if all of the records in this pool fail this pool should be tried
fallback: na
# One or more values for this pool
values:
- value: 1.1.1.1
- value: 2.2.2.2
eu:
fallback: na
values:
- value: 3.3.3.3
# Weight for this value, if omitted the default is 1
weight: 2
- value: 4.4.4.4
weight: 3
na:
# Implicitly goes to the backup pool (below) if all values are failing
# health checks
values:
- value: 5.5.5.5
- value: 6.6.6.6
- value: 7.7.7.7
# Rules that assign queries to pools
rules:
- geos:
# Geos used in matching queries
- AF-ZA
- AS
- OC
# The pool to service the query from
pool: apac
- geos:
# AF-ZA was sent to apac above and the rest of AF else goes to eu here,
# sub-locations (e.g. AF-ZA) should come before their parents (AF.) If a
# more specific geo occured after a general one requests in that
# location would have already matched the previous rule. For the same
# reasons locations may not be repeated in multiple rules.
- AF
- EU
pool: eu
# No geos means match all queries, the final rule should generally be a
# "catch-all", served to any requests that didn't match the preceeding
# rules. The catch-all is the only case where a pool may be re-used.
- pool: na
ttl: 60
type: A
# These values become a non-healthchecked backup/default pool, generally it
# should be a superset of the catch-all pool and include enough capacity to
# try and serve all global requests (with degraded performance.) The main
# case they will come into play is if all dynamic healthchecks are failing,
# either on the service side or if the providers systems are expeiencing
# problems. They will also be used for when the record is pushed to a
# provider that doesn't support dynamic records.
values:
- 3.3.3.3
- 4.4.4.4
- 5.5.5.5
- 6.6.6.6
- 7.7.7.7
If you encounter validation errors in dynamic records suggesting best practices
that you have specific reasons for not following see :doc:records#lenience for
how to turn the errors into warnings. Doing so is taking on the burden of
thoroughly testing and verifying that what you're doing behaves the way you
expect. You may well encounter situations where the octoDNS providers and/or
the underlying DNS services do not behave as desired.
Visual Representation of the Rules and Pools
............................................
.. mermaid::
flowchart LR
query((Query)) --> rule_0[Rule 0<br>AF-ZA<br>AS<br>OC]
rule_0 --no match--> rule_1[Rule 1<br>AF<br>EU]
rule_1 --no match--> rule_2["Rule 2<br>(catch all)"]
rule_0 --match--> pool_apac[Pool apac<br>1.1.1.1<br>2.2.2.2]
pool_apac --fallback--> pool_na
rule_1 --match--> pool_eu["Pool eu<br>3.3.3.3 (2/5)<br>4.4.4.4 (3/5)"]
pool_eu --fallback--> pool_na
rule_2 --> pool_na[Pool na<br>5.5.5.5<br>6.6.6.6<br>7.7.7.7]
pool_na --backup--> values[values<br>3.3.3.3<br>4.4.4.4<br>5.5.5.5<br>6.6.6.6<br>7.7.7.7]
classDef queryColor fill:#3B67A8,color:#ffffff
classDef ruleColor fill:#D8F57A,color:#000000
classDef poolColor fill:#F57261,color:#000000
classDef valueColor fill:#498FF5,color:#000000
class query queryColor
class rule_0,rule_1,rule_2 ruleColor
class pool_apac,pool_eu,pool_na poolColor
class values valueColor
Geo Codes
.........
Geo codes consist of one to three parts depending on the scope of the area
being targeted. Examples of these look like:
* 'NA-US-KY' - North America, United States, Kentucky
* 'NA-US' - North America, United States
* 'NA' - North America
The first portion is the continent:
* 'AF': 14, # Continental Africa
* 'AN': 17, # Continental Antarctica
* 'AS': 15, # Continental Asia
* 'EU': 13, # Continental Europe
* 'NA': 11, # Continental North America
* 'OC': 16, # Continental Australia/Oceania
* 'SA': 12, # Continental South America
The second is the two-letter ISO Country Code
https://en.wikipedia.org/wiki/ISO_3166-2 and the third is the ISO Country Code
Subdivision as per https://en.wikipedia.org/wiki/ISO_3166-2:US. Change the code
at the end for the country you are subdividing. Note that these may not always
be supported depending on the providers in use.
Subnets
.......
Dynamic record rules also support subnet targeting in some providers::
...
rules:
- geos:
- AS
- OC
subnets:
# Subnets used in matching queries
- 5.149.176.0/24
pool: apac
...
Rule ordering
-------------
octoDNS has validations in place to ensure that sources have the rules ordered
from the most specific match to the least specific match per the following
categories:
1. Subnet-only rules
2. Subnet+Geo rules
3. Geo-only rules
4. Catch-all rule (with no subnet or geo matching)
The first 3 categories are optional, while the last one is mandatory.
Subnet targeting is considered more specific than geo targeting. This means
that if there is a subnet rule match as well as a geo rule match, subnet match
must take precedence. Provider implementations must ensure this behavior of
targeting precedence.
Health Checks
-------------
octoDNS will automatically configure the provider to monitor each IP and check
for a 200 response for **https://<ip_address>/_dns**.
These checks can be customized via the ``healthcheck`` configuration options.::
---
test:
...
octodns:
healthcheck:
host: my-host-name
path: /dns-health-check
port: 443
protocol: HTTPS
...
.. list-table::
:header-rows: 1
* - Key
- Description
- Default
* - host
- FQDN for host header and SNI
-
* - path
- path to check
- _dns
* - port
- port to check
- 443
* - protocol
- HTTP/HTTPS/TCP
- HTTPS
Healthchecks can also be skipped for individual pool values. These values can
be forced to always-serve or never-serve using the ``status`` flag.
``status`` flag is optional and accepts one of three possible values,
``up``/``down``/``obey``, with ``obey`` being the default::
test:
...
dynamic:
pools:
na:
values:
- value: 1.2.3.4
status: down
- value: 2.3.4.5
status: up
- value: 3.4.5.6
# defaults to status: obey
...
Support matrix:
* NS1 and Azure DNS support all 3 flag values
* All other dynamic-capable providers only support the default ``obey``
See "Health Check Options" in individual provider documentation for
customization support.

+ 47
- 0
docs/source/examples/README.rst View File

@ -0,0 +1,47 @@
Examples
========
* Getting started with a [basic octoDNS configuration](basic/README.md) - new
to octoDNS this is the place to start. It'll walk you through the main pieces
of DNS IaC with octoDNS including the process of planning and applying changes.
* [Migrating to octoDNS](migrating-to-octodns/README.md) - have an existing DNS
setup you'd like to bring into octoDNS check this example out right after
[basic](basic/README.md). It'll walk you through the steps of using
``octodns-dump`` to pull the existing data out of your provider into matching
YAML config files on disk.
This is a WIP and will definitely be rough around the edges, but in the spirit
of not letting perfect get in the way of good enough, or really existing at
all. It's being uploaded as a starting point. PRs/suggestions welcome as are
ideas for other subjects to cover.
.. _running-powerdns:
Running PowerDNS
----------------
If you'd like to play around with running the examples in this directory
interactively you'll need a target for pushing data to. `octodns-powerdns`_ is
the best stand-alone option for this and the examples directory makes extensive
use of it. There is a [docker-compose.yml](docker-compose.yml) file that should
get a fully functional copy of PowerDNS backed my MySQL with the API enabled
along with other relivant functionality. For any of the examples that refer to
the local PowerDNS instance the following instructions below should get it up
and running.
.. _octodns-powerdns: https://github.com/octodns/octodns-powerdns
1. If you haven't already `install docker compose`_
1. If you don't already have a copy of octoDNS checked out run ``git clone https://github.com/octodns/octodns.git``
1. In a seperate terminal window or tab
1. cd into the examples directory ``cd octodns/examples``
1. Run docker-compose up ``docker-compose up``, this will start up MySQL and PowerDNS running them in the foreground with their logs printing to the terminal
.. _install docker compose: https://docs.docker.com/compose/install/
.. toctree::
:maxdepth: 1
basic/README.md
migrating-to-octodns/README.md

docs/examples/basic/README.md → docs/source/examples/basic/README.md View File


docs/examples/basic/config/my-domain.com.yaml → docs/source/examples/basic/config/my-domain.com.yaml View File


docs/examples/basic/config/octodns.yaml → docs/source/examples/basic/config/octodns.yaml View File


docs/examples/basic/config/unused-domain.io.yaml → docs/source/examples/basic/config/unused-domain.io.yaml View File


docs/examples/basic/requirements.txt → docs/source/examples/basic/requirements.txt View File


docs/examples/basic/target/my-domain.com.yaml → docs/source/examples/basic/target/my-domain.com.yaml View File


docs/examples/basic/target/unused-domain.io.yaml → docs/source/examples/basic/target/unused-domain.io.yaml View File


docs/examples/docker-compose.yml → docs/source/examples/docker-compose.yml View File


docs/examples/env.sh → docs/source/examples/env.sh View File


docs/examples/migrating-to-octodns/README.md → docs/source/examples/migrating-to-octodns/README.md View File


docs/examples/migrating-to-octodns/config/octodns.yaml → docs/source/examples/migrating-to-octodns/config/octodns.yaml View File


docs/examples/migrating-to-octodns/populate/my-dumpable.com.yaml → docs/source/examples/migrating-to-octodns/populate/my-dumpable.com.yaml View File


docs/examples/migrating-to-octodns/populate/octodns.yaml → docs/source/examples/migrating-to-octodns/populate/octodns.yaml View File


docs/examples/migrating-to-octodns/populate/unused-dumpable.com.yaml → docs/source/examples/migrating-to-octodns/populate/unused-dumpable.com.yaml View File


docs/examples/migrating-to-octodns/requirements.txt → docs/source/examples/migrating-to-octodns/requirements.txt View File


+ 253
- 0
docs/source/getting-started.rst View File

@ -0,0 +1,253 @@
Getting Started
===============
Workspace
---------
Running through the following commands will install the latest release of
octoDNS and set up a place for your config files to live. To determine if
provider specific requirements are necessary see the :ref:`provider-list`
below.::
$ mkdir dns
$ cd dns
$ python -m venv env
...
$ source env/bin/activate
# provider-specific-requirements would be things like: octodns-route53 octodns-azure
$ pip install octodns <provider-specific-requirements>
$ mkdir config
Installing a specific commit SHA
................................
If you'd like to install a version that has not yet been released in a
repeatable/safe manner you can do the following. In general octoDNS is fairly
stable in between releases thanks to the plan and apply process, but care
should be taken regardless.::
$ pip install -e git+https://git@github.com/octodns/octodns.git@<SHA>#egg=octodns
Config
------
We start by creating a config file to tell octoDNS about our providers and the
zone(s) we want it to manage. Below we're setting up a ``YamlProvider`` to
source records from our config files and both a ``Route53Provider`` and
``DynProvider`` to serve as the targets for those records. You can have any
number of zones set up and any number of sources of data and targets for
records for each. You can also have multiple config files, that make use of
separate accounts and each manage a distinct set of zones. A good example of
this this might be ``./config/staging.yaml`` & ``./config/production.yaml``.
We'll focus on a ``config/production.yaml``.
.. _dynamic-zone-config:
Dynamic Zone Config
...................
octoDNS supports dynamically building the list of zones it will work with when
source providers support it. The most common use of this would be with
``YamlProvider`` and a single dynamic entry to in effect use the files that
exist in the provider's directory as the source of truth. Other providers may
support the ``list_zones`` method and be available to populate zones
dynamically as well. This can be especially useful when using ``octodns-dump``
to create an initial setup from an existing provider.
An example config would look something like::
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
default_ttl: 3600
enforce_order: True
ns1:
class: octodns_ns1.Ns1Provider
api_key: env/NS1_API_KEY
route53:
class: octodns_route53.Route53Provider
access_key_id: env/AWS_ACCESS_KEY_ID
secret_access_key: env/AWS_SECRET_ACCESS_KEY
zones:
# This is a dynamic zone config. The source(s), here `config`, will be
# queried for a list of zone names and each will dynamically be set up to
# match the dynamic entry.
'*':
sources:
- config
targets:
- ns1
- route53
Quick Example Record
....................
Now that we have something to tell octoDNS about our providers & zones we need
to tell it about our records. We'll keep it simple for now and just create a
single ``A`` record at the top-level of the domain.
``config/example.com.yaml``::
---
'':
ttl: 60
type: A
values:
- 1.2.3.4
- 1.2.3.5
Further information can be found in :doc:`records` documentation.
Noop
----
We're ready to do a dry-run with our new setup to see what changes it would
make. Since we're pretending here we'll act like there are no existing records
for ``example.com.`` in our accounts on either provider.::
$ octodns-sync --config-file=./config/production.yaml
...
********************************************************************************
* example.com.
********************************************************************************
* route53 (Route53Provider)
* Create <ARecord A 60, example.com., [u'1.2.3.4', '1.2.3.5']>
* Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0
* dyn (DynProvider)
* Create <ARecord A 60, example.com., [u'1.2.3.4', '1.2.3.5']>
* Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0
********************************************************************************
...
There will be other logging information presented on the screen, but successful
runs of sync will always end with a summary like the above for any providers &
zones with changes. If there are no changes a message saying so will be printed
instead. Above we're creating a new zone in both providers so they show the
same change, but that doesn't always have to be the case. If, to start, one of
them had a different state, you would see the changes octoDNS intends to make
to sync them up.
Making changes
--------------
**WARNING**: octoDNS assumes ownership of any domain you point it to. When you
tell it to act it will do whatever is necessary to try and match up states
including deleting any unexpected records. Be careful when playing around with
octoDNS. It's best to experiment with a fake zone or one without any data that
matters until you're comfortable with the system.
Now it's time to tell octoDNS to make things happen. We'll invoke it again with
the same options and add a ``--doit`` on the end to tell it this time we
actually want it to try and make the specified changes.::
$ octodns-sync --config-file=./config/production.yaml --doit
...
The output here would be the same as before with a few more log lines at the
end as it makes the actual changes. After which the config in Route53 and Dyn
should match what's in the yaml file.
Workflow
--------
In the above case we manually ran octoDNS from the command line. That works and
it's better than heading into the provider GUIs and making changes by clicking
around, but octoDNS is designed to be run as part of a deploy process. The
implementation details are well beyond the scope of this README, but here is an
example of the workflow we use at GitHub. It follows the way `GitHub itself is
branch deployed`_.
.. _GitHub itself is branch deployed: https://githubengineering.com/deploying-branches-to-github-com/
The first step is to create a PR with your changes.
.. image:: assets/pr.png
:alt: GitHub user interface of a pull request
Assuming the code tests and config validation statuses are green the next step
is to do a noop deploy and verify that the changes octoDNS plans to make are
the ones you expect.
.. image:: assets/noop.png
:alt: Output of a noop deployment command
After that comes a set of reviews. One from a teammate who should have full
context on what you're trying to accomplish and visibility into the changes
you're making to do it. The other is from a member of the team here at GitHub
that owns DNS, mostly as a sanity check and to make sure that best practices
are being followed. As much of that as possible is baked into
``octodns-validate``.
After the reviews it's time to branch deploy the change.
.. image:: assets/deploy.png
:alt: Output of a deployment command
If that goes smoothly, you again see the expected changes, and verify them with
``dig`` and/or ``octodns-report`` you're good to hit the merge button. If there
are problems you can quickly do a ``.deploy dns/main`` to go back to the
previous state.
Other Uses
----------
Syncing between providers
.........................
While the primary use-case is to sync a set of yaml config files up to one or
more DNS providers, octoDNS has been built in such a way that you can easily
source and target things arbitrarily. As a quick example the config below would
sync ``githubtest.net.`` from Route53 to Dyn.::
---
providers:
route53:
class: octodns.provider.route53.Route53Provider
access_key_id: env/AWS_ACCESS_KEY_ID
secret_access_key: env/AWS_SECRET_ACCESS_KEY
dyn:
class: octodns.provider.dyn.DynProvider
customer: env/DYN_CUSTOMER
username: env/DYN_USERNAME
password: env/DYN_PASSWORD
zones:
githubtest.net.:
sources:
- route53
targets:
- dyn
Dynamic sources
...............
Internally we use custom sources to create records based on dynamic data that
changes frequently without direct human intervention. An example of that might
look something like the following. For hosts this mechanism is janitorial, run
periodically, making sure the correct records exist as long as the host is
alive and ensuring they are removed after the host is destroyed. The host
provisioning and destruction processes do the actual work to create and destroy
the records.::
---
providers:
gpanel-site:
class: github.octodns.source.gpanel.GPanelProvider
host: 'gpanel.site.github.foo'
token: env/GPANEL_SITE_TOKEN
powerdns-site:
class: octodns.provider.powerdns.PowerDnsProvider
host: "internal-dns.site.github.foo"
api_key: env/POWERDNS_SITE_API_KEY
zones:
hosts.site.github.foo.:
sources:
- gpanel-site
targets:
- powerdns-site

+ 460
- 0
docs/source/index.rst View File

@ -0,0 +1,460 @@
.. image:: assets/octodns-logo.png
:alt: GitHub user interface of a pull request
DNS as Code
===========
Tools for managing DNS across multiple providers
------------------------------------------------
In the vein of infrastructure as code octoDNS provides a set of tools &
patterns that make it easy to manage your DNS records across multiple
providers. The resulting config can live in a repository and be deployed just
like the rest of your code, maintaining a clear history and using your existing
review & workflow.
The architecture is pluggable and the tooling is flexible to make it applicable
to a wide variety of use-cases. Effort has been made to make adding new
providers as easy as possible. In the simple case that involves writing of a
single class and a couple hundred lines of code, most of which is translating
between the provider's schema and octoDNS's.
Documentation
-------------
.. toctree::
:maxdepth: 1
getting-started.rst
records.md
configuration.rst
dynamic_records.rst
auto_arpa.rst
examples/README.rst
api.rst
.. _provider-list:
Providers
---------
The table below lists the providers octoDNS supports. They are maintained in
their own repositories and released as independent modules.
.. list-table::
:header-rows: 1
* - Provider
- Module
- Notes
* - /etc/hosts
- `octodns_etchosts`_
-
* - `Akamai Edge DNS`_
- `octodns_edgedns`_
-
* - `Amazon Route 53`_
- `octodns_route53`_
-
* - `AutoDNS`_
- `octodns_autodns`_
-
* - `Azion DNS`_
- `octodns_azion`_
-
* - `Azure DNS`_
- `octodns_azure`_
-
* - `BIND, AXFR, RFC-2136`_
- `octodns_bind`_
-
* - `Bunny DNS`_
- `octodns_bunny`_
-
* - `Cloudflare DNS`_
- `octodns_cloudflare`_
-
* - `ClouDNS`_
- `octodns_cloudns`_
-
* - `Constellix`_
- `octodns_constellix`_
-
* - `deSEC`_
- `octodns_desec`_
-
* - `DigitalOcean`_
- `octodns_digitalocean`_
-
* - `DNS Made Easy`_
- `octodns_dnsmadeeasy`_
-
* - `DNSimple`_
- `octodns_dnsimple`_
-
* - `Dyn`_ [deprecated]
- `octodns_dyn`_
-
* - `easyDNS`_
- `octodns_easydns`_
-
* - `EdgeCenter DNS`_
- `octodns_edgecenter`_
-
* - `Fastly`_
- `Financial-Times/octodns-fastly`_
-
* - `G-Core Labs DNS`_
- `octodns_gcore`_
-
* - `Gandi`_
- `octodns_gandi`_
-
* - `Google Cloud DNS`_
- `octodns_googlecloud`_
-
* - `Hetzner DNS`_
- `octodns_hetzner`_
-
* - `Infoblox`_
- `asyncon/octoblox`_
-
* - `Infomaniak`_
- `octodns_infomaniak`_
-
* - `Lexicon`_
- `dns-lexicon/dns-lexicon`_
-
* - `Mythic Beasts DNS`_
- `octodns_mythicbeasts`_
-
* - `NetBox-DNS Plugin`_
- `olofvndrhr/octodns-netbox-dns`_
-
* - `NS1`_
- `octodns_ns1`_
-
* - `OVHcloud DNS`_
- `octodns_ovh`_
-
* - `Pi-hole`_
- `jvoss/octodns-pihole`_
-
* - `PowerDNS`_
- `octodns_powerdns`_
-
* - `Rackspace`_
- `octodns_rackspace`_
-
* - `Scaleway`_
- `octodns_scaleway`_
-
* - `Selectel`_
- `octodns_selectel`_
-
* - `SPF Value Management`_
- `octodns_spf`_
-
* - `TransIP`_
- `octodns_transip`_
-
* - `UltraDNS`_
- `octodns_ultra`_
-
* - `YamlProvider`_
- built-in
- Supports all record types and core functionality
* - Zonefile
- `kompetenzbolzen/octodns-custom-provider`_
-
.. _octodns_etchosts: https://github.com/octodns/octodns-etchosts/
.. _Akamai Edge DNS: https://www.akamai.com/products/edge-dns
.. _octodns_edgedns: https://github.com/octodns/octodns-edgedns/
.. _Amazon Route 53: https://aws.amazon.com/route53/
.. _octodns_route53: https://github.com/octodns/octodns-route53
.. _AutoDNS: https://www.internetx.com/autodns/
.. _octodns_autodns: https://github.com/octodns/octodns-autodns
.. _Azion DNS: https://www.azion.com/en/products/edge-dns/
.. _octodns_azion: https://github.com/aziontech/octodns-azion/
.. _Azure DNS: https://azure.microsoft.com/en-us/services/dns/
.. _octodns_azure: https://github.com/octodns/octodns-azure/
.. _BIND, AXFR, RFC-2136: https://www.isc.org/bind/
.. _octodns_bind: https://github.com/octodns/octodns-bind/
.. _Bunny DNS: https://bunny.net/dns/
.. _octodns_bunny: https://github.com/Relkian/octodns-bunny
.. _Cloudflare DNS: https://www.cloudflare.com/dns/
.. _octodns_cloudflare: https://github.com/octodns/octodns-cloudflare/
.. _ClouDNS: https://www.cloudns.net/
.. _octodns_cloudns: https://github.com/ClouDNS/octodns_cloudns
.. _Constellix: https://constellix.com/
.. _octodns_constellix: https://github.com/octodns/octodns-constellix/
.. _deSEC: https://desec.io/
.. _octodns_desec: https://github.com/rootshell-labs/octodns-desec
.. _DigitalOcean: https://docs.digitalocean.com/products/networking/dns/
.. _octodns_digitalocean: https://github.com/octodns/octodns-digitalocean/
.. _DNS Made Easy: https://dnsmadeeasy.com/
.. _octodns_dnsmadeeasy: https://github.com/octodns/octodns-dnsmadeeasy/
.. _DNSimple: https://dnsimple.com/
.. _octodns_dnsimple: https://github.com/octodns/octodns-dnsimple/
.. _Dyn: https://www.oracle.com/cloud/networking/dns/
.. _octodns_dyn: https://github.com/octodns/octodns-dyn/
.. _easyDNS: https://easydns.com/
.. _octodns_easydns: https://github.com/octodns/octodns-easydns/
.. _EdgeCenter DNS: https://edgecenter.ru/dns/
.. _octodns_edgecenter: https://github.com/octodns/octodns-edgecenter/
.. _Fastly: https://www.fastly.com/de/
.. _Financial-Times/octodns-fastly: https://github.com/Financial-Times/octodns-fastly
.. _G-Core Labs DNS: https://gcorelabs.com/dns/
.. _octodns_gcore: https://github.com/octodns/octodns-gcore/
.. _Gandi: https://www.gandi.net/en-US/domain/dns
.. _octodns_gandi: https://github.com/octodns/octodns-gandi/
.. _Google Cloud DNS: https://cloud.google.com/dns
.. _octodns_googlecloud: https://github.com/octodns/octodns-googlecloud/
.. _Hetzner DNS: https://www.hetzner.com/dns-console
.. _octodns_hetzner: https://github.com/octodns/octodns-hetzner/
.. _Infoblox: https://www.infoblox.com/
.. _asyncon/octoblox: https://github.com/asyncon/octoblox
.. _Infomaniak: https://www.infomaniak.com/
.. _octodns_infomaniak: https://github.com/M0NsTeRRR/octodns-infomaniak
.. _Lexicon: https://dns-lexicon.github.io/dns-lexicon/#
.. _dns-lexicon/dns-lexicon: https://github.com/dns-lexicon/dns-lexicon
.. _Mythic Beasts DNS: https://www.mythic-beasts.com/support/hosting/dns
.. _octodns_mythicbeasts: https://github.com/octodns/octodns-mythicbeasts/
.. _NetBox-DNS Plugin: https://github.com/peteeckel/netbox-plugin-dns
.. _olofvndrhr/octodns-netbox-dns: https://github.com/olofvndrhr/octodns-netbox-dns
.. _NS1: https://ns1.com/products/managed-dns
.. _octodns_ns1: https://github.com/octodns/octodns-ns1/
.. _OVHcloud DNS: https://www.ovhcloud.com/en/domains/dns-subdomain/
.. _octodns_ovh: https://github.com/octodns/octodns-ovh/
.. _Pi-hole: https://pi-hole.net/
.. _jvoss/octodns-pihole: https://github.com/jvoss/octodns-pihole
.. _PowerDNS: https://www.powerdns.com/
.. _octodns_powerdns: https://github.com/octodns/octodns-powerdns/
.. _Rackspace: https://www.rackspace.com/library/what-is-dns
.. _octodns_rackspace: https://github.com/octodns/octodns-rackspace/
.. _Scaleway: https://www.scaleway.com/en/dns/
.. _octodns_scaleway: https://github.com/scaleway/octodns-scaleway
.. _Selectel: https://selectel.ru/en/services/additional/dns/
.. _octodns_selectel: https://github.com/octodns/octodns-selectel/
.. _SPF Value Management: https://github.com/octodns/octodns-spf
.. _octodns_spf: https://github.com/octodns/octodns-spf/
.. _TransIP: https://www.transip.eu/knowledgebase/entry/155-dns-and-nameservers/
.. _octodns_transip: https://github.com/octodns/octodns-transip/
.. _UltraDNS: https://vercara.com/authoritative-dns
.. _octodns_ultra: https://github.com/octodns/octodns-ultra/
.. _YamlProvider: /octodns/provider/yaml.py
.. _kompetenzbolzen/octodns-custom-provider: https://github.com/kompetenzbolzen/octodns-custom-provider
Sources
-------
Similar to providers, but can only serve to populate records into a zone,
cannot be synced to.
.. list-table::
:header-rows: 1
:widths: 50 50
* - Source/Module
- Notes
* - `AxfrSource (BIND)`_
-
* - `DDNS Source`_
-
* - `EnvVarSource`_
- read-only environment variable injection
* - `Lexicon Source`_
-
* - `Netbox Source`_
-
* - `PHPIPAM Source`_
-
* - `TinyDnsFileSource`_
-
* - `ZoneFileSource`_
-
.. _AxfrSource (BIND): https://github.com/octodns/octodns-bind/
.. _DDNS Source: https://github.com/octodns/octodns-ddns
.. _EnvVarSource: /octodns/source/envvar.py
.. _Lexicon Source: https://github.com/doddo/octodns-lexicon
.. _Netbox Source: https://github.com/sukiyaki/octodns-netbox
.. _PHPIPAM Source: https://github.com/kompetenzbolzen/octodns-custom-provider
.. _TinyDnsFileSource: /octodns/source/tinydns.py
.. _ZoneFileSource: https://github.com/octodns/octodns-bind/
Processors
----------
.. list-table::
:header-rows: 1
* - Processor
- Description
* - `AcmeManagingProcessor`_
- Useful when processes external to octoDNS are managing acme challenge DNS records, e.g. LetsEncrypt
* - `AutoArpa`_
- See :ref:`automatic-ptr-generation`
* - `EnsureTrailingDots`_
- Processor that ensures ALIAS, CNAME, DNAME, MX, NS, PTR, and SRVs have trailing dots
* - `ExcludeRootNsChanges`_
- Filter that errors or warns on planned root/APEX NS records changes.
* - `IgnoreRootNsFilter`_
- Filter that IGNORES root/APEX NS records and prevents octoDNS from trying to manage them (where supported.)
* - `MetaProcessor`_
- Adds a special meta record with timing, UUID, providers, and/or version to aid in debugging and monitoring.
* - `NameAllowlistFilter`_
- Filter that ONLY manages records that match specified naming patterns, all others will be ignored
* - `NameRejectlistFilter`_
- Filter that IGNORES records that match specified naming patterns, all others will be managed
* - `ValueAllowlistFilter`_
- Filter that ONLY manages records that match specified value patterns based on `rdata_text`, all others will be ignored
* - `ValueRejectlistFilter`_
- Filter that IGNORES records that match specified value patterns based on `rdata_text`, all others will be managed
* - `OwnershipProcessor`_
- Processor that implements ownership in octoDNS so that it can manage only the records in a zone in sources and will ignore all others.
* - `SpfDnsLookupProcessor`_
- Processor that checks SPF values for violations of DNS query limits
* - `TtlRestrictionFilter`_
- Processor that restricts the allow TTL values to a specified range or list of specific values
* - `TypeAllowlistFilter`_
- Filter that ONLY manages records of specified types, all others will be ignored
* - `TypeRejectlistFilter`_
- Filter that IGNORES records of specified types, all others will be managed
* - `octodns-spf`_
- SPF Value Management for octoDNS
.. _AcmeManagingProcessor: https://github.com/octodns/octodns/tree/main/octodns/processor/acme.py)
.. _AutoArpa: https://github.com/octodns/octodns/tree/main/octodns/processor/arpa.py)
.. _EnsureTrailingDots: https://github.com/octodns/octodns/tree/main/octodns/processor/trailing_dots.py)
.. _ExcludeRootNsChanges: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _IgnoreRootNsFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _MetaProcessor: https://github.com/octodns/octodns/tree/main/octodns/processor/meta.py)
.. _NameAllowlistFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _NameRejectlistFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _ValueAllowlistFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _ValueRejectlistFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _OwnershipProcessor: https://github.com/octodns/octodns/tree/main/octodns/processor/ownership.py)
.. _SpfDnsLookupProcessor: https://github.com/octodns/octodns/tree/main/octodns/processor/spf.py)
.. _TtlRestrictionFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/restrict.py)
.. _TypeAllowlistFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _TypeRejectlistFilter: https://github.com/octodns/octodns/tree/main/octodns/processor/filter.py)
.. _octodns-spf: https://github.com/octodns/octodns/tree/main//github.com/octodns/octodns-spf)
Custom Sources and Providers
----------------------------
You can check out the source_ and provider_ directories to see what's currently
supported. Sources act as a source of record information. AxfrSource and
TinyDnsFileSource are currently the only OSS sources, though we have several
others internally that are specific to our environment. These include something
to pull host data from gPanel_ and a similar provider that sources information
about our network gear to create both ``A`` & ``PTR`` records for their
interfaces. Things that might make good OSS sources might include an
``ElbSource`` that pulls information about `AWS Elastic Load Balancers`_ and
dynamically creates ``CNAME``s for them, or ``Ec2Source`` that pulls instance
information so that records can be created for hosts similar to how our
``GPanelProvider`` works.
.. _source: https://github.com/octodns/octodns/tree/main/octodns/source/
.. _provider: https://github.com/octodns/octodns/tree/main/octodns/provider/
.. _gPanel: https://githubengineering.com/githubs-metal-cloud/
.. _AWS Elastic Load Balancers: https://aws.amazon.com/elasticloadbalancing/
Most of the things included in octoDNS are providers, the obvious difference
being that they can serve as both sources and targets of data. We'd really like
to see this list grow over time so if you use an unsupported provider then PRs
are welcome. The existing providers should serve as reasonable examples. Those
that have no GeoDNS support are relatively straightforward. Unfortunately most
of the APIs involved to do GeoDNS style traffic management are complex and
somewhat inconsistent so adding support for that function would be nice, but is
optional and best done in a separate pass.
The ``class`` key in the providers config section can be used to point to
arbitrary classes in the python path so internal or 3rd party providers can
easily be included with no coordination beyond getting them into
``PYTHONPATH``, most likely installed into the virtualenv with octoDNS.
For examples of building third-party sources and providers, see `Related
Projects and Resources`_
Contributing
------------
Please see our contributing_ document if you would like to participate!
.. _contributing: https://github.com/octodns/octodns/tree/main/CONTRIBUTING.md
Getting help
------------
If you have a problem or suggestion, please `open an issue`_ in this repository, and
we will do our best to help.
Please note that this project adheres to the `Contributor Covenant Code of
Conduct`_.
.. _open an issue: https://github.com/octodns/octodns/issues/new
.. _Contributor Covenant Code of Conduct: https://github.com/octodns/octodns/tree/main/CODE_OF_CONDUCT.md
Related Projects and Resources
------------------------------
* GitHub Action: `octoDNS-Sync`_
* NixOS Integration: `NixOS-DNS`_
* Sample Implementations, see how others are using it
- `hackclub/dns`_
- `kubernetes/k8s.io:/dns`_
- `g0v-network/domains`_
- `jekyll/dns`_
* Resources
- Article: `Visualising DNS records with Neo4j`_ + code
- Video: `FOSDEM 2019 - DNS as code with octodns`_
- GitHub Blog: `Enabling DNS split authority with octoDNS`_
- Tutorial: `How To Deploy and Manage Your DNS using octoDNS on Ubuntu 18.04`_
- Cloudflare Blog: `Improving the Resiliency of Our Infrastructure DNS Zone`_
.. _octoDNS-Sync: https://github.com/marketplace/actions/octodns-sync
.. _NixOS-DNS: https://github.com/Janik-Haag/nixos-dns/
.. _hackclub/dns: https://github.com/hackclub/dns)
.. _`kubernetes/k8s.io:/dns`: https://github.com/kubernetes/k8s.io/tree/main/dns)
.. _g0v-network/domains: https://github.com/g0v-network/domains)
.. _jekyll/dns: https://github.com/jekyll/dns)
.. _Visualising DNS records with Neo4j: https://medium.com/@costask/querying-and-visualising-octodns-records-with-neo4j-f4f72ab2d474
.. _FOSDEM 2019 - DNS as code with octodns: https://archive.fosdem.org/2019/schedule/event/dns_octodns/
.. _Enabling DNS split authority with octoDNS: https://github.blog/2017-04-27-enabling-split-authority-dns-with-octodns/
.. _How To Deploy and Manage Your DNS using octoDNS on Ubuntu 18.04: https://www.digitalocean.com/community/tutorials/how-to-deploy-and-manage-your-dns-using-octodns-on-ubuntu-18-04
.. _Improving the Resiliency of Our Infrastructure DNS Zone: https://blog.cloudflare.com/improving-the-resiliency-of-our-infrastructure-dns-zone/
If you know of any other resources, please do let us know!
License
-------
octoDNS is licensed under the `MIT license`_.
.. _MIT license: https://github.com/octodns/octodns/tree/mainLICENSE
The MIT license grant is not for GitHub's trademarks, which include the logo
designs. GitHub reserves all trademark and copyright rights in and to all
GitHub trademarks. GitHub's logos include, for instance, the stylized designs
that include "logo" in the file title in the following folder:
https://github.com/octodns/octodns/tree/main/docs/logos/
GitHub® and its stylized versions and the Invertocat mark are GitHub's
Trademarks or registered Trademarks. When using GitHub's logos, be sure to
follow the GitHub logo guidelines.
Authors
-------
octoDNS was designed and authored by `Ross McFarland`_ and `Joe Williams`_. See
https://github.com/octodns/octodns/graphs/contributors for a complete list of
people who've contributed.
.. _Ross McFarland: https://github.com/ross
.. _Joe Williams: https://github.com/joewilliams

+ 243
- 0
docs/source/records.rst View File

@ -0,0 +1,243 @@
Records
=======
Underlying provider support for each of octoDNS's record types varies and some
providers have extra requirements or limitations. In cases where a record type
is not supported by a provider octoDNS will ignore it there and continue to
manage the record elsewhere. For example ``SSHFP`` is supported by Dyn, but not
Route53. If your source data includes an SSHFP record octoDNS will keep it in
sync on Dyn, but not consider it when evaluating the state of Route53. The best
way to find out what types are supported by a provider is to look for its
``supports`` method. If that method exists the logic will drive which records are
supported and which are ignored. If the provider does not implement the method
it will fall back to :py:attr:`BaseProvider.supports` which indicates full support.
Adding new record types to octoDNS is relatively straightforward, but will
require careful evaluation of each provider to determine whether or not it will
be supported and the addition of code in each to handle and test the new type.
Advanced Record Support
-----------------------
* :doc:`dynamic_records` - the preferred method for configuring geo-location, weights, and healthcheck based fallback between pools of services.
Config (``YamlProvider``)
-------------------------
octoDNS records and :py:class:`octodns.provider.yaml.YamlProvider`'s schema is
essentially a 1:1 match. Properties on the objects will match keys in the
config.
Names
.....
Each top-level key in the yaml file is a record name. Two common special cases
are the root record ``''``, and a wildcard ``'*'``::
---
'':
type: A
values:
- 1.2.3.4
- 1.2.3.5
'*':
type: CNAME
value: www.example.com.
www:
type: A
values:
- 1.2.3.4
- 1.2.3.5
www.sub:
type: A
values:
- 1.2.3.6
- 1.2.3.7
The above config lays out 4 records, ``A``s for ``example.com.``,
``www.example.com.``, and ``www.sub.example.com`` and a wildcard ``CNAME`` mapping
``*.example.com.`` to ``www.example.com.``.
Multiple records
................
In the above example each name had a single record, but there are cases where a
name will need to have multiple records associated with it. This can be
accomplished by using a list::
---
'':
- type: A
values:
- 1.2.3.4
- 1.2.3.5
- type: MX
values:
- exchange: mx1.example.com.
preference: 10
- exchange: mx2.example.com.
preference: 10
Record data
...........
Each record type has a corresponding set of required data. The easiest way to
determine what's required is probably to look at
:py:class:`octodns.record.Record`. You may also utilize ``octodns-validate``
which will throw errors about what's missing when run.
``type`` is required for all records. ``ttl`` is optional. When TTL is not
specified the :py:class:`octodns.provider.yaml.YamlProvider`'s default will be
used. In any situation where an array of ``values`` can be used you can opt to
go with ``value`` as a single item if there's only one.
.. _lenience:
Lenience
........
octoDNS is fairly strict in terms of standards compliance and is opinionated in
terms of best practices. Examples of the former include SRV record naming
requirements and the latter that ALIAS records are constrained to the root of
zones. The strictness and support of providers varies so you may encounter
existing records that fail validation when you try to dump them or you may even
have use cases for which you need to create or preserve records that don't
validate. octoDNS's solution to this is the ``lenient`` flag.
It's best to think of the ``lenient`` flag as "I know what I'm doing and accept
any problems I run across." The main reason being is that some providers may
allow the non-compliant setup and others may not. The behavior of the
non-compliant records may even vary from one provider to another. Caveat
emptor.
Record priority for AutoArpa
++++++++++++++++++++++++++++
When multiple A or AAAA records point to the same IP, it is possible to set an
optional priority on each record. The records with the lowest priority will
have the highest preference when being processed by AutoArpa. The AutoArpa
provider will create PTR records in order of preference, up to a set limit
defined by the ``max_auto_arpa`` option in the provider configuration::
test:
- type: A
value: 1.2.3.4
octodns:
auto_arpa_priority: 1
octodns-dump
++++++++++++
If you're trying to import a zone into octoDNS config file using
``octodns-dump`` which fails due to validation errors you can supply the
``--lenient`` argument to tell octoDNS that you acknowledge that things aren't
lining up with its expectations, but you'd like it to go ahead anyway. This
will do its best to populate the zone and dump the results out into an octoDNS
zone file and include the non-compliant bits. If you go to use that config file
octoDNS will again complain about the validation problems. You can correct them
in cases where that makes sense, but if you need to preserve the non-compliant
records read on for options.
Record level lenience
+++++++++++++++++++++
When there are non-compliant records configured in Yaml you can add the
following to tell octoDNS to do it's best to proceed with them anyway. If you
use ``--lenient`` above to dump a zone and you'd like to sync it as-is you can
mark the problematic records this way::
'not-root':
octodns:
lenient: true
type: ALIAS
values: something.else.com.
Zone level lenience
+++++++++++++++++++
If you'd like to enable lenience for a whole zone you can do so with the
following, thought it's strongly encouraged to mark things at record level when
possible. The most common case where things may need to be done at the zone
level is when using something other than
:py:class:`octodns.provider.yaml.YamlProvider` as a source, e.g. syncing from
``Route53Provider`` to ``Ns1Provider`` when there are non-compliant records in
the zone in Route53::
non-compliant-zone.com.:
lenient: true
sources:
- route53
targets:
- ns1
Restrict Record manipulations
+++++++++++++++++++++++++++++
octoDNS currently provides the ability to limit the number of updates/deletes
on DNS records by configuring a percentage of allowed operations as a provider
threshold. If left unconfigured, suitable defaults take over instead. In the
below example, the Dyn provider is configured with limits of 40% on both update
and delete operations over all the records present::
dyn:
class: octodns.provider.dyn.DynProvider
update_pcent_threshold: 0.4
delete_pcent_threshold: 0.4
Additionally, thresholds can be configured at the zone level. Zone thresholds
take precedence over any provider default or explicit configuration. Zone
thresholds do not have a default::
zones:
example.com.:
update_pcent_threshold: 0.2
delete_pcent_threshold: 0.1
Provider specific record types
------------------------------
Creating and registering
........................
octoDNS has support for provider specific record types through a dynamic type
registration system. This functionality is powered by
py:meth:`octodns.record.Record.register_type` and can be used as follows::
class _SpecificValue(object):
...
class SomeProviderSpecificRecord(ValuesMixin, Record):
_type = 'SomeProvider/SPECIFIC'
_value_type = _SpecificValue
Record.register_type(SomeProviderSpecificRecord)
Have a look at ``Route53Provider``'s `Route53Provider/ALIAS`_ for an example.
_`Route53Provider/ALIAS`: https://github.com/octodns/octodns-route53/blob/main/octodns_route53/record.py
In general this support is intended for record types that only make sense for a
single provider. If multiple providers have a similar record it may make sense
to implement it in octoDNS core.
Naming
......
By convention the record type should be prefixed with the provider class, e.g.
``Route53Provider`` followed by a ``/`` and an all-caps record type name
``ALIAS``, e.g. ``Route53Provider/ALIAS``.
YamlProvider support
....................
Once the type is registered :py:class:`octodns.provider.yaml.YamlProvider` will
automatically gain support for it and they can be included in your zone yaml
files::
alias:
type: Route53Provider/ALIAS
values:
- name: www
type: A
- name: www
type: AAAA

+ 164
- 164
octodns/provider/yaml.py View File

@ -1,4 +1,168 @@
#
'''
Example Configuration
---------------------
Core provider for records configured in yaml files on disk::
config:
class: octodns.provider.yaml.YamlProvider
# The location of yaml config files. By default records are defined in a
# file named for the zone in this directory, the zone file, e.g.
# something.com.yaml.
# (required)
directory: ./config
# The ttl to use for records when not specified in the data
# (optional, default 3600)
default_ttl: 3600
# Whether or not to enforce sorting order when loading yaml
# (optional, default True)
enforce_order: true
# What sort mode to employ when enforcing order
# - simple: `sort`
# - natural: https://pypi.org/project/natsort/
# (optional, default natural)
order_mode: natural
# Whether duplicate records should replace rather than error
# (optional, default False)
populate_should_replace: false
# The file extension used when loading split style zones, Null means
# disabled. When enabled the provider will search for zone records split
# across multiple YAML files in the directory with split_extension
# appended to the zone name, See "Split Details" below.
# split_extension should include the "."
# (optional, default null, "." is the recommended best practice when
# enabling)
split_extension: null
# When writing YAML records out to disk with split_extension enabled
# each record is written out into its own file with .yaml appended to
# the name of the record. The two exceptions are for the root and
# wildcard nodes. These records are written into a file named
# `$[zone.name].yaml`. If you would prefer this catchall file not be
# used `split_catchall` can be set to False to instead write those
# records out to `.yaml` and `*.yaml` respectively. Note that some
# operating systems may not allow files with those names.
# (optional, default True)
split_catchall: true
# Optional filename with record data to be included in all zones
# populated by this provider. Has no effect when used as a target.
# (optional, default null)
shared_filename: null
# Disable loading of the zone .yaml files.
# (optional, default False)
disable_zonefile: false
# Whether or not ; in values, e.g. TXT, need to be escaped \\;
# (optional, default True)
escaped_semicolons: True
.. Note::
When using this provider as a target any existing comments or formatting in
the zone files will be lost when changes are applyed.
Split Details
-------------
All files are stored in a subdirectory matching the name of the zone
(including the trailing .) of the directory config. It is a recommended
best practice that the files be named RECORD.yaml, but all files are
sourced and processed ignoring the filenames so it is up to you how to
organize them.
With `split_extension: .` the directory structure for the zone github.com.
managed under directory "zones/" would look like::
zones/
github.com./
$github.com.yaml
www.yaml
...
Overriding Values
-----------------
Overriding values can be accomplished using multiple yaml providers in the
`sources` list where subsequent providers have `populate_should_replace`
set to `true`. An example use of this would be a zone that you want to push
to external DNS providers and internally, but you want to modify some of
the records in the internal version.
config/octodns.com.yaml::
---
other:
type: A
values:
- 192.30.252.115
- 192.30.252.116
www:
type: A
values:
- 192.30.252.113
- 192.30.252.114
internal/octodns.com.yaml::
---
'www':
type: A
values:
- 10.0.0.12
- 10.0.0.13
external.yaml::
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
zones:
octodns.com.:
sources:
- config
targets:
- route53
internal.yaml::
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
internal:
class: octodns.provider.yaml.YamlProvider
directory: ./internal
populate_should_replace: true
zones:
octodns.com.:
sources:
- config
- internal
targets:
- pdns
You can then sync our records eternally with `--config-file=external.yaml`
and internally (with the custom overrides) with
`--config-file=internal.yaml`
'''
#
#
@ -15,170 +179,6 @@ from .base import BaseProvider
class YamlProvider(BaseProvider):
'''
Example Configuration
---------------------
Core provider for records configured in yaml files on disk.::
config:
class: octodns.provider.yaml.YamlProvider
# The location of yaml config files. By default records are defined in a
# file named for the zone in this directory, the zone file, e.g.
# something.com.yaml.
# (required)
directory: ./config
# The ttl to use for records when not specified in the data
# (optional, default 3600)
default_ttl: 3600
# Whether or not to enforce sorting order when loading yaml
# (optional, default True)
enforce_order: true
# What sort mode to employ when enforcing order
# - simple: `sort`
# - natural: https://pypi.org/project/natsort/
# (optional, default natural)
order_mode: natural
# Whether duplicate records should replace rather than error
# (optional, default False)
populate_should_replace: false
# The file extension used when loading split style zones, Null means
# disabled. When enabled the provider will search for zone records split
# across multiple YAML files in the directory with split_extension
# appended to the zone name, See "Split Details" below.
# split_extension should include the "."
# (optional, default null, "." is the recommended best practice when
# enabling)
split_extension: null
# When writing YAML records out to disk with split_extension enabled
# each record is written out into its own file with .yaml appended to
# the name of the record. The two exceptions are for the root and
# wildcard nodes. These records are written into a file named
# `$[zone.name].yaml`. If you would prefer this catchall file not be
# used `split_catchall` can be set to False to instead write those
# records out to `.yaml` and `*.yaml` respectively. Note that some
# operating systems may not allow files with those names.
# (optional, default True)
split_catchall: true
# Optional filename with record data to be included in all zones
# populated by this provider. Has no effect when used as a target.
# (optional, default null)
shared_filename: null
# Disable loading of the zone .yaml files.
# (optional, default False)
disable_zonefile: false
# Whether or not ; in values, e.g. TXT, need to be escaped \\;
# (optional, default True)
escaped_semicolons: True
.. Note::
When using this provider as a target any existing comments or formatting in
the zone files will be lost when changes are applyed.
Split Details
-------------
All files are stored in a subdirectory matching the name of the zone
(including the trailing .) of the directory config. It is a recommended
best practice that the files be named RECORD.yaml, but all files are
sourced and processed ignoring the filenames so it is up to you how to
organize them.
With `split_extension: .` the directory structure for the zone github.com.
managed under directory "zones/" would look like::
zones/
github.com./
$github.com.yaml
www.yaml
...
Overriding Values
-----------------
Overriding values can be accomplished using multiple yaml providers in the
`sources` list where subsequent providers have `populate_should_replace`
set to `true`. An example use of this would be a zone that you want to push
to external DNS providers and internally, but you want to modify some of
the records in the internal version.
config/octodns.com.yaml::
---
other:
type: A
values:
- 192.30.252.115
- 192.30.252.116
www:
type: A
values:
- 192.30.252.113
- 192.30.252.114
internal/octodns.com.yaml::
---
'www':
type: A
values:
- 10.0.0.12
- 10.0.0.13
external.yaml::
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
zones:
octodns.com.:
sources:
- config
targets:
- route53
internal.yaml::
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
internal:
class: octodns.provider.yaml.YamlProvider
directory: ./internal
populate_should_replace: true
zones:
octodns.com.:
sources:
- config
- internal
targets:
- pdns
You can then sync our records eternally with `--config-file=external.yaml`
and internally (with the custom overrides) with
`--config-file=internal.yaml`
'''
SUPPORTS_GEO = True
SUPPORTS_DYNAMIC = True
SUPPORTS_POOL_VALUE_STATUS = True


+ 5
- 0
octodns/source/base.py View File

@ -10,6 +10,11 @@ class BaseSource(object):
SUPPORTS_DYNAMIC_SUBNETS = False
def __init__(self, id):
'''
:param id: unique identifier for the provider or source
:type id: str
'''
self.id = id
if not getattr(self, 'log', False):
raise NotImplementedError(


+ 3
- 3
requirements-dev.txt View File

@ -6,12 +6,12 @@ build==1.3.0
certifi==2025.8.3
cffi==1.17.1
changelet==0.1.0
charset-normalizer==3.4.2
charset-normalizer==3.4.3
click==8.1.8; python_version<'3.10'
click==8.2.1; python_version>='3.10'
cmarkgfm==2024.11.20
coverage==7.10.2
cryptography==45.0.5
coverage==7.10.3
cryptography==45.0.6
docutils==0.21.2
id==1.5.0
iniconfig==2.1.0


+ 5
- 7
requirements-docs.txt View File

@ -4,25 +4,23 @@ MarkupSafe==3.0.2
Sphinx==7.4.7; python_version=='3.9'
Sphinx==8.1.3; python_version=='3.10'
Sphinx==8.2.3; python_version>='3.11'
accessible-pygments==0.0.5
alabaster==0.7.16; python_version<'3.10'
alabaster==1.0.0; python_version>='3.10'
babel==2.17.0
beautifulsoup4==4.13.4
furo==2025.7.19
imagesize==1.4.1
mdit-py-plugins==0.4.2
mdit-py-plugins==0.4.2; python_version=='3.9'
mdit-py-plugins==0.5.0; python_version>='3.10'
myst-parser==3.0.1; python_version<'3.10'
myst-parser==4.0.1; python_version>='3.10'
roman-numerals-py==3.1.0
snowballstemmer==3.0.1
soupsieve==2.7
sphinx-basic-ng==1.0.0b2
sphinx-copybutton==0.5.2
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-mermaid==1.0.0
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
typing_extensions==4.14.1

+ 1
- 1
script/format View File

@ -2,7 +2,7 @@
set -e
SOURCES="$(find *.py octodns tests -name '*.py') $(grep --files-with-matches '^#!.*python' script/*)"
SOURCES="$(find *.py octodns tests docs -name '*.py') $(grep --files-with-matches '^#!.*python' script/*)"
. env/bin/activate


+ 20
- 16
script/generate-docs View File

@ -1,23 +1,27 @@
#!/bin/bash
#!/bin/sh
set -e
cd "$(dirname "$0")/.."
ROOT=$(pwd)
cd "$ROOT"
rm -rf ./docs/_build ./docs/modules
if [ -z "$VENV_NAME" ]; then
VENV_NAME="env"
fi
ACTIVATE="$VENV_NAME/bin/activate"
if [ ! -f "$ACTIVATE" ]; then
echo "$ACTIVATE does not exist, run ./script/bootstrap" >&2
exit 1
fi
. "$ACTIVATE"
cd docs
if [ -z "$BUILDER" ]; then
BUILDER=html
fi
for f in $(find octodns -name "*.py" | grep -v __init__.py); do
module_name=$(echo $f | sed -e 's#/#.#g' -e 's/.py//')
outdir="docs/modules$(dirname $f | sed -e 's#octodns##')"
mkdir -p "$outdir"
cat <<EOL > "${outdir}/${module_name}.rst"
====================================
``${module_name}``
====================================
.. automodule:: ${module_name}
EOL
done
build="_build/${BUILDER}"
rm -rf "$build" source/api/records/ source/api/processors/
sphinx-build -M html "${ROOT}/docs" "${ROOT}/docs/_build" "$@"
sphinx-build --builder "$BUILDER" --conf-dir . --fail-on-warning "$@" "source/" "$build"

+ 1
- 1
script/lint View File

@ -15,6 +15,6 @@ if [ ! -f "$ACTIVATE" ]; then
fi
. "$ACTIVATE"
SOURCES="$(find *.py octodns tests -name '*.py') $(grep --files-with-matches '^#!.*python' script/*)"
SOURCES="$(find *.py octodns tests docs -name '*.py') $(grep --files-with-matches '^#!.*python' script/*)"
pyflakes $SOURCES

+ 4
- 0
script/update-requirements View File

@ -60,6 +60,10 @@ versions['Sphinx'] = (
"8.1.3; python_version=='3.10'",
f"{versions['Sphinx'][0]}; python_version>='3.11'",
)
versions['mdit-py-plugins'] = (
"0.4.2; python_version=='3.9'",
f"{versions['mdit-py-plugins'][0]}; python_version>='3.10'",
)
def write_packages(fh, packages, header, prefix=''):


+ 3
- 2
setup.py View File

@ -72,10 +72,11 @@ setup(
'twine>=3.4.2',
),
'docs': (
'Sphinx>=8.2.3',
'furo>=2024.8.6',
'myst-parser>=4.0.1',
'Sphinx>=8.2.3',
'sphinxcontrib-mermaid>=1.0.0',
'sphinx-copybutton>=0.5.2',
'sphinx_rtd_theme',
),
},
install_requires=(


Loading…
Cancel
Save