linkerd2/controller/api/destination/endpoint_translator.go

171 lines
4.5 KiB
Go

package destination
import (
"fmt"
pb "github.com/linkerd/linkerd2-proxy-api/go/destination"
"github.com/linkerd/linkerd2-proxy-api/go/net"
"github.com/linkerd/linkerd2/controller/api/destination/watcher"
"github.com/linkerd/linkerd2/pkg/addr"
"github.com/linkerd/linkerd2/pkg/k8s"
logging "github.com/sirupsen/logrus"
)
const defaultWeight uint32 = 10000
// endpointTranslator satisfies EndpointUpdateListener and translates updates
// into Destination.Get messages.
type endpointTranslator struct {
controllerNS string
identityTrustDomain string
enableH2Upgrade bool
labels map[string]string
stream pb.Destination_GetServer
log *logging.Entry
}
func newEndpointTranslator(
controllerNS string,
identityTrustDomain string,
enableH2Upgrade bool,
service watcher.ServiceID,
stream pb.Destination_GetServer,
log *logging.Entry,
) *endpointTranslator {
log = log.WithFields(logging.Fields{
"component": "endpoint-translator",
"service": service,
})
labels := map[string]string{
"namespace": service.Namespace,
"service": service.Name,
}
return &endpointTranslator{controllerNS, identityTrustDomain, enableH2Upgrade, labels, stream, log}
}
func (et *endpointTranslator) Add(set watcher.PodSet) {
addrs := []*pb.WeightedAddr{}
for _, address := range set {
wa, err := et.toWeightedAddr(address)
if err != nil {
et.log.Errorf("Failed to translate endpoints to weighted addr: %s", err)
continue
}
addrs = append(addrs, wa)
}
add := &pb.Update{Update: &pb.Update_Add{
Add: &pb.WeightedAddrSet{
Addrs: addrs,
MetricLabels: et.labels,
},
}}
et.log.Debugf("Sending destination add: %+v", add)
if err := et.stream.Send(add); err != nil {
et.log.Errorf("Failed to send address update: %s", err)
}
}
func (et *endpointTranslator) Remove(set watcher.PodSet) {
addrs := []*net.TcpAddress{}
for _, address := range set {
tcpAddr, err := et.toAddr(address)
if err != nil {
et.log.Errorf("Failed to translate endpoints to addr: %s", err)
continue
}
addrs = append(addrs, tcpAddr)
}
remove := &pb.Update{Update: &pb.Update_Remove{
Remove: &pb.AddrSet{
Addrs: addrs,
},
}}
et.log.Debugf("Sending destination remove: %+v", remove)
if err := et.stream.Send(remove); err != nil {
et.log.Errorf("Failed to send address update: %s", err)
}
}
func (et *endpointTranslator) NoEndpoints(exists bool) {
et.log.Debugf("NoEndpoints(%+v)", exists)
u := &pb.Update{
Update: &pb.Update_NoEndpoints{
NoEndpoints: &pb.NoEndpoints{
Exists: exists,
},
},
}
et.log.Debugf("Sending destination no endpoints: %+v", u)
if err := et.stream.Send(u); err != nil {
et.log.Errorf("Failed to send address update: %s", err)
}
}
func (et *endpointTranslator) toAddr(address watcher.Address) (*net.TcpAddress, error) {
ip, err := addr.ParseProxyIPV4(address.IP)
if err != nil {
return nil, err
}
return &net.TcpAddress{
Ip: ip,
Port: address.Port,
}, nil
}
func (et *endpointTranslator) toWeightedAddr(address watcher.Address) (*pb.WeightedAddr, error) {
controllerNS := address.Pod.Labels[k8s.ControllerNSLabel]
sa, ns := k8s.GetServiceAccountAndNS(address.Pod)
labels := k8s.GetPodLabels(address.OwnerKind, address.OwnerName, address.Pod)
// If the pod is controlled by any Linkerd control plane, then it can be hinted
// that this destination knows H2 (and handles our orig-proto translation).
var hint *pb.ProtocolHint
if et.enableH2Upgrade && controllerNS != "" {
hint = &pb.ProtocolHint{
Protocol: &pb.ProtocolHint_H2_{
H2: &pb.ProtocolHint_H2{},
},
}
}
// If the pod is controlled by the same Linkerd control plane, then it can
// participate in identity with peers.
//
// TODO this should be relaxed to match a trust domain annotation so that
// multiple meshes can participate in identity if they share trust roots.
var identity *pb.TlsIdentity
if et.identityTrustDomain != "" &&
controllerNS == et.controllerNS &&
address.Pod.Annotations[k8s.IdentityModeAnnotation] == k8s.IdentityModeDefault {
id := fmt.Sprintf("%s.%s.serviceaccount.identity.%s.%s", sa, ns, controllerNS, et.identityTrustDomain)
identity = &pb.TlsIdentity{
Strategy: &pb.TlsIdentity_DnsLikeIdentity_{
DnsLikeIdentity: &pb.TlsIdentity_DnsLikeIdentity{
Name: id,
},
},
}
}
tcpAddr, err := et.toAddr(address)
if err != nil {
return nil, err
}
return &pb.WeightedAddr{
Addr: tcpAddr,
Weight: defaultWeight,
MetricLabels: labels,
TlsIdentity: identity,
ProtocolHint: hint,
}, nil
}