kops/dns-controller/cmd/dns-controller/main.go

223 lines
7.2 KiB
Go

/*
Copyright 2016 The Kubernetes Authors.
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
http://www.apache.org/licenses/LICENSE-2.0
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.
*/
package main
import (
"bytes"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/pflag"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
"k8s.io/kops/dns-controller/pkg/dns"
"k8s.io/kops/dns-controller/pkg/watchers"
"k8s.io/kops/dnsprovider/pkg/dnsprovider"
"k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/aws/route53"
k8scoredns "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/coredns"
_ "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/google/clouddns"
_ "k8s.io/kops/pkg/resources/digitalocean/dns"
"k8s.io/kops/protokube/pkg/gossip"
gossipdns "k8s.io/kops/protokube/pkg/gossip/dns"
gossipdnsprovider "k8s.io/kops/protokube/pkg/gossip/dns/provider"
"k8s.io/kops/protokube/pkg/gossip/mesh"
)
var (
flags = pflag.NewFlagSet("", pflag.ExitOnError)
BuildVersion = "0.1"
)
func main() {
fmt.Printf("dns-controller version %s\n", BuildVersion)
var dnsServer, dnsProviderID, gossipListen, gossipSecret, watchNamespace, metricsListen string
var gossipSeeds, zones []string
var watchIngress bool
var updateInterval int
// Be sure to get the glog flags
glog.Flush()
flag.StringVar(&dnsServer, "dns-server", "", "DNS Server")
flags.BoolVar(&watchIngress, "watch-ingress", true, "Configure hostnames found in ingress resources")
flags.StringSliceVar(&gossipSeeds, "gossip-seed", gossipSeeds, "If set, will enable gossip zones and seed using the provided addresses")
flags.StringSliceVarP(&zones, "zone", "z", []string{}, "Configure permitted zones and their mappings")
flags.StringVar(&dnsProviderID, "dns", "aws-route53", "DNS provider we should use (aws-route53, google-clouddns, digitalocean, coredns, gossip)")
flags.StringVar(&gossipListen, "gossip-listen", "0.0.0.0:3998", "The address on which to listen if gossip is enabled")
flags.StringVar(&gossipSecret, "gossip-secret", gossipSecret, "Secret to use to secure gossip")
flags.StringVar(&watchNamespace, "watch-namespace", "", "Limits the functionality for pods, services and ingress to specific namespace, by default all")
flag.IntVar(&route53.MaxBatchSize, "route53-batch-size", route53.MaxBatchSize, "Maximum number of operations performed per changeset batch")
flag.StringVar(&metricsListen, "metrics-listen", "", "The address on which to listen for Prometheus metrics.")
flags.IntVar(&updateInterval, "update-interval", 5, "Configure interval at which to update DNS records.")
// Trick to avoid 'logging before flag.Parse' warning
flag.CommandLine.Parse([]string{})
flag.Set("logtostderr", "true")
flags.AddGoFlagSet(flag.CommandLine)
flags.Parse(os.Args)
if metricsListen != "" {
go func() {
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(metricsListen, nil))
}()
}
zoneRules, err := dns.ParseZoneRules(zones)
if err != nil {
glog.Errorf("unexpected zone flags: %q", err)
os.Exit(1)
}
config, err := rest.InClusterConfig()
if err != nil {
glog.Errorf("error building client configuration: %v", err)
os.Exit(1)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
glog.Fatalf("error building REST client: %v", err)
}
var dnsProviders []dnsprovider.Interface
if dnsProviderID != "gossip" {
var file io.Reader
if dnsProviderID == k8scoredns.ProviderName {
var lines []string
lines = append(lines, "etcd-endpoints = "+dnsServer)
lines = append(lines, "zones = "+zones[0])
config := "[global]\n" + strings.Join(lines, "\n") + "\n"
file = bytes.NewReader([]byte(config))
}
dnsProvider, err := dnsprovider.GetDnsProvider(dnsProviderID, file)
if err != nil {
glog.Errorf("Error initializing DNS provider %q: %v", dnsProviderID, err)
os.Exit(1)
}
if dnsProvider == nil {
glog.Errorf("DNS provider was nil %q: %v", dnsProviderID, err)
os.Exit(1)
}
dnsProviders = append(dnsProviders, dnsProvider)
}
if len(gossipSeeds) != 0 {
gossipSeeds := gossip.NewStaticSeedProvider(gossipSeeds)
id := os.Getenv("HOSTNAME")
if id == "" {
glog.Fatalf("Unable to fetch HOSTNAME for use as node identifier")
}
gossipName := "dns-controller." + id
channelName := "dns"
gossipState, err := mesh.NewMeshGossiper(gossipListen, channelName, gossipName, []byte(gossipSecret), gossipSeeds)
if err != nil {
glog.Errorf("Error initializing gossip: %v", err)
os.Exit(1)
}
go func() {
err := gossipState.Start()
if err != nil {
glog.Fatalf("gossip exited unexpectedly: %v", err)
} else {
glog.Fatalf("gossip exited unexpectedly, but without error")
}
}()
dnsView := gossipdns.NewDNSView(gossipState)
dnsProvider, err := gossipdnsprovider.New(dnsView)
if err != nil {
glog.Errorf("Error initializing gossip DNS provider: %v", err)
os.Exit(1)
}
if dnsProvider == nil {
glog.Errorf("Gossip DNS provider was nil: %v", err)
os.Exit(1)
}
dnsProviders = append(dnsProviders, dnsProvider)
}
dnsController, err := dns.NewDNSController(dnsProviders, zoneRules, updateInterval)
if err != nil {
glog.Errorf("Error building DNS controller: %v", err)
os.Exit(1)
}
// @step: initialize the watchers
if err := initializeWatchers(client, dnsController, watchNamespace, watchIngress); err != nil {
glog.Errorf("%s", err)
os.Exit(1)
}
// start and wait on the dns controller
dnsController.Run()
}
// initializeWatchers is responsible for creating the watchers
func initializeWatchers(client kubernetes.Interface, dnsctl *dns.DNSController, namespace string, watchIngress bool) error {
glog.V(1).Infof("initializing the watch controllers, namespace: %q", namespace)
nodeController, err := watchers.NewNodeController(client, dnsctl)
if err != nil {
return fmt.Errorf("failed to initialize the node controller, error: %v", err)
}
podController, err := watchers.NewPodController(client, dnsctl, namespace)
if err != nil {
return fmt.Errorf("failed to initialize the pod controller, error: %v", err)
}
serviceController, err := watchers.NewServiceController(client, dnsctl, namespace)
if err != nil {
return fmt.Errorf("failed to initialize the service controller, error: %v", err)
}
var ingressController *watchers.IngressController
if watchIngress {
ingressController, err = watchers.NewIngressController(client, dnsctl, namespace)
if err != nil {
return fmt.Errorf("failed to initialize the ingress controller, error: %v", err)
}
} else {
glog.Infof("Ingress controller disabled")
}
go nodeController.Run()
go podController.Run()
go serviceController.Run()
if watchIngress {
go ingressController.Run()
}
return nil
}