diff --git a/Makefile b/Makefile index 1ffb471b61..9ce4f36151 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,9 @@ GOVERSION=1.6 # See http://stackoverflow.com/questions/18136918/how-to-get-current-relative-directory-of-your-makefile MAKEDIR:=$(strip $(shell dirname "$(realpath $(lastword $(MAKEFILE_LIST)))")) -TAG=1.4 +# Keep in sync with upup/models/cloudup/resources/addons/dns-controller/ +DNS_CONTROLLER_TAG=1.4.1 +PROTOKUBE_TAG=1.4.0 ifndef VERSION VERSION := git-$(shell git describe --always) @@ -123,10 +125,10 @@ protokube-build-in-docker: protokube-builder-image docker run -it -e VERSION=${VERSION} -v `pwd`:/src protokube-builder /onbuild.sh protokube-image: protokube-build-in-docker - docker build -t ${DOCKER_REGISTRY}/protokube:${TAG} -f images/protokube/Dockerfile . + docker build -t ${DOCKER_REGISTRY}/protokube:${PROTOKUBE_TAG} -f images/protokube/Dockerfile . protokube-push: protokube-image - docker push ${DOCKER_REGISTRY}/protokube:${TAG} + docker push ${DOCKER_REGISTRY}/protokube:${PROTOKUBE_TAG} @@ -154,10 +156,10 @@ dns-controller-build-in-docker: dns-controller-builder-image docker run -it -e VERSION=${VERSION} -v `pwd`:/src dns-controller-builder /onbuild.sh dns-controller-image: dns-controller-build-in-docker - docker build -t ${DOCKER_REGISTRY}/dns-controller:${TAG} -f images/dns-controller/Dockerfile . + docker build -t ${DOCKER_REGISTRY}/dns-controller:${DNS_CONTROLLER_TAG} -f images/dns-controller/Dockerfile . dns-controller-push: dns-controller-image - docker push ${DOCKER_REGISTRY}/dns-controller:${TAG} + docker push ${DOCKER_REGISTRY}/dns-controller:${DNS_CONTROLLER_TAG} # -------------------------------------------------- # development targets diff --git a/dns-controller/cmd/dns-controller/main.go b/dns-controller/cmd/dns-controller/main.go index 72df524b89..bdd223327f 100644 --- a/dns-controller/cmd/dns-controller/main.go +++ b/dns-controller/cmd/dns-controller/main.go @@ -24,6 +24,9 @@ func main() { dnsProviderId := "aws-route53" flags.StringVar(&dnsProviderId, "dns", dnsProviderId, "DNS provider we should use (aws-route53, google-clouddns)") + var zones []string + flags.StringSliceVarP(&zones, "zone", "z", []string{}, "Configure permitted zones and their mappings") + watchIngress := true flags.BoolVar(&watchIngress, "watch-ingress", watchIngress, "Configure hostnames found in ingress resources") @@ -37,6 +40,12 @@ func main() { flags.Parse(os.Args) + zoneRules, err := dns.ParseZoneRules(zones) + if err != nil { + glog.Errorf("unexpected zone flags: %q", err) + os.Exit(1) + } + config, err := clientConfig.ClientConfig() if err != nil { glog.Errorf("error building client configuration: %v", err) @@ -63,7 +72,7 @@ func main() { os.Exit(1) } - dnsController, err := dns.NewDNSController(dnsProvider) + dnsController, err := dns.NewDNSController(dnsProvider, zoneRules) if err != nil { glog.Errorf("Error building DNS controller: %v", err) os.Exit(1) diff --git a/dns-controller/docs/flags.md b/dns-controller/docs/flags.md new file mode 100644 index 0000000000..4685717bf5 --- /dev/null +++ b/dns-controller/docs/flags.md @@ -0,0 +1,16 @@ +## zone + +Pass a list of zones to determine which names can be updated. Zones not permitted will be ignored +(but the default is to allow all zones). + +The following syntax options are recognized: + +`*` or `*/*` wildcard allowing all zones to be updated. The default if no zones are specified, but +can also be used if some zones must be explicitly mapped. + +`example.com` to permit updates in this zone, specified by name. Use the ID syntax +if there are multiple zones with the same name. + +`*/id` to permit updates in a zone, by id. + +`example.com/id` to permit updates in the zone named example.com, by id. diff --git a/dns-controller/pkg/dns/dnscontroller.go b/dns-controller/pkg/dns/dnscontroller.go index 0ad9665f5a..6e7ab3d7de 100644 --- a/dns-controller/pkg/dns/dnscontroller.go +++ b/dns-controller/pkg/dns/dnscontroller.go @@ -18,6 +18,8 @@ import ( // DNSController applies the desired DNS state to the DNS backend type DNSController struct { + zoneRules *ZoneRules + util.Stoppable // zones is the DNS provider @@ -59,13 +61,14 @@ type DNSControllerScope struct { var _ Scope = &DNSControllerScope{} // NewDnsController creates a DnsController -func NewDNSController(provider dnsprovider.Interface) (*DNSController, error) { +func NewDNSController(provider dnsprovider.Interface, zoneRules *ZoneRules) (*DNSController, error) { if provider == nil { return nil, fmt.Errorf("must pass provider") } c := &DNSController{ - scopes: make(map[string]*DNSControllerScope), + scopes: make(map[string]*DNSControllerScope), + zoneRules: zoneRules, } zones, ok := provider.Zones() @@ -213,7 +216,7 @@ func (c *DNSController) runOnce() error { oldValueMap = c.lastSuccessfulSnapshot.recordValues } - op, err := newDNSOp(c.zones) + op, err := newDNSOp(c.zoneRules, c.zones) if err != nil { return err } @@ -275,7 +278,7 @@ type dnsOp struct { zones map[string]dnsprovider.Zone } -func newDNSOp(zonesProvider dnsprovider.Zones) (*dnsOp, error) { +func newDNSOp(zoneRules *ZoneRules, zonesProvider dnsprovider.Zones) (*dnsOp, error) { o := &dnsOp{ zonesProvider: zonesProvider, } @@ -284,10 +287,34 @@ func newDNSOp(zonesProvider dnsprovider.Zones) (*dnsOp, error) { if err != nil { return nil, fmt.Errorf("error querying for zones: %v", err) } - zoneMap := make(map[string]dnsprovider.Zone) + + // First we build up a map of all zones by name, + // then we go through and pick the "correct" zone for each name + allZoneMap := make(map[string][]dnsprovider.Zone) for _, zone := range zones { name := EnsureDotSuffix(zone.Name()) - zoneMap[name] = zone + allZoneMap[name] = append(allZoneMap[name], zone) + } + + zoneMap := make(map[string]dnsprovider.Zone) + for name, zones := range allZoneMap { + var matches []dnsprovider.Zone + for _, zone := range zones { + if zoneRules.MatchesExplicitly(zone) { + matches = append(matches, zone) + } + } + + if len(matches) == 0 && zoneRules.Wildcard { + // No explicit matches but wildcard; treat everything as matching + matches = append(matches, zones...) + } + + if len(matches) == 1 { + zoneMap[name] = matches[0] + } else if len(matches) > 1 { + glog.Warningf("Found multiple zones for name %q, won't manage zone (To fix: provide zone mapping flag with ID of zone)", name) + } } o.zones = zoneMap diff --git a/dns-controller/pkg/dns/zonespec.go b/dns-controller/pkg/dns/zonespec.go new file mode 100644 index 0000000000..6edbc00c26 --- /dev/null +++ b/dns-controller/pkg/dns/zonespec.go @@ -0,0 +1,83 @@ +package dns + +import ( + "fmt" + "github.com/golang/glog" + "k8s.io/kubernetes/federation/pkg/dnsprovider" + "strings" +) + +type ZoneSpec struct { + Name string + ID string +} + +func ParseZoneSpec(s string) (*ZoneSpec, error) { + s = strings.TrimSpace(s) + + tokens := strings.SplitN(s, "/", 2) + if len(tokens) == 2 && tokens[0] == "*" { + // */1234: Match by ID + return &ZoneSpec{ID: tokens[1]}, nil + } + name := EnsureDotSuffix(tokens[0]) + if len(tokens) == 1 { + // example.com: Match by name + return &ZoneSpec{Name: name}, nil + } + + // example.com/1234: Match by name & id + return &ZoneSpec{Name: name, ID: tokens[1]}, nil +} + +type ZoneRules struct { + // We don't use a map so we can support e.g. *.example.com later + Zones []*ZoneSpec + Wildcard bool +} + +func ParseZoneRules(zones []string) (*ZoneRules, error) { + r := &ZoneRules{} + + for _, s := range zones { + s = strings.TrimSpace(s) + if s == "*" || s == "*/*" { + r.Wildcard = true + continue + } + + zoneSpec, err := ParseZoneSpec(s) + if err != nil { + return nil, fmt.Errorf("error parsing %q: %v", s, err) + } + + r.Zones = append(r.Zones, zoneSpec) + } + + if len(zones) == 0 { + glog.Infof("No rules specified, will permit management of all zones") + r.Wildcard = true + } + + return r, nil +} + +// MatchesExplicitly returns true if this matches an explicit rule (not a wildcard) +func (r *ZoneRules) MatchesExplicitly(zone dnsprovider.Zone) bool { + name := EnsureDotSuffix(zone.Name()) + id := zone.ID() + + for _, zoneSpec := range r.Zones { + if zoneSpec.Name != "" && zoneSpec.Name != name { + continue + } + + if zoneSpec.ID != "" && zoneSpec.ID != id { + return false + } + + return true + } + + return false +} diff --git a/upup/models/cloudup/manifests/addons.yaml b/upup/models/cloudup/manifests/addons.yaml index 4ebb924b15..aa81ba44a9 100644 --- a/upup/models/cloudup/manifests/addons.yaml +++ b/upup/models/cloudup/manifests/addons.yaml @@ -11,5 +11,5 @@ managedFile/{{ ClusterName }}-addons-bootstrap-core: contents: resources/addons/core/v1.4.0.yaml managedFile/{{ ClusterName }}-addons-bootstrap-dns-controller: - location: addons/dns-controller/v1.4.0.yaml - contents: resources/addons/dns-controller/v1.4.0.yaml + location: addons/dns-controller/v1.4.1.yaml + contents: resources/addons/dns-controller/v1.4.1.yaml diff --git a/upup/models/cloudup/resources/addons/bootstrap-channel.yaml b/upup/models/cloudup/resources/addons/bootstrap-channel.yaml index 78ce24fd8a..f7ad8d35b9 100644 --- a/upup/models/cloudup/resources/addons/bootstrap-channel.yaml +++ b/upup/models/cloudup/resources/addons/bootstrap-channel.yaml @@ -14,7 +14,7 @@ spec: k8s-addon: core.addons.k8s.io manifest: core/v1.4.0.yaml - name: dns-controller - version: 1.4.0 + version: 1.4.1 selector: k8s-addon: dns-controller.addons.k8s.io - manifest: dns-controller/v1.4.0.yaml + manifest: dns-controller/v1.4.1.yaml diff --git a/upup/models/cloudup/resources/addons/dns-controller/addon.yaml b/upup/models/cloudup/resources/addons/dns-controller/addon.yaml index a3e9ad4080..e4efee95a9 100644 --- a/upup/models/cloudup/resources/addons/dns-controller/addon.yaml +++ b/upup/models/cloudup/resources/addons/dns-controller/addon.yaml @@ -3,8 +3,8 @@ metadata: name: dns-controller spec: addons: - - version: 1.4.0 + - version: 1.4.1 selector: k8s-addon: dns-controller.addons.k8s.io - manifest: v1.4.0.yaml + manifest: v1.4.1.yaml diff --git a/upup/models/cloudup/resources/addons/dns-controller/v1.4.1.yaml.template b/upup/models/cloudup/resources/addons/dns-controller/v1.4.1.yaml.template new file mode 100644 index 0000000000..5a32dde067 --- /dev/null +++ b/upup/models/cloudup/resources/addons/dns-controller/v1.4.1.yaml.template @@ -0,0 +1,38 @@ +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + name: dns-controller + namespace: kube-system + labels: + k8s-addon: dns-controller.addons.k8s.io + k8s-app: dns-controller + version: v1.4.1 +spec: + replicas: 1 + selector: + matchLabels: + k8s-app: dns-controller + template: + metadata: + labels: + k8s-addon: dns-controller.addons.k8s.io + k8s-app: dns-controller + version: v1.4.1 + annotations: + scheduler.alpha.kubernetes.io/critical-pod: '' + scheduler.alpha.kubernetes.io/tolerations: '[{"key": "dedicated", "value": "master"}]' + spec: + nodeSelector: + kubernetes.io/role: master + dnsPolicy: Default # Don't use cluster DNS (we are likely running before kube-dns) + containers: + - name: dns-controller + image: kope/dns-controller:1.4.1 + command: +{{ range $arg := DnsControllerArgv }} + - "{{ $arg }}" +{{ end }} + resources: + requests: + cpu: 50m + memory: 50Mi diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index efa8d063cb..9e87bde25d 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -105,6 +105,8 @@ func (tf *TemplateFunctions) AddTo(dest template.FuncMap) { dest["KubeDNS"] = func() *api.KubeDNSConfig { return tf.cluster.Spec.KubeDNS } + + dest["DnsControllerArgv"] = tf.DnsControllerArgv } func (tf *TemplateFunctions) EtcdClusterMemberTags(etcd *api.EtcdClusterSpec, m *api.EtcdMemberSpec) map[string]string { @@ -258,3 +260,28 @@ func (tf *TemplateFunctions) APIServerCount() int { } return count } + +func (tf *TemplateFunctions) DnsControllerArgv() ([]string, error) { + var argv []string + + argv = append(argv, "/usr/bin/dns-controller") + + argv = append(argv, "--watch-ingress=false") + argv = append(argv, "--dns=aws-route53") + + zone := tf.cluster.Spec.DNSZone + if zone != "" { + if strings.Contains(zone, ".") { + // match by name + argv = append(argv, "--zone="+zone) + } else { + // match by id + argv = append(argv, "--zone=*/"+zone) + } + } + // permit wildcard updates + argv = append(argv, "--zone=*/*") + argv = append(argv, "-v=8") + + return argv, nil +}