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  }