Ejemplo n.º 1
0
func (kd *KubeDNS) recordsForFederation(records []skymsg.Service, path []string, exact bool, federationSegments []string) (retval []skymsg.Service, err error) {
	// For federation query, verify that the local service has endpoints.
	validRecord := false
	for _, val := range records {
		// We know that a headless service has endpoints for sure if a
		// record was returned for it. The record contains endpoint
		// IPs. So nothing to check for headless services.
		//
		// TODO: this access to the cluster IP map does not seem to be
		// threadsafe.
		if !kd.isHeadlessServiceRecord(&val) {
			ok, err := kd.serviceWithClusterIPHasEndpoints(&val)
			if err != nil {
				glog.V(2).Infof(
					"Federation: error finding if service has endpoint: %v", err)
				continue
			}
			if !ok {
				glog.V(2).Infof("Federation: skipping record since service has no endpoint: %v", val)
				continue
			}
		}
		validRecord = true
		break
	}

	if validRecord {
		// There is a local service with valid endpoints, return its CNAME.
		name := strings.Join(util.ReverseArray(path), ".")
		// Ensure that this name that we are returning as a CNAME response
		// is a fully qualified domain name so that the client's resolver
		// library doesn't have to go through its search list all over
		// again.
		if !strings.HasSuffix(name, ".") {
			name = name + "."
		}
		glog.V(3).Infof(
			"Federation: Returning CNAME for local service: %v", name)
		return []skymsg.Service{{Host: name}}, nil
	}

	// If the name query is not an exact query and does not match any
	// records in the local store, attempt to send a federation redirect
	// (CNAME) response.
	if !exact {
		glog.V(3).Infof(
			"Federation: Did not find a local service. Trying federation redirect (CNAME)")
		return kd.federationRecords(util.ReverseArray(federationSegments))
	}

	return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
}
Ejemplo n.º 2
0
func assertReverseRecord(t *testing.T, kd *KubeDNS, s *v1.Service) {
	segments := util.ReverseArray(strings.Split(s.Spec.ClusterIP, "."))
	reverseLookup := fmt.Sprintf("%s%s", strings.Join(segments, "."), util.ArpaSuffix)
	reverseRecord, err := kd.ReverseRecord(reverseLookup)
	require.NoError(t, err)
	assert.Equal(t, getServiceFQDN(kd.domain, s), reverseRecord.Host)
}
Ejemplo n.º 3
0
func assertNoReverseRecord(t *testing.T, kd *KubeDNS, s *v1.Service) {
	segments := util.ReverseArray(strings.Split(s.Spec.ClusterIP, "."))
	reverseLookup := fmt.Sprintf("%s%s", strings.Join(segments, "."), util.ArpaSuffix)
	reverseRecord, err := kd.ReverseRecord(reverseLookup)
	require.Error(t, err)
	require.Nil(t, reverseRecord)
}
Ejemplo n.º 4
0
// Records responds with DNS records that match the given name, in a format
// understood by the skydns server. If "exact" is true, a single record
// matching the given name is returned, otherwise all records stored under
// the subtree matching the name are returned.
func (kd *KubeDNS) Records(name string, exact bool) (retval []skymsg.Service, err error) {
	glog.V(2).Infof("Received DNS Request:%s, exact:%v", name, exact)

	trimmed := strings.TrimRight(name, ".")
	segments := strings.Split(trimmed, ".")
	isFederationQuery := false
	federationSegments := []string{}

	if !exact && kd.isFederationQuery(segments) {
		glog.V(2).Infof(
			"federation service query: Received federation query. Going to try to find local service first")
		// Try quering the non-federation (local) service first.
		// Will try the federation one later, if this fails.
		isFederationQuery = true
		federationSegments = append(federationSegments, segments...)
		// To try local service, remove federation name from segments.
		// Federation name is 3rd in the segment (after service name and namespace).
		segments = append(segments[:2], segments[3:]...)
	}

	path := util.ReverseArray(segments)
	records, err := kd.getRecordsForPath(path, exact)

	if err != nil {
		return nil, err
	}

	if isFederationQuery {
		return kd.recordsForFederation(records, path, exact, federationSegments)
	} else if len(records) > 0 {
		return records, nil
	}

	return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
}
Ejemplo n.º 5
0
func newKubeDNS() *KubeDNS {
	kd := &KubeDNS{
		domain:              testDomain,
		endpointsStore:      cache.NewStore(cache.MetaNamespaceKeyFunc),
		servicesStore:       cache.NewStore(cache.MetaNamespaceKeyFunc),
		cache:               treecache.NewTreeCache(),
		reverseRecordMap:    make(map[string]*skymsg.Service),
		clusterIPServiceMap: make(map[string]*kapi.Service),
		cacheLock:           sync.RWMutex{},
		domainPath:          util.ReverseArray(strings.Split(strings.TrimRight(testDomain, "."), ".")),
		nodesStore:          cache.NewStore(cache.MetaNamespaceKeyFunc),
	}
	return kd
}
Ejemplo n.º 6
0
// federationRecords checks if the given `queryPath` is for a federated service and if it is,
// it returns a CNAME response containing the cluster zone name and federation domain name
// suffix.
func (kd *KubeDNS) federationRecords(queryPath []string) ([]skymsg.Service, error) {
	// `queryPath` is a reversed-array of the queried name, reverse it back to make it easy
	// to follow through this code and reduce confusion. There is no reason for it to be
	// reversed here.
	path := util.ReverseArray(queryPath)

	// Check if the name query matches the federation query pattern.
	if !kd.isFederationQuery(path) {
		return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
	}

	// Now that we have already established that the query is a federation query, remove the local
	// domain path components, i.e. kd.domainPath, from the query.
	path = path[:len(path)-len(kd.domainPath)]

	// Append the zone name (zone in the cloud provider terminology, not a DNS
	// zone) and the region name.
	zone, region, err := kd.getClusterZoneAndRegion()
	if err != nil {
		return nil, fmt.Errorf("failed to obtain the cluster zone and region: %v", err)
	}
	path = append(path, zone, region)

	// We have already established that the map entry exists for the given federation,
	// we just need to retrieve the domain name, validate it and append it to the path.
	kd.configLock.RLock()
	domain := kd.config.Federations[path[2]]
	kd.configLock.RUnlock()

	// We accept valid subdomains as well, so just let all the valid subdomains.
	if len(validation.IsDNS1123Subdomain(domain)) != 0 {
		return nil, fmt.Errorf("%s is not a valid domain name for federation %s", domain, path[2])
	}
	name := strings.Join(append(path, domain), ".")

	// Ensure that this name that we are returning as a CNAME response is a fully qualified
	// domain name so that the client's resolver library doesn't have to go through its
	// search list all over again.
	if !strings.HasSuffix(name, ".") {
		name = name + "."
	}
	return []skymsg.Service{{Host: name}}, nil
}
Ejemplo n.º 7
0
func newKubeDNS() *KubeDNS {
	return &KubeDNS{
		domain:     testDomain,
		domainPath: util.ReverseArray(strings.Split(strings.TrimRight(testDomain, "."), ".")),

		endpointsStore: cache.NewStore(cache.MetaNamespaceKeyFunc),
		servicesStore:  cache.NewStore(cache.MetaNamespaceKeyFunc),
		nodesStore:     cache.NewStore(cache.MetaNamespaceKeyFunc),

		cache:               treecache.NewTreeCache(),
		reverseRecordMap:    make(map[string]*skymsg.Service),
		clusterIPServiceMap: make(map[string]*v1.Service),
		cacheLock:           sync.RWMutex{},

		config:     config.NewDefaultConfig(),
		configLock: sync.RWMutex{},
		configSync: config.NewNopSync(config.NewDefaultConfig()),
	}
}
Ejemplo n.º 8
0
func NewKubeDNS(client clientset.Interface, clusterDomain string, configSync config.Sync) *KubeDNS {
	kd := &KubeDNS{
		kubeClient:          client,
		domain:              clusterDomain,
		cache:               treecache.NewTreeCache(),
		cacheLock:           sync.RWMutex{},
		nodesStore:          kcache.NewStore(kcache.MetaNamespaceKeyFunc),
		reverseRecordMap:    make(map[string]*skymsg.Service),
		clusterIPServiceMap: make(map[string]*v1.Service),
		domainPath:          util.ReverseArray(strings.Split(strings.TrimRight(clusterDomain, "."), ".")),

		configLock: sync.RWMutex{},
		configSync: configSync,
	}

	kd.setEndpointsStore()
	kd.setServicesStore()

	return kd
}
Ejemplo n.º 9
0
func NewKubeDNS(client clientset.Interface, domain string, federations map[string]string) (*KubeDNS, error) {
	// Verify that federation names should not contain dots ('.')
	// We can not allow dots since we use that as separator for path segments (svcname.nsname.fedname.svc.domain)
	for key := range federations {
		if strings.ContainsAny(key, ".") {
			return nil, fmt.Errorf("invalid federation name: %s, cannot have '.'", key)
		}
	}
	kd := &KubeDNS{
		kubeClient:          client,
		domain:              domain,
		cache:               NewTreeCache(),
		cacheLock:           sync.RWMutex{},
		nodesStore:          kcache.NewStore(kcache.MetaNamespaceKeyFunc),
		reverseRecordMap:    make(map[string]*skymsg.Service),
		clusterIPServiceMap: make(map[string]*kapi.Service),
		domainPath:          util.ReverseArray(strings.Split(strings.TrimRight(domain, "."), ".")),
		federations:         federations,
	}
	kd.setEndpointsStore()
	kd.setServicesStore()
	return kd, nil
}
Ejemplo n.º 10
0
// fqdn constructs the fqdn for the given service. subpaths is a list of path
// elements rooted at the given service, ending at a service record.
func (kd *KubeDNS) fqdn(service *v1.Service, subpaths ...string) string {
	domainLabels := append(append(kd.domainPath, serviceSubdomain, service.Namespace, service.Name), subpaths...)
	return dns.Fqdn(strings.Join(util.ReverseArray(domainLabels), "."))
}