| /* |
| Copyright 2015 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package node |
| |
| import ( |
| "context" |
| "fmt" |
| "net" |
| "time" |
| |
| "k8s.io/klog/v2" |
| |
| v1 "k8s.io/api/core/v1" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/util/wait" |
| clientset "k8s.io/client-go/kubernetes" |
| netutils "k8s.io/utils/net" |
| ) |
| |
| const ( |
| // NodeUnreachablePodReason is the reason on a pod when its state cannot be confirmed as kubelet is unresponsive |
| // on the node it is (was) running. |
| NodeUnreachablePodReason = "NodeLost" |
| // NodeUnreachablePodMessage is the message on a pod when its state cannot be confirmed as kubelet is unresponsive |
| // on the node it is (was) running. |
| NodeUnreachablePodMessage = "Node %v which was running pod %v is unresponsive" |
| ) |
| |
| // NoMatchError is a typed implementation of the error interface. It indicates a failure to get a matching Node. |
| type NoMatchError struct { |
| addresses []v1.NodeAddress |
| } |
| |
| // Error is the implementation of the conventional interface for |
| // representing an error condition, with the nil value representing no error. |
| func (e *NoMatchError) Error() string { |
| return fmt.Sprintf("no preferred addresses found; known addresses: %v", e.addresses) |
| } |
| |
| // GetPreferredNodeAddress returns the address of the provided node, using the provided preference order. |
| // If none of the preferred address types are found, an error is returned. |
| func GetPreferredNodeAddress(node *v1.Node, preferredAddressTypes []v1.NodeAddressType) (string, error) { |
| for _, addressType := range preferredAddressTypes { |
| for _, address := range node.Status.Addresses { |
| if address.Type == addressType { |
| return address.Address, nil |
| } |
| } |
| } |
| return "", &NoMatchError{addresses: node.Status.Addresses} |
| } |
| |
| // GetNodeHostIPs returns the provided node's IP(s); either a single "primary IP" for the |
| // node in a single-stack cluster, or a dual-stack pair of IPs in a dual-stack cluster |
| // (for nodes that actually have dual-stack IPs). Among other things, the IPs returned |
| // from this function are used as the `.status.PodIPs` values for host-network pods on the |
| // node, and the first IP is used as the `.status.HostIP` for all pods on the node. |
| func GetNodeHostIPs(node *v1.Node) ([]net.IP, error) { |
| // Re-sort the addresses with InternalIPs first and then ExternalIPs |
| allIPs := make([]net.IP, 0, len(node.Status.Addresses)) |
| for _, addr := range node.Status.Addresses { |
| if addr.Type == v1.NodeInternalIP { |
| ip := netutils.ParseIPSloppy(addr.Address) |
| if ip != nil { |
| allIPs = append(allIPs, ip) |
| } |
| } |
| } |
| for _, addr := range node.Status.Addresses { |
| if addr.Type == v1.NodeExternalIP { |
| ip := netutils.ParseIPSloppy(addr.Address) |
| if ip != nil { |
| allIPs = append(allIPs, ip) |
| } |
| } |
| } |
| if len(allIPs) == 0 { |
| return nil, fmt.Errorf("host IP unknown; known addresses: %v", node.Status.Addresses) |
| } |
| |
| nodeIPs := []net.IP{allIPs[0]} |
| for _, ip := range allIPs { |
| if netutils.IsIPv6(ip) != netutils.IsIPv6(nodeIPs[0]) { |
| nodeIPs = append(nodeIPs, ip) |
| break |
| } |
| } |
| |
| return nodeIPs, nil |
| } |
| |
| // GetNodeHostIP returns the provided node's "primary" IP; see GetNodeHostIPs for more details |
| func GetNodeHostIP(node *v1.Node) (net.IP, error) { |
| ips, err := GetNodeHostIPs(node) |
| if err != nil { |
| return nil, err |
| } |
| // GetNodeHostIPs always returns at least one IP if it didn't return an error |
| return ips[0], nil |
| } |
| |
| // GetNodeIP returns an IP (as with GetNodeHostIP) for the node with the provided name. |
| // If required, it will wait for the node to be created. |
| func GetNodeIP(client clientset.Interface, name string) net.IP { |
| var nodeIP net.IP |
| backoff := wait.Backoff{ |
| Steps: 6, |
| Duration: 1 * time.Second, |
| Factor: 2.0, |
| Jitter: 0.2, |
| } |
| |
| err := wait.ExponentialBackoff(backoff, func() (bool, error) { |
| node, err := client.CoreV1().Nodes().Get(context.TODO(), name, metav1.GetOptions{}) |
| if err != nil { |
| klog.Errorf("Failed to retrieve node info: %v", err) |
| return false, nil |
| } |
| nodeIP, err = GetNodeHostIP(node) |
| if err != nil { |
| klog.Errorf("Failed to retrieve node IP: %v", err) |
| return false, err |
| } |
| return true, nil |
| }) |
| if err == nil { |
| klog.Infof("Successfully retrieved node IP: %v", nodeIP) |
| } |
| return nodeIP |
| } |
| |
| // IsNodeReady returns true if a node is ready; false otherwise. |
| func IsNodeReady(node *v1.Node) bool { |
| for _, c := range node.Status.Conditions { |
| if c.Type == v1.NodeReady { |
| return c.Status == v1.ConditionTrue |
| } |
| } |
| return false |
| } |