github.com/nmstate/kubernetes-nmstate@v0.82.0/pkg/node/nodes.go (about) 1 /* 2 Copyright The Kubernetes NMState Authors. 3 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package node 19 20 import ( 21 "context" 22 23 nmstatev1 "github.com/nmstate/kubernetes-nmstate/api/v1" 24 "github.com/nmstate/kubernetes-nmstate/pkg/enactment" 25 "github.com/nmstate/kubernetes-nmstate/pkg/environment" 26 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/util/intstr" 28 29 corev1 "k8s.io/api/core/v1" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 32 "github.com/pkg/errors" 33 ) 34 35 const ( 36 DefaultMaxunavailable = "50%" 37 MinMaxunavailable = 1 38 ) 39 40 type MaxUnavailableLimitReachedError struct{} 41 42 func (f MaxUnavailableLimitReachedError) Error() string { 43 return "maximal number of nodes are already processing policy configuration" 44 } 45 46 func NodesRunningNmstate(cli client.Reader, nodeSelector map[string]string) ([]corev1.Node, error) { 47 nodes := corev1.NodeList{} 48 err := cli.List(context.TODO(), &nodes, client.MatchingLabels(nodeSelector)) 49 if err != nil { 50 return []corev1.Node{}, errors.Wrap(err, "getting nodes failed") 51 } 52 53 pods := corev1.PodList{} 54 byComponent := client.MatchingLabels{"component": "kubernetes-nmstate-handler"} 55 err = cli.List(context.TODO(), &pods, byComponent) 56 if err != nil { 57 return []corev1.Node{}, errors.Wrap(err, "getting pods failed") 58 } 59 60 filteredNodes := []corev1.Node{} 61 for nodeIndex := range nodes.Items { 62 for podIndex := range pods.Items { 63 if nodes.Items[nodeIndex].Name == pods.Items[podIndex].Spec.NodeName { 64 filteredNodes = append(filteredNodes, nodes.Items[nodeIndex]) 65 break 66 } 67 } 68 } 69 return filteredNodes, nil 70 } 71 72 func FilterReady(nodes []corev1.Node) []corev1.Node { 73 filteredNodes := []corev1.Node{} 74 for i := range nodes { 75 if isReady(&nodes[i]) { 76 filteredNodes = append(filteredNodes, nodes[i]) 77 } 78 } 79 return filteredNodes 80 } 81 82 func isReady(node *corev1.Node) bool { 83 for _, condition := range node.Status.Conditions { 84 if condition.Type == corev1.NodeReady { 85 return condition.Status == corev1.ConditionTrue 86 } 87 } 88 return false 89 } 90 91 func MaxUnavailableNodeCount(cli client.Reader, policy *nmstatev1.NodeNetworkConfigurationPolicy) (int, error) { 92 enactmentsTotal, _, err := enactment.CountByPolicy(cli, policy) 93 if err != nil { 94 return MinMaxunavailable, err 95 } 96 intOrPercent := intstr.FromString(DefaultMaxunavailable) 97 if policy.Spec.MaxUnavailable != nil { 98 intOrPercent = *policy.Spec.MaxUnavailable 99 } 100 return ScaledMaxUnavailableNodeCount(enactmentsTotal, intOrPercent) 101 } 102 103 func ScaledMaxUnavailableNodeCount(matchingNodes int, intOrPercent intstr.IntOrString) (int, error) { 104 correctMaxUnavailable := func(maxUnavailable int) int { 105 if maxUnavailable < 1 { 106 return MinMaxunavailable 107 } 108 return maxUnavailable 109 } 110 maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(&intOrPercent, matchingNodes, true) 111 if err != nil { 112 defaultMaxUnavailable := intstr.FromString(DefaultMaxunavailable) 113 maxUnavailable, _ = intstr.GetScaledValueFromIntOrPercent( 114 &defaultMaxUnavailable, 115 matchingNodes, 116 true, 117 ) 118 return correctMaxUnavailable(maxUnavailable), err 119 } 120 return correctMaxUnavailable(maxUnavailable), nil 121 } 122 123 // Return true if the event name is the name of 124 // the pods's node (reading the env var NODE_NAME) 125 func EventIsForThisNode(meta v1.Object) bool { 126 createdNodeName := meta.GetName() 127 podNodeName := environment.NodeName() 128 // Only reconcile is it's for this pod 129 return createdNodeName == podNodeName 130 }