main.go 5.56 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
package main

import (
	"net/http"
	"os"
	"os/signal"
23
	"strings"
24
25
26
27
	"syscall"
	"time"

	log "github.com/Sirupsen/logrus"
28
	"github.com/linki/instrumented_http"
29
	"github.com/prometheus/client_golang/prometheus/promhttp"
30

31
	"k8s.io/client-go/kubernetes"
32
	_ "k8s.io/client-go/plugin/pkg/client/auth"
33
34
	"k8s.io/client-go/tools/clientcmd"

35
	"github.com/kubernetes-incubator/external-dns/controller"
36
37
	"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns"
	"github.com/kubernetes-incubator/external-dns/pkg/apis/externaldns/validation"
38
	"github.com/kubernetes-incubator/external-dns/plan"
39
	"github.com/kubernetes-incubator/external-dns/provider"
Yerken's avatar
Yerken committed
40
	"github.com/kubernetes-incubator/external-dns/registry"
41
	"github.com/kubernetes-incubator/external-dns/source"
42
43
)

44
func main() {
45
	cfg := externaldns.NewConfig()
46
	if err := cfg.ParseFlags(os.Args[1:]); err != nil {
ideahitme's avatar
ideahitme committed
47
48
		log.Fatalf("flag parsing error: %v", err)
	}
49
50
	log.Infof("config: %+v", cfg)

51
	if err := validation.ValidateConfig(cfg); err != nil {
52
		log.Fatalf("config validation failed: %v", err)
53
54
	}

ideahitme's avatar
ideahitme committed
55
	if cfg.LogFormat == "json" {
56
57
		log.SetFormatter(&log.JSONFormatter{})
	}
58
	if cfg.DryRun {
ideahitme's avatar
ideahitme committed
59
		log.Info("running in dry-run mode. No changes to DNS records will be made.")
60
	}
ideahitme's avatar
ideahitme committed
61
	if cfg.Debug {
62
63
64
65
66
		log.SetLevel(log.DebugLevel)
	}

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

67
	go serveMetrics(cfg.MetricsAddress)
68
69
	go handleSigterm(stopChan)

Ian Smith's avatar
Ian Smith committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
	var client *kubernetes.Clientset

	// create only those services we explicitly ask for in cfg.Sources
	for _, sourceType := range cfg.Sources {
		// we only need a k8s client if we're creating a non-fake source, and
		// have not already instantiated a k8s client
		if sourceType != "fake" && client == nil {
			var err error
			client, err = newClient(cfg)
			if err != nil {
				log.Fatal(err)
			}
		}

		var src source.Source
		var err error
		switch sourceType {
		case "fake":
			src, err = source.NewFakeSource(cfg.FqdnTemplate)
		case "service":
			src, err = source.NewServiceSource(client, cfg.Namespace, cfg.FqdnTemplate, cfg.Compatibility)
		case "ingress":
			src, err = source.NewIngressSource(client, cfg.Namespace, cfg.FqdnTemplate)
		default:
			log.Fatalf("Don't know how to handle sourceType '%s'", sourceType)
		}

		if err != nil {
			log.Fatal(err)
		}

		source.Register(sourceType, src)
102
	}
103

104
105
106
107
108
	sources, err := source.LookupMultiple(cfg.Sources)
	if err != nil {
		log.Fatal(err)
	}

109
	endpointsSource := source.NewDedupSource(source.NewMultiSource(sources))
110

111
112
	var p provider.Provider
	switch cfg.Provider {
113
	case "google":
114
		p, err = provider.NewGoogleProvider(cfg.GoogleProject, cfg.DomainFilter, cfg.DryRun)
115
	case "aws":
116
		p, err = provider.NewAWSProvider(cfg.DomainFilter, cfg.DryRun)
117
118
	case "digitalocean":
		p, err = provider.NewDigitalOceanProvider(cfg.DomainFilter, cfg.DryRun)
119
120
	case "azure":
		p, err = provider.NewAzureProvider(cfg.AzureConfigFile, cfg.DomainFilter, cfg.AzureResourceGroup, cfg.DryRun)
121
122
	case "inmemory":
		p, err = provider.NewInMemoryProvider(provider.InMemoryWithDomain(cfg.DomainFilter), provider.InMemoryWithLogging()), nil
123
	default:
124
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
125
	}
126
127
128
129
	if err != nil {
		log.Fatal(err)
	}

Yerken's avatar
Yerken committed
130
131
132
133
134
	var r registry.Registry
	switch cfg.Registry {
	case "noop":
		r, err = registry.NewNoopRegistry(p)
	case "txt":
135
		r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTOwnerID)
Yerken's avatar
Yerken committed
136
137
138
139
	default:
		log.Fatalf("unknown registry: %s", cfg.Registry)
	}

Yerken's avatar
Yerken committed
140
141
142
143
	if err != nil {
		log.Fatal(err)
	}

144
145
146
147
148
	policy, exists := plan.Policies[cfg.Policy]
	if !exists {
		log.Fatalf("unknown policy: %s", cfg.Policy)
	}

149
	ctrl := controller.Controller{
150
		Source:   endpointsSource,
Yerken's avatar
Yerken committed
151
		Registry: r,
152
		Policy:   policy,
153
		Interval: cfg.Interval,
154
155
	}

156
	if cfg.Once {
157
158
159
160
161
162
		err := ctrl.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		os.Exit(0)
163
164
	}

165
	ctrl.Run(stopChan)
166
	for {
167
		log.Info("Pod waiting to be deleted")
168
169
170
171
172
173
174
175
		time.Sleep(time.Second * 30)
	}
}

func handleSigterm(stopChan chan struct{}) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM)
	<-signals
176
	log.Info("Received SIGTERM. Terminating...")
177
178
	close(stopChan)
}
179
180

func newClient(cfg *externaldns.Config) (*kubernetes.Clientset, error) {
181
182
183
184
	if cfg.KubeConfig == "" {
		if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil {
			cfg.KubeConfig = clientcmd.RecommendedHomeFile
		}
185
186
	}

187
	config, err := clientcmd.BuildConfigFromFlags(cfg.Master, cfg.KubeConfig)
188
189
190
191
	if err != nil {
		return nil, err
	}

192
193
194
195
196
197
198
199
200
	config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
		return instrumented_http.NewTransport(rt, &instrumented_http.Callbacks{
			PathProcessor: func(path string) string {
				parts := strings.Split(path, "/")
				return parts[len(parts)-1]
			},
		})
	}

201
202
203
204
205
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

206
207
	log.Infof("Connected to cluster at %s", config.Host)

208
209
	return client, nil
}
210
211
212
213
214
215
216
217
218
219
220

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))
}