github.com/noironetworks/cilium-net@v1.6.12/pkg/k8s/node.go (about)

     1  // Copyright 2016-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package k8s
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"net"
    22  
    23  	"github.com/cilium/cilium/pkg/annotation"
    24  	"github.com/cilium/cilium/pkg/cidr"
    25  	"github.com/cilium/cilium/pkg/controller"
    26  	"github.com/cilium/cilium/pkg/k8s/types"
    27  	"github.com/cilium/cilium/pkg/logging/logfields"
    28  	"github.com/cilium/cilium/pkg/node"
    29  	"github.com/cilium/cilium/pkg/node/addressing"
    30  	"github.com/cilium/cilium/pkg/option"
    31  	"github.com/cilium/cilium/pkg/source"
    32  
    33  	"github.com/sirupsen/logrus"
    34  	"k8s.io/api/core/v1"
    35  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    36  	"k8s.io/client-go/kubernetes"
    37  )
    38  
    39  // ParseNodeAddressType converts a Kubernetes NodeAddressType to a Cilium
    40  // NodeAddressType. If the Kubernetes NodeAddressType does not have a
    41  // corresponding Cilium AddressType, returns an error.
    42  func ParseNodeAddressType(k8sAddress v1.NodeAddressType) (addressing.AddressType, error) {
    43  
    44  	var err error
    45  	convertedAddr := addressing.AddressType(k8sAddress)
    46  
    47  	switch convertedAddr {
    48  	case addressing.NodeExternalDNS, addressing.NodeExternalIP, addressing.NodeHostName, addressing.NodeInternalIP, addressing.NodeInternalDNS:
    49  	default:
    50  		err = fmt.Errorf("invalid Kubernetes NodeAddressType %s", convertedAddr)
    51  	}
    52  	return convertedAddr, err
    53  }
    54  
    55  // ParseNode parses a kubernetes node to a cilium node
    56  func ParseNode(k8sNode *types.Node, source source.Source) *node.Node {
    57  	scopedLog := log.WithFields(logrus.Fields{
    58  		logfields.NodeName:  k8sNode.Name,
    59  		logfields.K8sNodeID: k8sNode.UID,
    60  	})
    61  	addrs := []node.Address{}
    62  	for _, addr := range k8sNode.StatusAddresses {
    63  		// We only care about this address types,
    64  		// we ignore all other types.
    65  		switch addr.Type {
    66  		case v1.NodeInternalIP, v1.NodeExternalIP:
    67  		default:
    68  			continue
    69  		}
    70  		// If the address is not set let's not parse it at all.
    71  		// This can be the case for v1.NodeExternalIPs
    72  		if addr.Address == "" {
    73  			continue
    74  		}
    75  		ip := net.ParseIP(addr.Address)
    76  		if ip == nil {
    77  			scopedLog.WithFields(logrus.Fields{
    78  				logfields.IPAddr: addr.Address,
    79  				"type":           addr.Type,
    80  			}).Warn("Ignoring invalid node IP")
    81  			continue
    82  		}
    83  
    84  		addressType, err := ParseNodeAddressType(addr.Type)
    85  
    86  		if err != nil {
    87  			scopedLog.WithError(err).Warn("invalid address type for node")
    88  		}
    89  
    90  		na := node.Address{
    91  			Type: addressType,
    92  			IP:   ip,
    93  		}
    94  		addrs = append(addrs, na)
    95  	}
    96  
    97  	k8sNodeAddHostIP := func(annotation string) {
    98  		if ciliumInternalIP, ok := k8sNode.Annotations[annotation]; !ok || ciliumInternalIP == "" {
    99  			scopedLog.Debugf("Missing %s. Annotation required when IPSec Enabled", annotation)
   100  		} else if ip := net.ParseIP(ciliumInternalIP); ip == nil {
   101  			scopedLog.Debugf("ParseIP %s error", ciliumInternalIP)
   102  		} else {
   103  			na := node.Address{
   104  				Type: addressing.NodeCiliumInternalIP,
   105  				IP:   ip,
   106  			}
   107  			addrs = append(addrs, na)
   108  			scopedLog.Debugf("Add NodeCiliumInternalIP: %s", ip)
   109  		}
   110  	}
   111  
   112  	k8sNodeAddHostIP(annotation.CiliumHostIP)
   113  	k8sNodeAddHostIP(annotation.CiliumHostIPv6)
   114  
   115  	newNode := &node.Node{
   116  		Name:        k8sNode.Name,
   117  		Cluster:     option.Config.ClusterName,
   118  		IPAddresses: addrs,
   119  		Source:      source,
   120  	}
   121  
   122  	if len(k8sNode.SpecPodCIDR) != 0 {
   123  		if allocCIDR, err := cidr.ParseCIDR(k8sNode.SpecPodCIDR); err != nil {
   124  			scopedLog.WithError(err).WithField(logfields.V4Prefix, k8sNode.SpecPodCIDR).Warn("Invalid PodCIDR value for node")
   125  		} else {
   126  			if allocCIDR.IP.To4() != nil {
   127  				newNode.IPv4AllocCIDR = allocCIDR
   128  			} else {
   129  				newNode.IPv6AllocCIDR = allocCIDR
   130  			}
   131  		}
   132  	}
   133  	// Spec.PodCIDR takes precedence since it's
   134  	// the CIDR assigned by k8s controller manager
   135  	// In case it's invalid or empty then we fall back to our annotations.
   136  	if newNode.IPv4AllocCIDR == nil {
   137  		if ipv4CIDR, ok := k8sNode.Annotations[annotation.V4CIDRName]; !ok || ipv4CIDR == "" {
   138  			scopedLog.Debug("Empty IPv4 CIDR annotation in node")
   139  		} else {
   140  			allocCIDR, err := cidr.ParseCIDR(ipv4CIDR)
   141  			if err != nil {
   142  				scopedLog.WithError(err).WithField(logfields.V4Prefix, ipv4CIDR).Error("BUG, invalid IPv4 annotation CIDR in node")
   143  			} else {
   144  				newNode.IPv4AllocCIDR = allocCIDR
   145  			}
   146  		}
   147  	}
   148  
   149  	if newNode.IPv6AllocCIDR == nil {
   150  		if ipv6CIDR, ok := k8sNode.Annotations[annotation.V6CIDRName]; !ok || ipv6CIDR == "" {
   151  			scopedLog.Debug("Empty IPv6 CIDR annotation in node")
   152  		} else {
   153  			allocCIDR, err := cidr.ParseCIDR(ipv6CIDR)
   154  			if err != nil {
   155  				scopedLog.WithError(err).WithField(logfields.V6Prefix, ipv6CIDR).Error("BUG, invalid IPv6 annotation CIDR in node")
   156  			} else {
   157  				newNode.IPv6AllocCIDR = allocCIDR
   158  			}
   159  		}
   160  	}
   161  
   162  	if newNode.IPv4HealthIP == nil {
   163  		if healthIP, ok := k8sNode.Annotations[annotation.V4HealthName]; !ok || healthIP == "" {
   164  			scopedLog.Debug("Empty IPv4 health endpoint annotation in node")
   165  		} else if ip := net.ParseIP(healthIP); ip == nil {
   166  			scopedLog.WithField(logfields.V4HealthIP, healthIP).Error("BUG, invalid IPv4 health endpoint annotation in node")
   167  		} else {
   168  			newNode.IPv4HealthIP = ip
   169  		}
   170  	}
   171  
   172  	if newNode.IPv6HealthIP == nil {
   173  		if healthIP, ok := k8sNode.Annotations[annotation.V6HealthName]; !ok || healthIP == "" {
   174  			scopedLog.Debug("Empty IPv6 health endpoint annotation in node")
   175  		} else if ip := net.ParseIP(healthIP); ip == nil {
   176  			scopedLog.WithField(logfields.V6HealthIP, healthIP).Error("BUG, invalid IPv6 health endpoint annotation in node")
   177  		} else {
   178  			newNode.IPv6HealthIP = ip
   179  		}
   180  	}
   181  
   182  	return newNode
   183  }
   184  
   185  // GetNode returns the kubernetes nodeName's node information from the
   186  // kubernetes api server
   187  func GetNode(c kubernetes.Interface, nodeName string) (*v1.Node, error) {
   188  	// Try to retrieve node's cidr and addresses from k8s's configuration
   189  	return c.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
   190  }
   191  
   192  // setNodeNetworkUnavailableFalse sets Kubernetes NodeNetworkUnavailable to
   193  // false as Cilium is managing the network connectivity.
   194  // https://kubernetes.io/docs/concepts/architecture/nodes/#condition
   195  func setNodeNetworkUnavailableFalse(c kubernetes.Interface, nodeName string) error {
   196  	condition := v1.NodeCondition{
   197  		Type:               v1.NodeNetworkUnavailable,
   198  		Status:             v1.ConditionFalse,
   199  		Reason:             "CiliumIsUp",
   200  		Message:            "Cilium is running on this node",
   201  		LastTransitionTime: metav1.Now(),
   202  		LastHeartbeatTime:  metav1.Now(),
   203  	}
   204  	raw, err := json.Marshal(&[]v1.NodeCondition{condition})
   205  	if err != nil {
   206  		return err
   207  	}
   208  	patch := []byte(fmt.Sprintf(`{"status":{"conditions":%s}}`, raw))
   209  	_, err = c.CoreV1().Nodes().PatchStatus(nodeName, patch)
   210  	return err
   211  }
   212  
   213  // MarkNodeReady marks the Kubernetes node resource as ready from a networking
   214  // perspective
   215  func (k8sCli K8sClient) MarkNodeReady(nodeName string) {
   216  	log.WithField(logfields.NodeName, nodeName).Debug("Setting NetworkUnavailable=false")
   217  
   218  	controller.NewManager().UpdateController("mark-k8s-node-as-available",
   219  		controller.ControllerParams{
   220  			DoFunc: func(_ context.Context) error {
   221  				return setNodeNetworkUnavailableFalse(k8sCli, nodeName)
   222  			},
   223  		})
   224  }