Configure dns-controller with ID of hosted zones

Fix #584
This commit is contained in:
Justin Santa Barbara 2016-10-04 01:57:39 -04:00
parent 204d1364ac
commit a529ffbb65
10 changed files with 220 additions and 18 deletions

View File

@ -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

View File

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

View File

@ -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.

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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