main.go 8.58 KB
Newer Older
ideahitme's avatar
ideahitme committed
1
2
/*
Copyright 2017 The Kubernetes Authors.
3

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

ideahitme's avatar
ideahitme committed
8
    http://www.apache.org/licenses/LICENSE-2.0
9

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

17
18
19
20
21
22
23
24
package main

import (
	"net/http"
	"os"
	"os/signal"
	"syscall"

25
	"github.com/prometheus/client_golang/prometheus/promhttp"
26
	log "github.com/sirupsen/logrus"
27

28
	_ "k8s.io/client-go/plugin/pkg/client/auth"
29

30
	"github.com/kubernetes-incubator/external-dns/controller"
31
32
	"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns"
	"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns/validation"
33
	"github.com/kubernetes-incubator/external-dns/plan"
34
	"github.com/kubernetes-incubator/external-dns/provider"
Yerken's avatar
Yerken committed
35
	"github.com/kubernetes-incubator/external-dns/registry"
36
	"github.com/kubernetes-incubator/external-dns/source"
37
38
)

39
func main() {
40
	cfg := externaldns.NewConfig()
41
	if err := cfg.ParseFlags(os.Args[1:]); err != nil {
ideahitme's avatar
ideahitme committed
42
43
		log.Fatalf("flag parsing error: %v", err)
	}
44
	log.Infof("config: %s", cfg)
45

46
	if err := validation.ValidateConfig(cfg); err != nil {
47
		log.Fatalf("config validation failed: %v", err)
48
49
	}

ideahitme's avatar
ideahitme committed
50
	if cfg.LogFormat == "json" {
51
52
		log.SetFormatter(&log.JSONFormatter{})
	}
53
	if cfg.DryRun {
ideahitme's avatar
ideahitme committed
54
		log.Info("running in dry-run mode. No changes to DNS records will be made.")
55
	}
56
57
58
59

	ll, err := log.ParseLevel(cfg.LogLevel)
	if err != nil {
		log.Fatalf("failed to parse log level: %v", err)
60
	}
61
	log.SetLevel(ll)
62
63
64

	stopChan := make(chan struct{}, 1)

65
	go serveMetrics(cfg.MetricsAddress)
66
67
	go handleSigterm(stopChan)

68
69
	// Create a source.Config from the flags passed by the user.
	sourceCfg := &source.Config{
70
71
72
73
74
75
		Namespace:                cfg.Namespace,
		AnnotationFilter:         cfg.AnnotationFilter,
		FQDNTemplate:             cfg.FQDNTemplate,
		CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation,
		Compatibility:            cfg.Compatibility,
		PublishInternal:          cfg.PublishInternal,
76
		PublishHostIP:            cfg.PublishHostIP,
77
		ConnectorServer:          cfg.ConnectorSourceServer,
78
79
80
81
		CRDSourceAPIVersion:      cfg.CRDSourceAPIVersion,
		CRDSourceKind:            cfg.CRDSourceKind,
		KubeConfig:               cfg.KubeConfig,
		KubeMaster:               cfg.Master,
82
		ServiceTypeFilter:        cfg.ServiceTypeFilter,
83
		IstioIngressGateway:      cfg.IstioIngressGateway,
84
	}
85

86
87
	// Lookup all the selected sources by names and pass them the desired configuration.
	sources, err := source.ByNames(&source.SingletonClientGenerator{
88
89
90
		KubeConfig:     cfg.KubeConfig,
		KubeMaster:     cfg.Master,
		RequestTimeout: cfg.RequestTimeout,
91
	}, cfg.Sources, sourceCfg)
92
93
94
95
	if err != nil {
		log.Fatal(err)
	}

96
	// Combine multiple sources into a single, deduplicated source.
97
	endpointsSource := source.NewDedupSource(source.NewMultiSource(sources))
98

99
	domainFilter := provider.NewDomainFilter(cfg.DomainFilter)
100
	zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
101
	zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
Cesar Wong's avatar
Cesar Wong committed
102
	zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
103

104
105
	var p provider.Provider
	switch cfg.Provider {
Li Yi's avatar
Li Yi committed
106
107
	case "alibabacloud":
		p, err = provider.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun)
108
	case "aws":
109
110
		p, err = provider.NewAWSProvider(
			provider.AWSConfig{
111
112
113
				DomainFilter:         domainFilter,
				ZoneIDFilter:         zoneIDFilter,
				ZoneTypeFilter:       zoneTypeFilter,
Cesar Wong's avatar
Cesar Wong committed
114
				ZoneTagFilter:        zoneTagFilter,
115
116
117
118
119
				BatchChangeSize:      cfg.AWSBatchChangeSize,
				BatchChangeInterval:  cfg.AWSBatchChangeInterval,
				EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth,
				AssumeRole:           cfg.AWSAssumeRole,
				DryRun:               cfg.DryRun,
120
121
			},
		)
122
123
	case "aws-sd":
		// Check that only compatible Registry is used with AWS-SD
124
125
126
		if cfg.Registry != "noop" && cfg.Registry != "aws-sd" {
			log.Infof("Registry \"%s\" cannot be used with AWS ServiceDiscovery. Switching to \"aws-sd\".", cfg.Registry)
			cfg.Registry = "aws-sd"
127
		}
128
		p, err = provider.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.AWSAssumeRole, cfg.DryRun)
129
	case "azure":
130
		p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
131
	case "cloudflare":
njuettner's avatar
njuettner committed
132
		p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun)
133
	case "google":
134
		p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
135
	case "digitalocean":
136
		p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
cliedeman's avatar
cliedeman committed
137
	case "linode":
138
		p, err = provider.NewLinodeProvider(domainFilter, cfg.DryRun, externaldns.Version)
139
	case "dnsimple":
140
		p, err = provider.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
141
142
143
144
	case "infoblox":
		p, err = provider.NewInfobloxProvider(
			provider.InfobloxConfig{
				DomainFilter: domainFilter,
145
				ZoneIDFilter: zoneIDFilter,
146
147
148
149
150
151
152
153
154
				Host:         cfg.InfobloxGridHost,
				Port:         cfg.InfobloxWapiPort,
				Username:     cfg.InfobloxWapiUsername,
				Password:     cfg.InfobloxWapiPassword,
				Version:      cfg.InfobloxWapiVersion,
				SSLVerify:    cfg.InfobloxSSLVerify,
				DryRun:       cfg.DryRun,
			},
		)
Julian Vassev's avatar
Julian Vassev committed
155
156
157
	case "dyn":
		p, err = provider.NewDynProvider(
			provider.DynConfig{
158
159
160
161
162
163
164
165
				DomainFilter:  domainFilter,
				ZoneIDFilter:  zoneIDFilter,
				DryRun:        cfg.DryRun,
				CustomerName:  cfg.DynCustomerName,
				Username:      cfg.DynUsername,
				Password:      cfg.DynPassword,
				MinTTLSeconds: cfg.DynMinTTLSeconds,
				AppVersion:    externaldns.Version,
Julian Vassev's avatar
Julian Vassev committed
166
167
			},
		)
Stan Lagun's avatar
Stan Lagun committed
168
	case "coredns", "skydns":
169
		p, err = provider.NewCoreDNSProvider(domainFilter, cfg.DryRun)
170
	case "exoscale":
Christopher Schmidt's avatar
Christopher Schmidt committed
171
		p, err = provider.NewExoscaleProvider(cfg.ExoscaleEndpoint, cfg.ExoscaleAPIKey, cfg.ExoscaleAPISecret, cfg.DryRun, provider.ExoscaleWithDomain(domainFilter), provider.ExoscaleWithLogging()), nil
172
	case "inmemory":
Anhad Jai Singh's avatar
Anhad Jai Singh committed
173
		p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
Stan Lagun's avatar
Stan Lagun committed
174
175
	case "designate":
		p, err = provider.NewDesignateProvider(domainFilter, cfg.DryRun)
Anhad Jai Singh's avatar
Anhad Jai Singh committed
176
	case "pdns":
177
178
179
		p, err = provider.NewPDNSProvider(
			provider.PDNSConfig{
				DomainFilter: domainFilter,
Jason Hoch's avatar
gofmt    
Jason Hoch committed
180
181
182
				DryRun:       cfg.DryRun,
				Server:       cfg.PDNSServer,
				APIKey:       cfg.PDNSAPIKey,
183
				TLSConfig: provider.TLSConfig{
Jason Hoch's avatar
gofmt    
Jason Hoch committed
184
185
186
					TLSEnabled:            cfg.PDNSTLSEnabled,
					CAFilePath:            cfg.TLSCA,
					ClientCertFilePath:    cfg.TLSClientCert,
187
188
189
190
					ClientCertKeyFilePath: cfg.TLSClientCertKey,
				},
			},
		)
191
	case "oci":
Andrew Pryde's avatar
Andrew Pryde committed
192
193
194
195
196
		var config *provider.OCIConfig
		config, err = provider.LoadOCIConfig(cfg.OCIConfigFile)
		if err == nil {
			p, err = provider.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.DryRun)
		}
Vladislav Troinich's avatar
Vladislav Troinich committed
197
	case "rfc2136":
198
		p, err = provider.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, nil)
199
	default:
200
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
201
	}
202
203
204
205
	if err != nil {
		log.Fatal(err)
	}

Yerken's avatar
Yerken committed
206
207
208
209
210
	var r registry.Registry
	switch cfg.Registry {
	case "noop":
		r, err = registry.NewNoopRegistry(p)
	case "txt":
211
		r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTOwnerID, cfg.TXTCacheInterval)
212
	case "aws-sd":
213
		r, err = registry.NewAWSSDRegistry(p.(*provider.AWSSDProvider), cfg.TXTOwnerID)
Yerken's avatar
Yerken committed
214
215
216
217
	default:
		log.Fatalf("unknown registry: %s", cfg.Registry)
	}

Yerken's avatar
Yerken committed
218
219
220
221
	if err != nil {
		log.Fatal(err)
	}

222
223
224
225
226
	policy, exists := plan.Policies[cfg.Policy]
	if !exists {
		log.Fatalf("unknown policy: %s", cfg.Policy)
	}

227
	ctrl := controller.Controller{
228
		Source:   endpointsSource,
Yerken's avatar
Yerken committed
229
		Registry: r,
230
		Policy:   policy,
231
		Interval: cfg.Interval,
232
233
	}

234
	if cfg.Once {
235
236
237
238
239
240
		err := ctrl.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		os.Exit(0)
241
	}
242
	ctrl.Run(stopChan)
243
244
245
246
247
248
}

func handleSigterm(stopChan chan struct{}) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM)
	<-signals
249
	log.Info("Received SIGTERM. Terminating...")
250
251
	close(stopChan)
}
252

253
254
255
256
257
258
259
260
261
262
func serveMetrics(address string) {
	http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))
	})

	http.Handle("/metrics", promhttp.Handler())

	log.Fatal(http.ListenAndServe(address, nil))
}