mirror of
https://github.com/traefik/mesh.git
synced 2026-06-01 18:37:57 +00:00
74 lines
2.4 KiB
Go
74 lines
2.4 KiB
Go
package dns
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/traefik/mesh/v2/pkg/controller"
|
|
"github.com/traefik/mesh/v2/pkg/k8s"
|
|
listers "k8s.io/client-go/listers/core/v1"
|
|
)
|
|
|
|
// ShadowServiceResolver is a DNS resolver implementation which resolves the shadow service ClusterIP corresponding to a subdomain.
|
|
// The subdomain must be of form: name.namespace.domain where name and namespace match the shadowed service metadata and domain the configured domain.
|
|
type ShadowServiceResolver struct {
|
|
domain string
|
|
namespace string
|
|
serviceLister listers.ServiceLister
|
|
}
|
|
|
|
// NewShadowServiceResolver creates and returns a new resolver.
|
|
func NewShadowServiceResolver(domain, namespace string, serviceLister listers.ServiceLister) *ShadowServiceResolver {
|
|
return &ShadowServiceResolver{
|
|
domain: domain,
|
|
namespace: namespace,
|
|
serviceLister: serviceLister,
|
|
}
|
|
}
|
|
|
|
// Domain returns the configured domain.
|
|
func (r *ShadowServiceResolver) Domain() string {
|
|
return r.domain
|
|
}
|
|
|
|
// LookupFQDN returns the ClusterIP of the Shadow Service corresponding to the given FQDN.
|
|
func (r *ShadowServiceResolver) LookupFQDN(fqdn string) (net.IP, error) {
|
|
namespace, name, err := r.parseNamespaceAndName(fqdn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
shadowServiceName, err := controller.GetShadowServiceName(namespace, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
shadowService, err := r.serviceLister.Services(r.namespace).Get(shadowServiceName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get shadow service %q: %w", shadowServiceName, err)
|
|
}
|
|
|
|
if shadowService.Labels[k8s.LabelServiceNamespace] != namespace || shadowService.Labels[k8s.LabelServiceName] != name {
|
|
return nil, fmt.Errorf("service labels in %q does not match service name %q and namespace %q", shadowServiceName, name, namespace)
|
|
}
|
|
|
|
return net.ParseIP(shadowService.Spec.ClusterIP), nil
|
|
}
|
|
|
|
// parseNamespaceAndName returns the namespace and the name corresponding to the given FQDN.
|
|
func (r *ShadowServiceResolver) parseNamespaceAndName(fqdn string) (string, string, error) {
|
|
domain := dns.CanonicalName(r.domain)
|
|
|
|
if !dns.IsSubDomain(domain, fqdn) {
|
|
return "", "", fmt.Errorf("name %q is not a subdomain of %q", fqdn, domain)
|
|
}
|
|
|
|
labels := dns.SplitDomainName(fqdn)
|
|
if len(labels)-dns.CountLabel(domain) < 2 {
|
|
return "", "", fmt.Errorf("malformed name %q", fqdn)
|
|
}
|
|
|
|
return labels[1], labels[0], nil
|
|
}
|