• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / external-dns / 15107231153

19 May 2025 07:43AM UTC coverage: 73.518% (+0.06%) from 73.459%
15107231153

push

github

web-flow
fix(docs): resove broken links, add source description (#5413)

14969 of 20361 relevant lines covered (73.52%)

692.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.37
/controller/execute.go
1
/*
2
Copyright 2025 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controller
18

19
import (
20
        "context"
21
        "fmt"
22
        "net/http"
23
        "os"
24
        "os/signal"
25
        "syscall"
26
        "time"
27

28
        "github.com/aws/aws-sdk-go-v2/service/dynamodb"
29
        "github.com/aws/aws-sdk-go-v2/service/route53"
30
        sd "github.com/aws/aws-sdk-go-v2/service/servicediscovery"
31
        "github.com/go-logr/logr"
32
        "github.com/prometheus/client_golang/prometheus/promhttp"
33
        log "github.com/sirupsen/logrus"
34
        "k8s.io/klog/v2"
35

36
        "sigs.k8s.io/external-dns/endpoint"
37
        "sigs.k8s.io/external-dns/pkg/apis/externaldns"
38
        "sigs.k8s.io/external-dns/pkg/apis/externaldns/validation"
39
        "sigs.k8s.io/external-dns/pkg/metrics"
40
        "sigs.k8s.io/external-dns/plan"
41
        "sigs.k8s.io/external-dns/provider"
42
        "sigs.k8s.io/external-dns/provider/akamai"
43
        "sigs.k8s.io/external-dns/provider/alibabacloud"
44
        "sigs.k8s.io/external-dns/provider/aws"
45
        "sigs.k8s.io/external-dns/provider/awssd"
46
        "sigs.k8s.io/external-dns/provider/azure"
47
        "sigs.k8s.io/external-dns/provider/civo"
48
        "sigs.k8s.io/external-dns/provider/cloudflare"
49
        "sigs.k8s.io/external-dns/provider/coredns"
50
        "sigs.k8s.io/external-dns/provider/digitalocean"
51
        "sigs.k8s.io/external-dns/provider/dnsimple"
52
        "sigs.k8s.io/external-dns/provider/exoscale"
53
        "sigs.k8s.io/external-dns/provider/gandi"
54
        "sigs.k8s.io/external-dns/provider/godaddy"
55
        "sigs.k8s.io/external-dns/provider/google"
56
        "sigs.k8s.io/external-dns/provider/ibmcloud"
57
        "sigs.k8s.io/external-dns/provider/inmemory"
58
        "sigs.k8s.io/external-dns/provider/linode"
59
        "sigs.k8s.io/external-dns/provider/ns1"
60
        "sigs.k8s.io/external-dns/provider/oci"
61
        "sigs.k8s.io/external-dns/provider/ovh"
62
        "sigs.k8s.io/external-dns/provider/pdns"
63
        "sigs.k8s.io/external-dns/provider/pihole"
64
        "sigs.k8s.io/external-dns/provider/plural"
65
        "sigs.k8s.io/external-dns/provider/rfc2136"
66
        "sigs.k8s.io/external-dns/provider/scaleway"
67
        "sigs.k8s.io/external-dns/provider/tencentcloud"
68
        "sigs.k8s.io/external-dns/provider/transip"
69
        "sigs.k8s.io/external-dns/provider/ultradns"
70
        "sigs.k8s.io/external-dns/provider/webhook"
71
        webhookapi "sigs.k8s.io/external-dns/provider/webhook/api"
72
        "sigs.k8s.io/external-dns/registry"
73
        "sigs.k8s.io/external-dns/source"
74
)
75

76
func Execute() {
×
77
        cfg := externaldns.NewConfig()
×
78
        if err := cfg.ParseFlags(os.Args[1:]); err != nil {
×
79
                log.Fatalf("flag parsing error: %v", err)
×
80
        }
×
81
        log.Infof("config: %s", cfg)
×
82
        if err := validation.ValidateConfig(cfg); err != nil {
×
83
                log.Fatalf("config validation failed: %v", err)
×
84
        }
×
85

86
        configureLogger(cfg)
×
87

×
88
        if cfg.DryRun {
×
89
                log.Info("running in dry-run mode. No changes to DNS records will be made.")
×
90
        }
×
91

92
        if log.GetLevel() < log.DebugLevel {
×
93
                // Klog V2 is used by k8s.io/apimachinery/pkg/labels and can throw (a lot) of irrelevant logs
×
94
                // See https://github.com/kubernetes-sigs/external-dns/issues/2348
×
95
                defer klog.ClearLogger()
×
96
                klog.SetLogger(logr.Discard())
×
97
        }
×
98

99
        log.Info(externaldns.Banner())
×
100

×
101
        ctx, cancel := context.WithCancel(context.Background())
×
102

×
103
        go serveMetrics(cfg.MetricsAddress)
×
104
        go handleSigterm(cancel)
×
105

×
106
        // Create a source.Config from the flags passed by the user.
×
107
        sourceCfg := source.NewSourceConfig(cfg)
×
108

×
109
        // Lookup all the selected sources by names and pass them the desired configuration.
×
110
        sources, err := source.ByNames(ctx, &source.SingletonClientGenerator{
×
111
                KubeConfig:   cfg.KubeConfig,
×
112
                APIServerURL: cfg.APIServerURL,
×
113
                // If update events are enabled, disable timeout.
×
114
                RequestTimeout: func() time.Duration {
×
115
                        if cfg.UpdateEvents {
×
116
                                return 0
×
117
                        }
×
118
                        return cfg.RequestTimeout
×
119
                }(),
120
        }, cfg.Sources, sourceCfg)
121
        if err != nil {
×
122
                log.Fatal(err)
×
123
        }
×
124

125
        // Filter targets
126
        targetFilter := endpoint.NewTargetNetFilterWithExclusions(cfg.TargetNetFilter, cfg.ExcludeTargetNets)
×
127

×
128
        // Combine multiple sources into a single, deduplicated source.
×
129
        endpointsSource := source.NewDedupSource(source.NewMultiSource(sources, sourceCfg.DefaultTargets))
×
130
        endpointsSource = source.NewNAT64Source(endpointsSource, cfg.NAT64Networks)
×
131
        endpointsSource = source.NewTargetFilterSource(endpointsSource, targetFilter)
×
132

×
133
        domainFilter := createDomainFilter(cfg)
×
134
        zoneNameFilter := endpoint.NewDomainFilter(cfg.ZoneNameFilter)
×
135
        zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
×
136
        zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
×
137
        zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
×
138

×
139
        var p provider.Provider
×
140
        switch cfg.Provider {
×
141
        case "akamai":
×
142
                p, err = akamai.NewAkamaiProvider(
×
143
                        akamai.AkamaiConfig{
×
144
                                DomainFilter:          domainFilter,
×
145
                                ZoneIDFilter:          zoneIDFilter,
×
146
                                ServiceConsumerDomain: cfg.AkamaiServiceConsumerDomain,
×
147
                                ClientToken:           cfg.AkamaiClientToken,
×
148
                                ClientSecret:          cfg.AkamaiClientSecret,
×
149
                                AccessToken:           cfg.AkamaiAccessToken,
×
150
                                EdgercPath:            cfg.AkamaiEdgercPath,
×
151
                                EdgercSection:         cfg.AkamaiEdgercSection,
×
152
                                DryRun:                cfg.DryRun,
×
153
                        }, nil)
×
154
        case "alibabacloud":
×
155
                p, err = alibabacloud.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun)
×
156
        case "aws":
×
157
                configs := aws.CreateV2Configs(cfg)
×
158
                clients := make(map[string]aws.Route53API, len(configs))
×
159
                for profile, config := range configs {
×
160
                        clients[profile] = route53.NewFromConfig(config)
×
161
                }
×
162

163
                p, err = aws.NewAWSProvider(
×
164
                        aws.AWSConfig{
×
165
                                DomainFilter:          domainFilter,
×
166
                                ZoneIDFilter:          zoneIDFilter,
×
167
                                ZoneTypeFilter:        zoneTypeFilter,
×
168
                                ZoneTagFilter:         zoneTagFilter,
×
169
                                ZoneMatchParent:       cfg.AWSZoneMatchParent,
×
170
                                BatchChangeSize:       cfg.AWSBatchChangeSize,
×
171
                                BatchChangeSizeBytes:  cfg.AWSBatchChangeSizeBytes,
×
172
                                BatchChangeSizeValues: cfg.AWSBatchChangeSizeValues,
×
173
                                BatchChangeInterval:   cfg.AWSBatchChangeInterval,
×
174
                                EvaluateTargetHealth:  cfg.AWSEvaluateTargetHealth,
×
175
                                PreferCNAME:           cfg.AWSPreferCNAME,
×
176
                                DryRun:                cfg.DryRun,
×
177
                                ZoneCacheDuration:     cfg.AWSZoneCacheDuration,
×
178
                        },
×
179
                        clients,
×
180
                )
×
181
        case "aws-sd":
×
182
                // Check that only compatible Registry is used with AWS-SD
×
183
                if cfg.Registry != "noop" && cfg.Registry != "aws-sd" {
×
184
                        log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry)
×
185
                        cfg.Registry = "aws-sd"
×
186
                }
×
187
                p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
×
188
        case "azure-dns", "azure":
×
189
                p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun)
×
190
        case "azure-private-dns":
×
191
                p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun)
×
192
        case "ultradns":
×
193
                p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
×
194
        case "civo":
×
195
                p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
×
196
        case "cloudflare":
×
197
                p, err = cloudflare.NewCloudFlareProvider(
×
198
                        domainFilter,
×
199
                        zoneIDFilter,
×
200
                        cfg.CloudflareProxied,
×
201
                        cfg.DryRun,
×
202
                        cfg.CloudflareRegionKey,
×
203
                        cloudflare.CustomHostnamesConfig{
×
204
                                Enabled:              cfg.CloudflareCustomHostnames,
×
205
                                MinTLSVersion:        cfg.CloudflareCustomHostnamesMinTLSVersion,
×
206
                                CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority,
×
207
                        },
×
208
                        cloudflare.DNSRecordsConfig{
×
209
                                PerPage: cfg.CloudflareDNSRecordsPerPage,
×
210
                                Comment: cfg.CloudflareDNSRecordsComment,
×
211
                        })
×
212
        case "google":
×
213
                p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun)
×
214
        case "digitalocean":
×
215
                p, err = digitalocean.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun, cfg.DigitalOceanAPIPageSize)
×
216
        case "ovh":
×
217
                p, err = ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHApiRateLimit, cfg.OVHEnableCNAMERelative, cfg.DryRun)
×
218
        case "linode":
×
219
                p, err = linode.NewLinodeProvider(domainFilter, cfg.DryRun)
×
220
        case "dnsimple":
×
221
                p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
×
222
        case "coredns", "skydns":
×
223
                p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.DryRun)
×
224
        case "exoscale":
×
225
                p, err = exoscale.NewExoscaleProvider(
×
226
                        cfg.ExoscaleAPIEnvironment,
×
227
                        cfg.ExoscaleAPIZone,
×
228
                        cfg.ExoscaleAPIKey,
×
229
                        cfg.ExoscaleAPISecret,
×
230
                        cfg.DryRun,
×
231
                        exoscale.ExoscaleWithDomain(domainFilter),
×
232
                        exoscale.ExoscaleWithLogging(),
×
233
                )
×
234
        case "inmemory":
×
235
                p, err = inmemory.NewInMemoryProvider(inmemory.InMemoryInitZones(cfg.InMemoryZones), inmemory.InMemoryWithDomain(domainFilter), inmemory.InMemoryWithLogging()), nil
×
236
        case "pdns":
×
237
                p, err = pdns.NewPDNSProvider(
×
238
                        ctx,
×
239
                        pdns.PDNSConfig{
×
240
                                DomainFilter: domainFilter,
×
241
                                DryRun:       cfg.DryRun,
×
242
                                Server:       cfg.PDNSServer,
×
243
                                ServerID:     cfg.PDNSServerID,
×
244
                                APIKey:       cfg.PDNSAPIKey,
×
245
                                TLSConfig: pdns.TLSConfig{
×
246
                                        SkipTLSVerify:         cfg.PDNSSkipTLSVerify,
×
247
                                        CAFilePath:            cfg.TLSCA,
×
248
                                        ClientCertFilePath:    cfg.TLSClientCert,
×
249
                                        ClientCertKeyFilePath: cfg.TLSClientCertKey,
×
250
                                },
×
251
                        },
×
252
                )
×
253
        case "oci":
×
254
                var config *oci.OCIConfig
×
255
                // if the instance-principals flag was set, and a compartment OCID was provided, then ignore the
×
256
                // OCI config file, and provide a config that uses instance principal authentication.
×
257
                if cfg.OCIAuthInstancePrincipal {
×
258
                        if len(cfg.OCICompartmentOCID) == 0 {
×
259
                                err = fmt.Errorf("instance principal authentication requested, but no compartment OCID provided")
×
260
                        } else {
×
261
                                authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true}
×
262
                                config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID}
×
263
                        }
×
264
                } else {
×
265
                        config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
×
266
                }
×
267
                config.ZoneCacheDuration = cfg.OCIZoneCacheDuration
×
268
                if err == nil {
×
269
                        p, err = oci.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.OCIZoneScope, cfg.DryRun)
×
270
                }
×
271
        case "rfc2136":
×
272
                tlsConfig := rfc2136.TLSConfig{
×
273
                        UseTLS:                cfg.RFC2136UseTLS,
×
274
                        SkipTLSVerify:         cfg.RFC2136SkipTLSVerify,
×
275
                        CAFilePath:            cfg.TLSCA,
×
276
                        ClientCertFilePath:    cfg.TLSClientCert,
×
277
                        ClientCertKeyFilePath: cfg.TLSClientCertKey,
×
278
                }
×
279
                p, err = rfc2136.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, cfg.RFC2136MinTTL, cfg.RFC2136CreatePTR, cfg.RFC2136GSSTSIG, cfg.RFC2136KerberosUsername, cfg.RFC2136KerberosPassword, cfg.RFC2136KerberosRealm, cfg.RFC2136BatchChangeSize, tlsConfig, cfg.RFC2136LoadBalancingStrategy, nil)
×
280
        case "ns1":
×
281
                p, err = ns1.NewNS1Provider(
×
282
                        ns1.NS1Config{
×
283
                                DomainFilter:  domainFilter,
×
284
                                ZoneIDFilter:  zoneIDFilter,
×
285
                                NS1Endpoint:   cfg.NS1Endpoint,
×
286
                                NS1IgnoreSSL:  cfg.NS1IgnoreSSL,
×
287
                                DryRun:        cfg.DryRun,
×
288
                                MinTTLSeconds: cfg.NS1MinTTLSeconds,
×
289
                        },
×
290
                )
×
291
        case "transip":
×
292
                p, err = transip.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun)
×
293
        case "scaleway":
×
294
                p, err = scaleway.NewScalewayProvider(ctx, domainFilter, cfg.DryRun)
×
295
        case "godaddy":
×
296
                p, err = godaddy.NewGoDaddyProvider(ctx, domainFilter, cfg.GoDaddyTTL, cfg.GoDaddyAPIKey, cfg.GoDaddySecretKey, cfg.GoDaddyOTE, cfg.DryRun)
×
297
        case "gandi":
×
298
                p, err = gandi.NewGandiProvider(ctx, domainFilter, cfg.DryRun)
×
299
        case "pihole":
×
300
                p, err = pihole.NewPiholeProvider(
×
301
                        pihole.PiholeConfig{
×
302
                                Server:                cfg.PiholeServer,
×
303
                                Password:              cfg.PiholePassword,
×
304
                                TLSInsecureSkipVerify: cfg.PiholeTLSInsecureSkipVerify,
×
305
                                DomainFilter:          domainFilter,
×
306
                                DryRun:                cfg.DryRun,
×
307
                                APIVersion:            cfg.PiholeApiVersion,
×
308
                        },
×
309
                )
×
310
        case "ibmcloud":
×
311
                p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun)
×
312
        case "plural":
×
313
                p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider)
×
314
        case "tencentcloud":
×
315
                p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun)
×
316
        case "webhook":
×
317
                p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL)
×
318
        default:
×
319
                log.Fatalf("unknown dns provider: %s", cfg.Provider)
×
320
        }
321
        if err != nil {
×
322
                log.Fatal(err)
×
323
        }
×
324

325
        if cfg.WebhookServer {
×
326
                webhookapi.StartHTTPApi(p, nil, cfg.WebhookProviderReadTimeout, cfg.WebhookProviderWriteTimeout, "127.0.0.1:8888")
×
327
                os.Exit(0)
×
328
        }
×
329

330
        if cfg.ProviderCacheTime > 0 {
×
331
                p = provider.NewCachedProvider(
×
332
                        p,
×
333
                        cfg.ProviderCacheTime,
×
334
                )
×
335
        }
×
336

337
        reg, err := selectRegistry(cfg, p)
×
338
        if err != nil {
×
339
                log.Fatal(err)
×
340
        }
×
341

342
        policy, exists := plan.Policies[cfg.Policy]
×
343
        if !exists {
×
344
                log.Fatalf("unknown policy: %s", cfg.Policy)
×
345
        }
×
346

347
        ctrl := Controller{
×
348
                Source:               endpointsSource,
×
349
                Registry:             reg,
×
350
                Policy:               policy,
×
351
                Interval:             cfg.Interval,
×
352
                DomainFilter:         domainFilter,
×
353
                ManagedRecordTypes:   cfg.ManagedDNSRecordTypes,
×
354
                ExcludeRecordTypes:   cfg.ExcludeDNSRecordTypes,
×
355
                MinEventSyncInterval: cfg.MinEventSyncInterval,
×
356
        }
×
357

×
358
        if cfg.Once {
×
359
                err := ctrl.RunOnce(ctx)
×
360
                if err != nil {
×
361
                        log.Fatal(err)
×
362
                }
×
363

364
                os.Exit(0)
×
365
        }
366

367
        if cfg.UpdateEvents {
×
368
                // Add RunOnce as the handler function that will be called when ingress/service sources have changed.
×
369
                // Note that k8s Informers will perform an initial list operation, which results in the handler
×
370
                // function initially being called for every Service/Ingress that exists
×
371
                ctrl.Source.AddEventHandler(ctx, func() { ctrl.ScheduleRunOnce(time.Now()) })
×
372
        }
373

374
        ctrl.ScheduleRunOnce(time.Now())
×
375
        ctrl.Run(ctx)
×
376
}
377

378
// This function configures the logger format and level based on the provided configuration.
379
func configureLogger(cfg *externaldns.Config) {
3✔
380
        if cfg.LogFormat == "json" {
4✔
381
                log.SetFormatter(&log.JSONFormatter{})
1✔
382
        }
1✔
383
        ll, err := log.ParseLevel(cfg.LogLevel)
3✔
384
        if err != nil {
4✔
385
                log.Fatalf("failed to parse log level: %v", err)
1✔
386
        }
1✔
387
        log.SetLevel(ll)
3✔
388
}
389

390
// selectRegistry selects the appropriate registry implementation based on the configuration in cfg.
391
// It initializes and returns a registry along with any error encountered during setup.
392
// Supported registry types include: dynamodb, noop, txt, and aws-sd.
393
func selectRegistry(cfg *externaldns.Config, p provider.Provider) (registry.Registry, error) {
5✔
394
        var r registry.Registry
5✔
395
        var err error
5✔
396
        switch cfg.Registry {
5✔
397
        case "dynamodb":
1✔
398
                var dynamodbOpts []func(*dynamodb.Options)
1✔
399
                if cfg.AWSDynamoDBRegion != "" {
2✔
400
                        dynamodbOpts = []func(*dynamodb.Options){
1✔
401
                                func(opts *dynamodb.Options) {
2✔
402
                                        opts.Region = cfg.AWSDynamoDBRegion
1✔
403
                                },
1✔
404
                        }
405
                }
406
                r, err = registry.NewDynamoDBRegistry(p, cfg.TXTOwnerID, dynamodb.NewFromConfig(aws.CreateDefaultV2Config(cfg), dynamodbOpts...), cfg.AWSDynamoDBTable, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, []byte(cfg.TXTEncryptAESKey), cfg.TXTCacheInterval)
1✔
407
        case "noop":
1✔
408
                r, err = registry.NewNoopRegistry(p)
1✔
409
        case "txt":
1✔
410
                r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey), cfg.TXTNewFormatOnly)
1✔
411
        case "aws-sd":
1✔
412
                r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID)
1✔
413
        default:
1✔
414
                log.Fatalf("unknown registry: %s", cfg.Registry)
1✔
415
        }
416
        return r, err
5✔
417
}
418

419
// RegexDomainFilter overrides DomainFilter
420
func createDomainFilter(cfg *externaldns.Config) endpoint.DomainFilter {
5✔
421
        if cfg.RegexDomainFilter != nil && cfg.RegexDomainFilter.String() != "" {
7✔
422
                return endpoint.NewRegexDomainFilter(cfg.RegexDomainFilter, cfg.RegexDomainExclusion)
2✔
423
        } else {
5✔
424
                return endpoint.NewDomainFilterWithExclusions(cfg.DomainFilter, cfg.ExcludeDomains)
3✔
425
        }
3✔
426
}
427

428
// handleSigterm listens for a SIGTERM signal and triggers the provided cancel function
429
// to gracefully terminate the application. It logs a message when the signal is received.
430
func handleSigterm(cancel func()) {
1✔
431
        signals := make(chan os.Signal, 1)
1✔
432
        signal.Notify(signals, syscall.SIGTERM)
1✔
433
        <-signals
1✔
434
        log.Info("Received SIGTERM. Terminating...")
1✔
435
        cancel()
1✔
436
}
1✔
437

438
// serveMetrics starts an HTTP server that serves health and metrics endpoints.
439
// The /healthz endpoint returns a 200 OK status to indicate the service is healthy.
440
// The /metrics endpoint serves Prometheus metrics.
441
// The server listens on the specified address and logs debug information about the endpoints.
442
func serveMetrics(address string) {
1✔
443
        http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
2✔
444
                w.WriteHeader(http.StatusOK)
1✔
445
                _, _ = w.Write([]byte("OK"))
1✔
446
        })
1✔
447

448
        log.Debugf("serving 'healthz' on 'localhost:%s/healthz'", address)
1✔
449
        log.Debugf("serving 'metrics' on 'localhost:%s/metrics'", address)
1✔
450
        log.Debugf("registered '%d' metrics", len(metrics.RegisterMetric.Metrics))
1✔
451

1✔
452
        http.Handle("/metrics", promhttp.Handler())
1✔
453

1✔
454
        log.Fatal(http.ListenAndServe(address, nil))
1✔
455
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc