main.go 5.26 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
32
33
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"

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

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

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

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

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

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

Ian Smith's avatar
Ian Smith committed
69
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
	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)
101
	}
102

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

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

110
111
	var p provider.Provider
	switch cfg.Provider {
112
	case "google":
113
		p, err = provider.NewGoogleProvider(cfg.GoogleProject, cfg.DomainFilter, cfg.DryRun)
114
	case "aws":
115
		p, err = provider.NewAWSProvider(cfg.DomainFilter, cfg.DryRun)
Ian Smith's avatar
Ian Smith committed
116
117
	case "inmemory":
		p, err = provider.NewInMemoryProviderWithDomainAndLogging("example.com"), nil
118
	default:
119
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
120
	}
121
122
123
124
	if err != nil {
		log.Fatal(err)
	}

Yerken's avatar
Yerken committed
125
126
127
128
129
	var r registry.Registry
	switch cfg.Registry {
	case "noop":
		r, err = registry.NewNoopRegistry(p)
	case "txt":
130
		r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTOwnerID)
Yerken's avatar
Yerken committed
131
132
133
134
	default:
		log.Fatalf("unknown registry: %s", cfg.Registry)
	}

Yerken's avatar
Yerken committed
135
136
137
138
	if err != nil {
		log.Fatal(err)
	}

139
140
141
142
143
	policy, exists := plan.Policies[cfg.Policy]
	if !exists {
		log.Fatalf("unknown policy: %s", cfg.Policy)
	}

144
	ctrl := controller.Controller{
145
		Source:   endpointsSource,
Yerken's avatar
Yerken committed
146
		Registry: r,
147
		Policy:   policy,
148
		Interval: cfg.Interval,
149
150
	}

151
	if cfg.Once {
152
153
154
155
156
157
		err := ctrl.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		os.Exit(0)
158
159
	}

160
	ctrl.Run(stopChan)
161
	for {
162
		log.Info("Pod waiting to be deleted")
163
164
165
166
167
168
169
170
		time.Sleep(time.Second * 30)
	}
}

func handleSigterm(stopChan chan struct{}) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM)
	<-signals
171
	log.Info("Received SIGTERM. Terminating...")
172
173
	close(stopChan)
}
174
175

func newClient(cfg *externaldns.Config) (*kubernetes.Clientset, error) {
176
177
178
179
	if cfg.KubeConfig == "" {
		if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil {
			cfg.KubeConfig = clientcmd.RecommendedHomeFile
		}
180
181
	}

182
	config, err := clientcmd.BuildConfigFromFlags(cfg.Master, cfg.KubeConfig)
183
184
185
186
	if err != nil {
		return nil, err
	}

187
188
189
190
191
192
193
194
195
	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]
			},
		})
	}

196
197
198
199
200
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

201
202
	log.Infof("Connected to cluster at %s", config.Host)

203
204
	return client, nil
}
205
206
207
208
209
210
211
212
213
214
215

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