github.com/imran-kn/cilium-fork@v1.6.9/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 }