main.go 8.6 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
				BatchChangeSize:      cfg.AWSBatchChangeSize,
				BatchChangeInterval:  cfg.AWSBatchChangeInterval,
				EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth,
				AssumeRole:           cfg.AWSAssumeRole,
119
				APIRetries:           cfg.AWSAPIRetries,
120
				DryRun:               cfg.DryRun,
121
122
			},
		)
123
124
	case "aws-sd":
		// Check that only compatible Registry is used with AWS-SD
125
126
127
		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"
128
		}
129
		p, err = provider.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.AWSAssumeRole, cfg.DryRun)
130
	case "azure":
131
		p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
132
	case "cloudflare":
133
		p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun)
134
	case "google":
135
		p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
136
	case "digitalocean":
137
		p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
cliedeman's avatar
cliedeman committed
138
	case "linode":
139
		p, err = provider.NewLinodeProvider(domainFilter, cfg.DryRun, externaldns.Version)
140
	case "dnsimple":
141
		p, err = provider.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
142
143
144
145
	case "infoblox":
		p, err = provider.NewInfobloxProvider(
			provider.InfobloxConfig{
				DomainFilter: domainFilter,
146
				ZoneIDFilter: zoneIDFilter,
147
148
149
150
151
152
153
154
155
				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
156
157
158
	case "dyn":
		p, err = provider.NewDynProvider(
			provider.DynConfig{
159
160
161
162
163
164
165
166
				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
167
168
			},
		)
Stan Lagun's avatar
Stan Lagun committed
169
	case "coredns", "skydns":
170
		p, err = provider.NewCoreDNSProvider(domainFilter, cfg.DryRun)
171
	case "exoscale":
Christopher Schmidt's avatar
Christopher Schmidt committed
172
		p, err = provider.NewExoscaleProvider(cfg.ExoscaleEndpoint, cfg.ExoscaleAPIKey, cfg.ExoscaleAPISecret, cfg.DryRun, provider.ExoscaleWithDomain(domainFilter), provider.ExoscaleWithLogging()), nil
173
	case "inmemory":
Anhad Jai Singh's avatar
Anhad Jai Singh committed
174
		p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
Stan Lagun's avatar
Stan Lagun committed
175
176
	case "designate":
		p, err = provider.NewDesignateProvider(domainFilter, cfg.DryRun)
Anhad Jai Singh's avatar
Anhad Jai Singh committed
177
	case "pdns":
178
179
180
		p, err = provider.NewPDNSProvider(
			provider.PDNSConfig{
				DomainFilter: domainFilter,
Jason Hoch's avatar
gofmt    
Jason Hoch committed
181
182
183
				DryRun:       cfg.DryRun,
				Server:       cfg.PDNSServer,
				APIKey:       cfg.PDNSAPIKey,
184
				TLSConfig: provider.TLSConfig{
Jason Hoch's avatar
gofmt    
Jason Hoch committed
185
186
187
					TLSEnabled:            cfg.PDNSTLSEnabled,
					CAFilePath:            cfg.TLSCA,
					ClientCertFilePath:    cfg.TLSClientCert,
188
189
190
191
					ClientCertKeyFilePath: cfg.TLSClientCertKey,
				},
			},
		)
192
	case "oci":
Andrew Pryde's avatar
Andrew Pryde committed
193
194
195
196
197
		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
198
	case "rfc2136":
199
		p, err = provider.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, nil)
200
	default:
201
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
202
	}
203
204
205
206
	if err != nil {
		log.Fatal(err)
	}

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

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

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

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

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

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

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

254
255
256
257
258
259
260
261
262
263
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))
}