One should not be afraid to use external-dns, because it can delete or overwrite the records preexisting in the DNS provider.
One should not be afraid to use external-dns, because it can delete or overwrite the records preexisting in the DNS provider.
**Why we need it?**
DNS provider (AWS Route53, Google DNS, etc.) stores dns records which are created via various means. Integration of External-DNS should be safe and should not delete or overwrite the records which it is not responsible for. Moreover, it should certainly be possible for multiple kubernetes clusters to share the same hosted zone within the dns provider, additionally multiple external-dns instances inside the same cluster should be able to co-exist without messing with the same set of records.
DNS provider (AWS Route53, Google DNS, etc.) stores dns records which are created via various means. Integration of External-DNS should be safe and should not delete or overwrite the records which it is not responsible for. Moreover, it should certainly be possible for multiple kubernetes clusters to share the same hosted zone within the dns provider, additionally multiple external-dns instances inside the same cluster should be able to co-exist without messing with the same set of records.
Storage provides a persistent storage with information to track the records created by the external-dns.
This proposal introduces multiple possible implementation with the details depending on the setup.
This proposal introduces multiple possible implementation with the details depending on the setup.
## Requirements and assumptions
1. Pre-existing records should not be modified by external-dns
2. External-dns instance only creates/modifies/deletes records which are created by this instance
3. It should be possible to transfer the ownership to another external-dns instance
4. Any integrated dns-provider should provide at least a single way to implement the storage
4. Any integrated DNS provider should provide at least a single way to implement the storage
5. Lifetime of the records should not be limited to lifetime of external-dns
6. External-dns should have its identifier for marking the managed records - **`storage-id`**
...
...
@@ -31,7 +31,7 @@ The following presents two ways to implement the storage, and we are planning to
This implementation idea is borrowed from [Mate](https://github.com/zalando-incubator/mate/)
Each record created by external-dns is accompanied by the TXT record, which internally stores the external-dns identifier. For example, if external dns with `storage-id="external-dns-1"` record to be created with dns name `foo.zone.org`, external-dns will create a TXT record with the same dns name `foo.zone.org` and injected value of `"external-dns-1"`. The transfer of ownership can be done by modifying the value of the TXT record. If no TXT record exists for the record or the value does not match its own `storage-id`, then external-dns will simply ignore it.
Each record created by external-dns is accompanied by the TXT record, which internally stores the external-dns identifier. For example, if external dns with `storage-id="external-dns-1"` record to be created with dns name `foo.zone.org`, external-dns will create a TXT record with the same dns name `foo.zone.org` and injected value of `"external-dns-1"`. The transfer of ownership can be done by modifying the value of the TXT record. If no TXT record exists for the record or the value does not match its own `storage-id`, then external-dns will simply ignore it.
#### Goods
...
...
@@ -47,7 +47,7 @@ Each record created by external-dns is accompanied by the TXT record, which inte
### ConfigMap
Store the state in the configmap. ConfigMap is created and managed by each external-dns individually, i.e. external-dns with **`storage-id=external-dns-1`** will create and operate on `extern-dns-1-storage` ConfigMap. ConfigMap will store **all** the records present in the DNS provider as serialized JSON. For example:
Store the state in the configmap. ConfigMap is created and managed by each external-dns individually, i.e. external-dns with **`storage-id=external-dns-1`** will create and operate on `extern-dns-1-storage` ConfigMap. ConfigMap will store **all** the records present in the DNS provider as serialized JSON. For example:
```
kind: ConfigMap
...
...
@@ -93,35 +93,35 @@ ConfigMap will be periodically resynced with the dns provider by fetching the dn
## Component integration
Components:
Components:
* Source - all endpoints ( collection of ingress, service[type=LoadBalancer] etc.)
*[Plan](https://github.com/kubernetes-incubator/external-dns/issues/13) - object responsible for the create of change lists in external-dns
*DNSProvider - interface to access the DNS provider API
*[Plan](https://github.com/kubernetes-incubator/external-dns/issues/13) - object responsible for the create of change lists in external-dns
* Provider - interface to access the DNS provider API
A single loop iteration of external-dns operation:
A single loop iteration of external-dns operation:
1. Get all endpoints ( collection ingress, service[type=LoadBalancer] etc.) into collection of `endpoints`
2. Get storage `Records()`
1. Get all endpoints ( collection ingress, service[type=LoadBalancer] etc.) into collection of `endpoints`
2. Get storage `Records()`
3. Pass `Records` (including ownership information) and list of endpoints to `Plan` to do the calculation
4. Make a call to DNS provider with `Plan` provided change list
5. If call succeeded pass the change list to storage `Assign()` to mark the records that are created
5. If call succeeded pass the change list to storage `Assign()` to mark the records that are created
Storage gets updated all the time via `Poll`. `Plan` does not call DNS provider directly. Good value of the `Poll` is to have simple rate limiting mechanism on DNS provider.
#### Notes:
1. DNS Provider should use batch operations
2. DNS Provider should be called with CREATE operation (not UPSERT!) when the record does not yet exist!
3. Storage does not need to be in complete sync with DNS provider due to #2. Hence resolving the potential caveats of ConfigMap implementation
2. DNS Provider should be called with CREATE operation (not UPSERT!) when the record does not yet exist!
3. Storage does not need to be in complete sync with DNS provider due to #2. Hence resolving the potential caveats of ConfigMap implementation
## Implementation
Basic implementation of the storage interface:
Basic implementation of the storage interface:
1. Storage has the dnsprovider object to retrieve the list of records, but it never makes the call to modify the records (think layer to help out with endpoint filtering)
2. Record() - returns whatever is stored in the storage
3. Assign(endpoints) - called when the records are registered with dns provider - hence storage need to mark its ownership. **Therefore DNSProvider serves as a safe-guard from race conditions**
3. Assign(endpoints) - called when the records are registered with dns provider - hence storage need to mark its ownership. **Therefore Provider serves as a safe-guard from race conditions**
4. WaitForSync() - called in the beginning to populate the storage, in case of configmap would be the configmap creation and fetching the dns provider records
5. Poll() - resync loop to stay-up-to-date with dns provider state
...
...
@@ -132,9 +132,9 @@ We will provide `InMemoryStorage` non-persistent storage which should help us wi