Skip to content

Commit df5d462

Browse files
authored
Merge pull request #24 from GDATASoftwareAG/etcd-proxy-idea
etcd proxy idea
2 parents e012c3f + 949217a commit df5d462

File tree

4 files changed

+406
-104
lines changed

4 files changed

+406
-104
lines changed

README.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,20 @@ usage: external-dns-coredns-webhook [<flags>]
88
ExternalDNS CoreDNS webhook
99
1010
Flags:
11-
--help Show context-sensitive help (also try --help-long and --help-man).
12-
--version Show application version.
13-
--dry-run When enabled, prints DNS record changes rather than actually performing them (default: disabled)
14-
--log-format=text The format in which log messages are printed (default: text, options: text, json)
15-
--log-level=info Set the level of logging. (default: info, options: panic, debug, info, warning, error, fatal
11+
--help Show context-sensitive help (also try --help-long and --help-man).
12+
--version Show application version.
13+
--dry-run When enabled, prints DNS record changes rather than actually performing them (default: disabled)
14+
--log-format=text The format in which log messages are printed (default: text, options: text, json)
15+
--log-level=info Set the level of logging. (default: info, options: panic, debug, info, warning, error, fatal
1616
--webhook-provider-read-timeout=5s
17-
The read timeout for the webhook provider in duration format (default: 5s)
17+
The read timeout for the webhook provider in duration format (default: 5s)
1818
--webhook-provider-write-timeout=5s
19-
The write timeout for the webhook provider in duration format (default: 5s)
19+
The write timeout for the webhook provider in duration format (default: 5s)
2020
--webhook-provider-port="0.0.0.0:8888"
21-
Webhook provider port (default: 0.0.0.0:8888)
22-
--prefix="/skydns/" Specify the prefix name
23-
--txt-owner-id="default" When using the TXT registry, a name that identifies this instance of ExternalDNS (default: default)
24-
--pre-filter-external-owned-records
25-
Services are pre filter based on the txt-owner-id (default: false)
21+
Webhook provider port (default: 0.0.0.0:8888)
22+
--prefix="/skydns/" Specify the prefix name
23+
--managed-by="" Only allow checking of services created by the same manager (default: "")
24+
--ignore-empty-managed-by If the 'managed-by' field is set, this prevents the takeover of services without a 'managed-by' value (default: disabled)
2625
```
2726

2827
## ENVs for Etcd
@@ -38,10 +37,9 @@ Flags:
3837
| ETCD_TLS_SERVER_NAME | Optionally, can be used to configure TLS settings for etcd. | "" |
3938
| ETCD_TLS_INSECURE | Optionally, To insecure handle connection use "true", default is false. | "" |
4039

41-
## Pre-filtering CoreDNS services based on ownerIDs
40+
## Pre-filtering CoreDNS services based on managed by field
4241

43-
If you are running external-dns in multi cluster, you can use `--coredns-pre-filter-external-owned-records` and
44-
`--txt-owner-id` to ignore external created services, for example from a different external-dns.
42+
If you are running external-dns in multi cluster, you can use `--managed-by` to filter externally created services, for example from a different external-dns.
4543

4644
## Custom attributes
4745

coredns.go

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ const (
4949
)
5050

5151
type CoreDNSConfig struct {
52-
coreDNSPrefix string
53-
domainFilter *endpoint.DomainFilter
54-
ownerID string
55-
preFilterExternalOwnedRecords bool
52+
coreDNSPrefix string
53+
domainFilter *endpoint.DomainFilter
5654
}
5755

5856
// coreDNSClient is an interface to work with CoreDNS service records in etcd
@@ -93,11 +91,16 @@ type Service struct {
9391

9492
// Etcd key where we found this service and ignored from json un-/marshaling
9593
Key string `json:"-"`
94+
95+
// ManagedBy is used to prevent service to be added by different external-dns (only used by external-dns)
96+
ManagedBy string `json:"managedby,omitempty"`
9697
}
9798

9899
type etcdClient struct {
99-
client *etcdcv3.Client
100-
ctx context.Context
100+
client *etcdcv3.Client
101+
ctx context.Context
102+
managedBy string
103+
ignoreEmptyManagedBy bool
101104
}
102105

103106
var _ coreDNSClient = etcdClient{}
@@ -120,12 +123,27 @@ func (c etcdClient) GetServices(prefix string) ([]*Service, error) {
120123
if err := json.Unmarshal(n.Value, svc); err != nil {
121124
return nil, fmt.Errorf("%s: %w", n.Key, err)
122125
}
123-
b := Service{Host: svc.Host, Port: svc.Port, Priority: svc.Priority, Weight: svc.Weight, Text: svc.Text, Key: string(n.Key)}
126+
b := Service{
127+
Host: svc.Host,
128+
Port: svc.Port,
129+
Priority: svc.Priority,
130+
Weight: svc.Weight,
131+
Text: svc.Text,
132+
Key: string(n.Key),
133+
ManagedBy: svc.ManagedBy,
134+
}
124135
if _, ok := bx[b]; ok {
125136
// skip the service if already added to service list.
126137
// the same service might be found in multiple etcd nodes.
127138
continue
128139
}
140+
if c.managedBy != "" {
141+
if c.ignoreEmptyManagedBy && b.ManagedBy != c.managedBy {
142+
continue
143+
} else if !c.ignoreEmptyManagedBy && b.ManagedBy != "" && b.ManagedBy != c.managedBy {
144+
continue
145+
}
146+
}
129147
bx[b] = true
130148

131149
svc.Key = string(n.Key)
@@ -143,6 +161,15 @@ func (c etcdClient) SaveService(service *Service) error {
143161
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
144162
defer cancel()
145163

164+
if c.managedBy != "" {
165+
service.ManagedBy = c.managedBy
166+
}
167+
if ownedBy, err := c.IsOwnedBy(service.Key); err != nil {
168+
return err
169+
} else if !ownedBy {
170+
return fmt.Errorf("key %q is not owned by this service", service.Key)
171+
}
172+
146173
value, err := json.Marshal(&service)
147174
if err != nil {
148175
return err
@@ -154,11 +181,52 @@ func (c etcdClient) SaveService(service *Service) error {
154181
return nil
155182
}
156183

184+
func (c etcdClient) IsOwnedBy(key string) (bool, error) {
185+
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
186+
defer cancel()
187+
188+
if c.managedBy == "" {
189+
return true, nil
190+
}
191+
192+
r, err := c.client.Get(ctx, key)
193+
if err != nil {
194+
return false, err
195+
}
196+
if r == nil {
197+
return true, nil
198+
} else if len(r.Kvs) > 1 {
199+
return false, fmt.Errorf("found multiple keys with the same key this service")
200+
} else if len(r.Kvs) == 0 {
201+
return true, nil
202+
}
203+
for _, n := range r.Kvs {
204+
svc := new(Service)
205+
if err := json.Unmarshal(n.Value, svc); err != nil {
206+
return false, fmt.Errorf("%s: %w", n.Key, err)
207+
}
208+
209+
if !c.ignoreEmptyManagedBy && svc.ManagedBy == "" {
210+
return true, nil
211+
}
212+
if svc.ManagedBy == c.managedBy {
213+
return true, nil
214+
}
215+
}
216+
return false, nil
217+
}
218+
157219
// DeleteService deletes service record from etcd
158220
func (c etcdClient) DeleteService(key string) error {
159221
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
160222
defer cancel()
161223

224+
if owned, err := c.IsOwnedBy(key); err != nil {
225+
return err
226+
} else if !owned {
227+
return fmt.Errorf("key %q is not owned by this service", key)
228+
}
229+
162230
_, err := c.client.Delete(ctx, key, etcdcv3.WithPrefix())
163231
return err
164232
}
@@ -193,7 +261,7 @@ func getETCDConfig() (*etcdcv3.Config, error) {
193261
}
194262

195263
// the newETCDClient is an etcd client constructor
196-
func newETCDClient() (coreDNSClient, error) {
264+
func newETCDClient(managedBy string, ignoreEmptyManagedBy bool) (coreDNSClient, error) {
197265
cfg, err := getETCDConfig()
198266
if err != nil {
199267
return nil, err
@@ -202,12 +270,12 @@ func newETCDClient() (coreDNSClient, error) {
202270
if err != nil {
203271
return nil, err
204272
}
205-
return etcdClient{c, context.Background()}, nil
273+
return etcdClient{c, context.Background(), managedBy, ignoreEmptyManagedBy}, nil
206274
}
207275

208276
// NewCoreDNSProvider is a CoreDNS provider constructor
209-
func NewCoreDNSProvider(config CoreDNSConfig, dryRun bool) (provider.Provider, error) {
210-
client, err := newETCDClient()
277+
func NewCoreDNSProvider(config CoreDNSConfig, managedBy string, ignoreEmptyManagedBy, dryRun bool) (provider.Provider, error) {
278+
client, err := newETCDClient(managedBy, ignoreEmptyManagedBy)
211279
if err != nil {
212280
return nil, err
213281
}
@@ -250,14 +318,6 @@ func (p coreDNSProvider) Records(_ context.Context) ([]*endpoint.Endpoint, error
250318
return nil, err
251319
}
252320
for _, service := range services {
253-
if p.preFilterExternalOwnedRecords && service.Text != "" {
254-
if labels, err := endpoint.NewLabelsFromStringPlain(service.Text); err == nil && labels != nil {
255-
if owner, exists := labels[endpoint.OwnerLabelKey]; exists && owner != p.ownerID {
256-
log.Debugf(`Skipping coredns service %v because owner id does not match, found: "%s", required: "%s"`, service, owner, p.ownerID)
257-
continue
258-
}
259-
}
260-
}
261321
domains := strings.Split(strings.TrimPrefix(service.Key, p.coreDNSPrefix), "/")
262322
reverse(domains)
263323
dnsName := strings.Join(domains[service.TargetStrip:], ".")

0 commit comments

Comments
 (0)