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

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

26
	"github.com/prometheus/client_golang/prometheus/promhttp"
27
	log "github.com/sirupsen/logrus"
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)
	}
Henning Jacobs's avatar
Henning Jacobs committed
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 105
	case "dnsimple":
		p, err = provider.NewDnsimpleProvider(domainFilter, cfg.DryRun)
106
	case "inmemory":
107
		p, err = provider.NewInMemoryProvider(provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil
108
	default:
109
		log.Fatalf("unknown dns provider: %s", cfg.Provider)
110
	}
111 112 113 114
	if err != nil {
		log.Fatal(err)
	}

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

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

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

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

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

		os.Exit(0)
148 149
	}

150
	ctrl.Run(stopChan)
151
	for {
Yerken's avatar
Yerken committed
152
		log.Info("Pod waiting to be deleted")
153 154 155 156 157 158 159 160
		time.Sleep(time.Second * 30)
	}
}

func handleSigterm(stopChan chan struct{}) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM)
	<-signals
Yerken's avatar
Yerken committed
161
	log.Info("Received SIGTERM. Terminating...")
162 163
	close(stopChan)
}
164

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