@@ -28,6 +28,7 @@ import (
2828 "time"
2929
3030 log "github.com/sirupsen/logrus"
31+ "go.etcd.io/etcd/api/v3/mvccpb"
3132 etcdcv3 "go.etcd.io/etcd/client/v3"
3233 "sigs.k8s.io/external-dns/pkg/tlsutils"
3334
@@ -92,22 +93,19 @@ type Service struct {
9293 // Etcd key where we found this service and ignored from json un-/marshaling
9394 Key string `json:"-"`
9495
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"`
97-
9896 // OwnedBy is used to prevent service to be added by different external-dns (only used by external-dns)
9997 OwnedBy string `json:"ownedby,omitempty"`
10098}
10199
102100type etcdClient struct {
103- client * etcdcv3.Client
104- managedBy string
105- ignoreEmptyManagedBy bool
101+ client * etcdcv3.Client
102+ ownerID string
103+ strictlyOwned bool
106104}
107105
108106var _ coreDNSClient = etcdClient {}
109107
110- // GetServices return all Service records stored in etcd stored anywhere under the given key (recursively)
108+ // GetServices GetService return all Service records stored in etcd stored anywhere under the given key (recursively)
111109func (c etcdClient ) GetServices (ctx context.Context , prefix string ) ([]* Service , error ) {
112110 ctx , cancel := context .WithTimeout (ctx , etcdTimeout )
113111 defer cancel ()
@@ -121,32 +119,26 @@ func (c etcdClient) GetServices(ctx context.Context, prefix string) ([]*Service,
121119 var svcs []* Service
122120 bx := make (map [Service ]bool )
123121 for _ , n := range r .Kvs {
124- svc := new (Service )
125- if err := json .Unmarshal (n .Value , svc ); err != nil {
126- return nil , fmt .Errorf ("%s: %w" , n .Key , err )
122+ svc , err := c .unmarshalService (n )
123+ if err != nil {
124+ return nil , err
125+ }
126+ if c .strictlyOwned && svc .OwnedBy != c .ownerID {
127+ continue
127128 }
128129 b := Service {
129- Host : svc .Host ,
130- Port : svc .Port ,
131- Priority : svc .Priority ,
132- Weight : svc .Weight ,
133- Text : svc .Text ,
134- Key : string (n .Key ),
135- ManagedBy : svc .ManagedBy ,
136- OwnedBy : svc .OwnedBy ,
130+ Host : svc .Host ,
131+ Port : svc .Port ,
132+ Priority : svc .Priority ,
133+ Weight : svc .Weight ,
134+ Text : svc .Text ,
135+ Key : string (n .Key ),
137136 }
138137 if _ , ok := bx [b ]; ok {
139138 // skip the service if already added to service list.
140139 // the same service might be found in multiple etcd nodes.
141140 continue
142141 }
143- if c .managedBy != "" {
144- if c .ignoreEmptyManagedBy && b .ManagedBy != c .managedBy {
145- continue
146- } else if ! c .ignoreEmptyManagedBy && b .ManagedBy != "" && b .ManagedBy != c .managedBy {
147- continue
148- }
149- }
150142 bx [b ] = true
151143
152144 svc .Key = string (n .Key )
@@ -155,7 +147,6 @@ func (c etcdClient) GetServices(ctx context.Context, prefix string) ([]*Service,
155147 }
156148 svcs = append (svcs , svc )
157149 }
158-
159150 return svcs , nil
160151}
161152
@@ -164,14 +155,23 @@ func (c etcdClient) SaveService(ctx context.Context, service *Service) error {
164155 ctx , cancel := context .WithTimeout (ctx , etcdTimeout )
165156 defer cancel ()
166157
167- if c .managedBy != "" {
168- service .ManagedBy = c .managedBy
169- service .OwnedBy = c .managedBy
170- }
171- if ownedBy , err := c .IsOwnedBy (ctx , service .Key ); err != nil {
172- return err
173- } else if ! ownedBy {
174- return fmt .Errorf ("key %q is not owned by this service" , service .Key )
158+ // check only for empty OwnedBy
159+ if c .strictlyOwned && service .OwnedBy != c .ownerID {
160+ r , err := c .client .Get (ctx , service .Key )
161+ if err != nil {
162+ return fmt .Errorf ("etcd get %q: %w" , service .Key , err )
163+ }
164+ // Key missing -> treat as owned (safe to create)
165+ if r != nil && len (r .Kvs ) != 0 {
166+ svc , err := c .unmarshalService (r .Kvs [0 ])
167+ if err != nil {
168+ return fmt .Errorf ("failed to unmarshal value for key %q: %w" , service .Key , err )
169+ }
170+ if svc .OwnedBy != c .ownerID {
171+ return fmt .Errorf ("key %q is not owned by this provider" , service .Key )
172+ }
173+ }
174+ service .OwnedBy = c .ownerID
175175 }
176176
177177 value , err := json .Marshal (& service )
@@ -185,54 +185,43 @@ func (c etcdClient) SaveService(ctx context.Context, service *Service) error {
185185 return nil
186186}
187187
188- func (c etcdClient ) IsOwnedBy (ctx context.Context , key string ) (bool , error ) {
188+ // DeleteService deletes service record from etcd
189+ func (c etcdClient ) DeleteService (ctx context.Context , key string ) error {
189190 ctx , cancel := context .WithTimeout (ctx , etcdTimeout )
190191 defer cancel ()
191192
192- if c .managedBy == "" {
193- return true , nil
194- }
195-
196- r , err := c .client .Get (ctx , key )
197- if err != nil {
198- return false , err
199- }
200- if r == nil {
201- return true , nil
202- } else if len (r .Kvs ) > 1 {
203- return false , fmt .Errorf ("found multiple keys with the same key this service" )
204- } else if len (r .Kvs ) == 0 {
205- return true , nil
206- }
207- for _ , n := range r .Kvs {
208- svc := new (Service )
209- if err := json .Unmarshal (n .Value , svc ); err != nil {
210- return false , fmt .Errorf ("%s: %w" , n .Key , err )
193+ if c .strictlyOwned {
194+ rs , err := c .client .Get (ctx , key , etcdcv3 .WithPrefix ())
195+ if err != nil {
196+ return err
211197 }
198+ for _ , r := range rs .Kvs {
199+ svc , err := c .unmarshalService (r )
200+ if err != nil {
201+ return err
202+ }
203+ if svc .OwnedBy != c .ownerID {
204+ continue
205+ }
212206
213- if ! c .ignoreEmptyManagedBy && svc .ManagedBy == "" {
214- return true , nil
215- }
216- if svc .ManagedBy == c .managedBy {
217- return true , nil
207+ _ , err = c .client .Delete (ctx , string (r .Key ))
208+ if err != nil {
209+ return err
210+ }
218211 }
212+ return err
213+ } else {
214+ _ , err := c .client .Delete (ctx , key , etcdcv3 .WithPrefix ())
215+ return err
219216 }
220- return false , nil
221217}
222218
223- // DeleteService deletes service record from etcd
224- func (c etcdClient ) DeleteService (ctx context.Context , key string ) error {
225- ctx , cancel := context .WithTimeout (ctx , etcdTimeout )
226- defer cancel ()
227-
228- if owned , err := c .IsOwnedBy (ctx , key ); err != nil {
229- return err
230- } else if ! owned {
231- return fmt .Errorf ("key %q is not owned by this service" , key )
219+ func (c etcdClient ) unmarshalService (n * mvccpb.KeyValue ) (* Service , error ) {
220+ svc := new (Service )
221+ if err := json .Unmarshal (n .Value , svc ); err != nil {
222+ return nil , fmt .Errorf ("failed to unmarshal %q: %w" , n .Key , err )
232223 }
233-
234- _ , err := c .client .Delete (ctx , key , etcdcv3 .WithPrefix ())
235- return err
224+ return svc , nil
236225}
237226
238227// builds etcd client config depending on connection scheme and TLS parameters
@@ -265,7 +254,7 @@ func getETCDConfig() (*etcdcv3.Config, error) {
265254}
266255
267256// the newETCDClient is an etcd client constructor
268- func newETCDClient (managedBy string , ignoreEmptyManagedBy bool ) (coreDNSClient , error ) {
257+ func newETCDClient (ownerID string , strictlyOwned bool ) (coreDNSClient , error ) {
269258 cfg , err := getETCDConfig ()
270259 if err != nil {
271260 return nil , err
@@ -274,12 +263,12 @@ func newETCDClient(managedBy string, ignoreEmptyManagedBy bool) (coreDNSClient,
274263 if err != nil {
275264 return nil , err
276265 }
277- return etcdClient {c , managedBy , ignoreEmptyManagedBy }, nil
266+ return etcdClient {c , ownerID , strictlyOwned }, nil
278267}
279268
280269// NewCoreDNSProvider is a CoreDNS provider constructor
281- func NewCoreDNSProvider (config CoreDNSConfig , managedBy string , ignoreEmptyManagedBy , dryRun bool ) (provider.Provider , error ) {
282- client , err := newETCDClient (managedBy , ignoreEmptyManagedBy )
270+ func NewCoreDNSProvider (config CoreDNSConfig , ownerID string , strictlyOwned , dryRun bool ) (provider.Provider , error ) {
271+ client , err := newETCDClient (ownerID , strictlyOwned )
283272 if err != nil {
284273 return nil , err
285274 }
0 commit comments