linkerd2/controller/api/destination/watcher/k8s.go

113 lines
3.1 KiB
Go

package watcher
import (
"fmt"
"github.com/linkerd/linkerd2/controller/k8s"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/cache"
)
const (
// PodIPIndex is the key for the index based on Pod IPs
PodIPIndex = "ip"
// HostIPIndex is the key for the index based on Host IP of pods with host network enabled
HostIPIndex = "hostIP"
)
type (
// ID is a namespace-qualified name.
ID struct {
Namespace string
Name string
}
// ServiceID is the namespace-qualified name of a service.
ServiceID = ID
// PodID is the namespace-qualified name of a pod.
PodID = ID
// ProfileID is the namespace-qualified name of a service profile.
ProfileID = ID
// Port is a numeric port.
Port = uint32
namedPort = intstr.IntOrString
// InvalidService is an error which indicates that the authority is not a
// valid service.
InvalidService struct {
authority string
}
)
func (is InvalidService) Error() string {
return fmt.Sprintf("Invalid k8s service %s", is.authority)
}
func invalidService(authority string) InvalidService {
return InvalidService{authority}
}
func (i ID) String() string {
return fmt.Sprintf("%s/%s", i.Namespace, i.Name)
}
// InitializeIndexers is used to initialize indexers on k8s informers, to be used across watchers
func InitializeIndexers(k8sAPI *k8s.API) error {
err := k8sAPI.Svc().Informer().AddIndexers(cache.Indexers{PodIPIndex: func(obj interface{}) ([]string, error) {
if svc, ok := obj.(*corev1.Service); ok {
return []string{svc.Spec.ClusterIP}, nil
}
return nil, fmt.Errorf("object is not a service")
}})
if err != nil {
return fmt.Errorf("could not create an indexer for services: %s", err)
}
err = k8sAPI.Pod().Informer().AddIndexers(cache.Indexers{PodIPIndex: func(obj interface{}) ([]string, error) {
if pod, ok := obj.(*corev1.Pod); ok {
// Pods that run in the host network are indexed by the host IP
// indexer in the IP watcher; they should be skipped by the pod
// IP indexer which is responsible only for indexing pod network
// pods.
if pod.Spec.HostNetwork {
return nil, nil
}
return []string{pod.Status.PodIP}, nil
}
return nil, fmt.Errorf("object is not a pod")
}})
if err != nil {
return fmt.Errorf("could not create an indexer for pods: %s", err)
}
err = k8sAPI.Pod().Informer().AddIndexers(cache.Indexers{HostIPIndex: func(obj interface{}) ([]string, error) {
if pod, ok := obj.(*corev1.Pod); ok {
var hostIPPods []string
if pod.Status.HostIP != "" {
// If the pod is reachable from the host network, then for
// each of its containers' ports that exposes a host port, add
// that hostIP:hostPort endpoint to the indexer.
for _, c := range pod.Spec.Containers {
for _, p := range c.Ports {
if p.HostPort != 0 {
addr := fmt.Sprintf("%s:%d", pod.Status.HostIP, p.HostPort)
hostIPPods = append(hostIPPods, addr)
}
}
}
}
return hostIPPods, nil
}
return nil, fmt.Errorf("object is not a pod")
}})
if err != nil {
return fmt.Errorf("could not create an indexer for pods: %s", err)
}
return nil
}