main.go 4.58 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
25
26
package main

import (
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	log "github.com/Sirupsen/logrus"
27
	"github.com/prometheus/client_golang/prometheus/promhttp"
28

29
	_ "k8s.io/client-go/plugin/pkg/client/auth"
30

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

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

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

ideahitme's avatar
ideahitme committed
51
	if cfg.LogFormat == "json" {
52
53
		log.SetFormatter(&log.JSONFormatter{})
	}
54
	if cfg.DryRun {
ideahitme's avatar
ideahitme committed
55
		log.Info("running in dry-run mode. No changes to DNS records will be made.")
56
	}
57
58
59
60

	ll, err := log.ParseLevel(cfg.LogLevel)
	if err != nil {
		log.Fatalf("failed to parse log level: %v", err)
61
	}
62
	log.SetLevel(ll)
63
64
65

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

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

69
70
	// Create a source.Config from the flags passed by the user.
	sourceCfg := &source.Config{
71
72
73
74
		Namespace:       cfg.Namespace,
		FQDNTemplate:    cfg.FQDNTemplate,
		Compatibility:   cfg.Compatibility,
		PublishInternal: cfg.PublishInternal,
75
	}
76

77
78
79
80
81
	// 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)
82
83
84
85
	if err != nil {
		log.Fatal(err)
	}

86
	// Combine multiple sources into a single, deduplicated source.
87
	endpointsSource := source.NewDedupSource(source.NewMultiSource(sources))
88

89
	domainFilter := provider.NewDomainFilter(cfg.DomainFilter)
90
	zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
91

92
93
	var p provider.Provider
	switch cfg.Provider {
94
	case "aws":
95
		p, err = provider.NewAWSProvider(domainFilter, zoneTypeFilter, cfg.DryRun)
96
	case "azure":
97
		p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, cfg.AzureResourceGroup, cfg.DryRun)
98
	case "cloudflare":
99
		p, err = provider.NewCloudFlareProvider(domainFilter, cfg.CloudflareProxied, cfg.DryRun)
100
	case "google":
101
		p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, cfg.DryRun)
102
	case "digitalocean":
103
		p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun)
104
	case "inmemory":
105
		p, err = provider.NewInMemoryProvider(provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
106
	default:
107
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
108
	}
109
110
111
112
	if err != nil {
		log.Fatal(err)
	}

Yerken's avatar
Yerken committed
113
114
115
116
117
	var r registry.Registry
	switch cfg.Registry {
	case "noop":
		r, err = registry.NewNoopRegistry(p)
	case "txt":
118
		r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTOwnerID)
Yerken's avatar
Yerken committed
119
120
121
122
	default:
		log.Fatalf("unknown registry: %s", cfg.Registry)
	}

Yerken's avatar
Yerken committed
123
124
125
126
	if err != nil {
		log.Fatal(err)
	}

127
128
129
130
131
	policy, exists := plan.Policies[cfg.Policy]
	if !exists {
		log.Fatalf("unknown policy: %s", cfg.Policy)
	}

132
	ctrl := controller.Controller{
133
		Source:   endpointsSource,
Yerken's avatar
Yerken committed
134
		Registry: r,
135
		Policy:   policy,
136
		Interval: cfg.Interval,
137
138
	}

139
	if cfg.Once {
140
141
142
143
144
145
		err := ctrl.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		os.Exit(0)
146
147
	}

148
	ctrl.Run(stopChan)
149
	for {
150
		log.Info("Pod waiting to be deleted")
151
152
153
154
155
156
157
158
		time.Sleep(time.Second * 30)
	}
}

func handleSigterm(stopChan chan struct{}) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM)
	<-signals
159
	log.Info("Received SIGTERM. Terminating...")
160
161
	close(stopChan)
}
162

163
164
165
166
167
168
169
170
171
172
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))
}