mirror of https://github.com/linkerd/linkerd2.git
171 lines
4.5 KiB
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
|
|
}
|