main.go 7.46 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)
124
	case "dnsimple":
125
		p, err = provider.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
126
127
128
129
	case "infoblox":
		p, err = provider.NewInfobloxProvider(
			provider.InfobloxConfig{
				DomainFilter: domainFilter,
130
				ZoneIDFilter: zoneIDFilter,
131
132
133
134
135
136
137
138
139
				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
140
141
142
	case "dyn":
		p, err = provider.NewDynProvider(
			provider.DynConfig{
143
144
145
146
147
148
149
150
				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
151
152
			},
		)
Stan Lagun's avatar
Stan Lagun committed
153
	case "coredns", "skydns":
154
		p, err = provider.NewCoreDNSProvider(domainFilter, cfg.DryRun)
155
	case "exoscale":
Christopher Schmidt's avatar
Christopher Schmidt committed
156
		p, err = provider.NewExoscaleProvider(cfg.ExoscaleEndpoint, cfg.ExoscaleAPIKey, cfg.ExoscaleAPISecret, cfg.DryRun, provider.ExoscaleWithDomain(domainFilter), provider.ExoscaleWithLogging()), nil
157
	case "inmemory":
Anhad Jai Singh's avatar
Anhad Jai Singh committed
158
		p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
Stan Lagun's avatar
Stan Lagun committed
159
160
	case "designate":
		p, err = provider.NewDesignateProvider(domainFilter, cfg.DryRun)
Anhad Jai Singh's avatar
Anhad Jai Singh committed
161
	case "pdns":
162
163
164
		p, err = provider.NewPDNSProvider(
			provider.PDNSConfig{
				DomainFilter: domainFilter,
Jason Hoch's avatar
gofmt    
Jason Hoch committed
165
166
167
				DryRun:       cfg.DryRun,
				Server:       cfg.PDNSServer,
				APIKey:       cfg.PDNSAPIKey,
168
				TLSConfig: provider.TLSConfig{
Jason Hoch's avatar
gofmt    
Jason Hoch committed
169
170
171
					TLSEnabled:            cfg.PDNSTLSEnabled,
					CAFilePath:            cfg.TLSCA,
					ClientCertFilePath:    cfg.TLSClientCert,
172
173
174
175
					ClientCertKeyFilePath: cfg.TLSClientCertKey,
				},
			},
		)
176
	case "oci":
Andrew Pryde's avatar
Andrew Pryde committed
177
178
179
180
181
		var config *provider.OCIConfig
		config, err = provider.LoadOCIConfig(cfg.OCIConfigFile)
		if err == nil {
			p, err = provider.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.DryRun)
		}
182
	default:
183
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
184
	}
185
186
187
188
	if err != nil {
		log.Fatal(err)
	}

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

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

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

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

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

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

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

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