main.go 7.54 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
82
83
84
	// Lookup all the selected sources by names and pass them the desired configuration.
	sources, err := source.ByNames(&source.SingletonClientGenerator{
		KubeConfig: cfg.KubeConfig,
		KubeMaster: cfg.Master,
	}, cfg.Sources, sourceCfg)
85
86
87
88
	if err != nil {
		log.Fatal(err)
	}

89
	// Combine multiple sources into a single, deduplicated source.
90
	endpointsSource := source.NewDedupSource(source.NewMultiSource(sources))
91

92
	domainFilter := provider.NewDomainFilter(cfg.DomainFilter)
93
	zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
94
	zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
95

96
97
	var p provider.Provider
	switch cfg.Provider {
98
	case "aws":
99
100
101
102
103
104
105
106
107
108
		p, err = provider.NewAWSProvider(
			provider.AWSConfig{
				DomainFilter:   domainFilter,
				ZoneIDFilter:   zoneIDFilter,
				ZoneTypeFilter: zoneTypeFilter,
				MaxChangeCount: cfg.AWSMaxChangeCount,
				AssumeRole:     cfg.AWSAssumeRole,
				DryRun:         cfg.DryRun,
			},
		)
109
110
	case "aws-sd":
		// Check that only compatible Registry is used with AWS-SD
111
112
113
		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"
114
115
		}
		p, err = provider.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun)
116
	case "azure":
117
		p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun)
118
	case "cloudflare":
119
		p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun)
120
	case "google":
121
		p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun)
122
	case "digitalocean":
123
		p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
cliedeman's avatar
cliedeman committed
124
125
	case "linode":
		p, err = provider.NewLinodeProvider(domainFilter, cfg.DryRun)
126
	case "dnsimple":
127
		p, err = provider.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
128
129
130
131
	case "infoblox":
		p, err = provider.NewInfobloxProvider(
			provider.InfobloxConfig{
				DomainFilter: domainFilter,
132
				ZoneIDFilter: zoneIDFilter,
133
134
135
136
137
138
139
140
141
				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
142
143
144
	case "dyn":
		p, err = provider.NewDynProvider(
			provider.DynConfig{
145
146
147
148
149
150
151
152
				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
153
154
			},
		)
Stan Lagun's avatar
Stan Lagun committed
155
	case "coredns", "skydns":
156
		p, err = provider.NewCoreDNSProvider(domainFilter, cfg.DryRun)
157
	case "exoscale":
Christopher Schmidt's avatar
Christopher Schmidt committed
158
		p, err = provider.NewExoscaleProvider(cfg.ExoscaleEndpoint, cfg.ExoscaleAPIKey, cfg.ExoscaleAPISecret, cfg.DryRun, provider.ExoscaleWithDomain(domainFilter), provider.ExoscaleWithLogging()), nil
159
	case "inmemory":
Anhad Jai Singh's avatar
Anhad Jai Singh committed
160
		p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
Stan Lagun's avatar
Stan Lagun committed
161
162
	case "designate":
		p, err = provider.NewDesignateProvider(domainFilter, cfg.DryRun)
Anhad Jai Singh's avatar
Anhad Jai Singh committed
163
	case "pdns":
164
165
166
		p, err = provider.NewPDNSProvider(
			provider.PDNSConfig{
				DomainFilter: domainFilter,
Jason Hoch's avatar
gofmt    
Jason Hoch committed
167
168
169
				DryRun:       cfg.DryRun,
				Server:       cfg.PDNSServer,
				APIKey:       cfg.PDNSAPIKey,
170
				TLSConfig: provider.TLSConfig{
Jason Hoch's avatar
gofmt    
Jason Hoch committed
171
172
173
					TLSEnabled:            cfg.PDNSTLSEnabled,
					CAFilePath:            cfg.TLSCA,
					ClientCertFilePath:    cfg.TLSClientCert,
174
175
176
177
					ClientCertKeyFilePath: cfg.TLSClientCertKey,
				},
			},
		)
178
	case "oci":
Andrew Pryde's avatar
Andrew Pryde committed
179
180
181
182
183
		var config *provider.OCIConfig
		config, err = provider.LoadOCIConfig(cfg.OCIConfigFile)
		if err == nil {
			p, err = provider.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.DryRun)
		}
184
	default:
185
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
186
	}
187
188
189
190
	if err != nil {
		log.Fatal(err)
	}

Yerken's avatar
Yerken committed
191
192
193
194
195
	var r registry.Registry
	switch cfg.Registry {
	case "noop":
		r, err = registry.NewNoopRegistry(p)
	case "txt":
196
		r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTOwnerID, cfg.TXTCacheInterval)
197
	case "aws-sd":
198
		r, err = registry.NewAWSSDRegistry(p.(*provider.AWSSDProvider), cfg.TXTOwnerID)
Yerken's avatar
Yerken committed
199
200
201
202
	default:
		log.Fatalf("unknown registry: %s", cfg.Registry)
	}

Yerken's avatar
Yerken committed
203
204
205
206
	if err != nil {
		log.Fatal(err)
	}

207
208
209
210
211
	policy, exists := plan.Policies[cfg.Policy]
	if !exists {
		log.Fatalf("unknown policy: %s", cfg.Policy)
	}

212
	ctrl := controller.Controller{
213
		Source:   endpointsSource,
Yerken's avatar
Yerken committed
214
		Registry: r,
215
		Policy:   policy,
216
		Interval: cfg.Interval,
217
218
	}

219
	if cfg.Once {
220
221
222
223
224
225
		err := ctrl.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		os.Exit(0)
226
	}
227
	ctrl.Run(stopChan)
228
229
230
231
232
233
}

func handleSigterm(stopChan chan struct{}) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM)
	<-signals
234
	log.Info("Received SIGTERM. Terminating...")
235
236
	close(stopChan)
}
237

238
239
240
241
242
243
244
245
246
247
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))
}