sigs.k8s.io/cluster-api@v1.7.1/util/conditions/getter.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package conditions
    18  
    19  import (
    20  	corev1 "k8s.io/api/core/v1"
    21  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    22  	"sigs.k8s.io/controller-runtime/pkg/client"
    23  
    24  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    25  )
    26  
    27  // Getter interface defines methods that a Cluster API object should implement in order to
    28  // use the conditions package for getting conditions.
    29  type Getter interface {
    30  	client.Object
    31  
    32  	// GetConditions returns the list of conditions for a cluster API object.
    33  	GetConditions() clusterv1.Conditions
    34  }
    35  
    36  // Get returns the condition with the given type, if the condition does not exists,
    37  // it returns nil.
    38  func Get(from Getter, t clusterv1.ConditionType) *clusterv1.Condition {
    39  	conditions := from.GetConditions()
    40  	if conditions == nil {
    41  		return nil
    42  	}
    43  
    44  	for _, condition := range conditions {
    45  		if condition.Type == t {
    46  			return &condition
    47  		}
    48  	}
    49  	return nil
    50  }
    51  
    52  // Has returns true if a condition with the given type exists.
    53  func Has(from Getter, t clusterv1.ConditionType) bool {
    54  	return Get(from, t) != nil
    55  }
    56  
    57  // IsTrue is true if the condition with the given type is True, otherwise it return false
    58  // if the condition is not True or if the condition does not exist (is nil).
    59  func IsTrue(from Getter, t clusterv1.ConditionType) bool {
    60  	if c := Get(from, t); c != nil {
    61  		return c.Status == corev1.ConditionTrue
    62  	}
    63  	return false
    64  }
    65  
    66  // IsFalse is true if the condition with the given type is False, otherwise it return false
    67  // if the condition is not False or if the condition does not exist (is nil).
    68  func IsFalse(from Getter, t clusterv1.ConditionType) bool {
    69  	if c := Get(from, t); c != nil {
    70  		return c.Status == corev1.ConditionFalse
    71  	}
    72  	return false
    73  }
    74  
    75  // IsUnknown is true if the condition with the given type is Unknown or if the condition
    76  // does not exist (is nil).
    77  func IsUnknown(from Getter, t clusterv1.ConditionType) bool {
    78  	if c := Get(from, t); c != nil {
    79  		return c.Status == corev1.ConditionUnknown
    80  	}
    81  	return true
    82  }
    83  
    84  // GetReason returns a nil safe string of Reason for the condition with the given type.
    85  func GetReason(from Getter, t clusterv1.ConditionType) string {
    86  	if c := Get(from, t); c != nil {
    87  		return c.Reason
    88  	}
    89  	return ""
    90  }
    91  
    92  // GetMessage returns a nil safe string of Message.
    93  func GetMessage(from Getter, t clusterv1.ConditionType) string {
    94  	if c := Get(from, t); c != nil {
    95  		return c.Message
    96  	}
    97  	return ""
    98  }
    99  
   100  // GetSeverity returns the condition Severity or nil if the condition
   101  // does not exist (is nil).
   102  func GetSeverity(from Getter, t clusterv1.ConditionType) *clusterv1.ConditionSeverity {
   103  	if c := Get(from, t); c != nil {
   104  		return &c.Severity
   105  	}
   106  	return nil
   107  }
   108  
   109  // GetLastTransitionTime returns the condition Severity or nil if the condition
   110  // does not exist (is nil).
   111  func GetLastTransitionTime(from Getter, t clusterv1.ConditionType) *metav1.Time {
   112  	if c := Get(from, t); c != nil {
   113  		return &c.LastTransitionTime
   114  	}
   115  	return nil
   116  }
   117  
   118  // summary returns a Ready condition with the summary of all the conditions existing
   119  // on an object. If the object does not have other conditions, no summary condition is generated.
   120  func summary(from Getter, options ...MergeOption) *clusterv1.Condition {
   121  	conditions := from.GetConditions()
   122  
   123  	mergeOpt := &mergeOptions{}
   124  	for _, o := range options {
   125  		o(mergeOpt)
   126  	}
   127  
   128  	// Identifies the conditions in scope for the Summary by taking all the existing conditions except Ready,
   129  	// or, if a list of conditions types is specified, only the conditions the condition in that list.
   130  	conditionsInScope := make([]localizedCondition, 0, len(conditions))
   131  	for i := range conditions {
   132  		c := conditions[i]
   133  		if c.Type == clusterv1.ReadyCondition {
   134  			continue
   135  		}
   136  
   137  		if mergeOpt.conditionTypes != nil {
   138  			found := false
   139  			for _, t := range mergeOpt.conditionTypes {
   140  				if c.Type == t {
   141  					found = true
   142  					break
   143  				}
   144  			}
   145  			if !found {
   146  				continue
   147  			}
   148  		}
   149  
   150  		conditionsInScope = append(conditionsInScope, localizedCondition{
   151  			Condition: &c,
   152  			Getter:    from,
   153  		})
   154  	}
   155  
   156  	// If it is required to add a step counter only if a subset of condition exists, check if the conditions
   157  	// in scope are included in this subset or not.
   158  	if mergeOpt.addStepCounterIfOnlyConditionTypes != nil {
   159  		for _, c := range conditionsInScope {
   160  			found := false
   161  			for _, t := range mergeOpt.addStepCounterIfOnlyConditionTypes {
   162  				if c.Type == t {
   163  					found = true
   164  					break
   165  				}
   166  			}
   167  			if !found {
   168  				mergeOpt.addStepCounter = false
   169  				break
   170  			}
   171  		}
   172  	}
   173  
   174  	// If it is required to add a step counter, determine the total number of conditions defaulting
   175  	// to the selected conditions or, if defined, to the total number of conditions type to be considered.
   176  	if mergeOpt.addStepCounter {
   177  		mergeOpt.stepCounter = len(conditionsInScope)
   178  		if mergeOpt.conditionTypes != nil {
   179  			mergeOpt.stepCounter = len(mergeOpt.conditionTypes)
   180  		}
   181  		if mergeOpt.addStepCounterIfOnlyConditionTypes != nil {
   182  			mergeOpt.stepCounter = len(mergeOpt.addStepCounterIfOnlyConditionTypes)
   183  		}
   184  	}
   185  
   186  	return merge(conditionsInScope, clusterv1.ReadyCondition, mergeOpt)
   187  }
   188  
   189  // mirrorOptions allows to set options for the mirror operation.
   190  type mirrorOptions struct {
   191  	fallbackTo       *bool
   192  	fallbackReason   string
   193  	fallbackSeverity clusterv1.ConditionSeverity
   194  	fallbackMessage  string
   195  }
   196  
   197  // MirrorOptions defines an option for mirroring conditions.
   198  type MirrorOptions func(*mirrorOptions)
   199  
   200  // WithFallbackValue specify a fallback value to use in case the mirrored condition does not exists;
   201  // in case the fallbackValue is false, given values for reason, severity and message will be used.
   202  func WithFallbackValue(fallbackValue bool, reason string, severity clusterv1.ConditionSeverity, message string) MirrorOptions {
   203  	return func(c *mirrorOptions) {
   204  		c.fallbackTo = &fallbackValue
   205  		c.fallbackReason = reason
   206  		c.fallbackSeverity = severity
   207  		c.fallbackMessage = message
   208  	}
   209  }
   210  
   211  // mirror mirrors the Ready condition from a dependent object into the target condition;
   212  // if the Ready condition does not exists in the source object, no target conditions is generated.
   213  func mirror(from Getter, targetCondition clusterv1.ConditionType, options ...MirrorOptions) *clusterv1.Condition {
   214  	mirrorOpt := &mirrorOptions{}
   215  	for _, o := range options {
   216  		o(mirrorOpt)
   217  	}
   218  
   219  	condition := Get(from, clusterv1.ReadyCondition)
   220  
   221  	if mirrorOpt.fallbackTo != nil && condition == nil {
   222  		switch *mirrorOpt.fallbackTo {
   223  		case true:
   224  			condition = TrueCondition(targetCondition)
   225  		case false:
   226  			condition = FalseCondition(targetCondition, mirrorOpt.fallbackReason, mirrorOpt.fallbackSeverity, mirrorOpt.fallbackMessage)
   227  		}
   228  	}
   229  
   230  	if condition != nil {
   231  		condition.Type = targetCondition
   232  	}
   233  
   234  	return condition
   235  }
   236  
   237  // Aggregates all the Ready condition from a list of dependent objects into the target object;
   238  // if the Ready condition does not exists in one of the source object, the object is excluded from
   239  // the aggregation; if none of the source object have ready condition, no target conditions is generated.
   240  func aggregate(from []Getter, targetCondition clusterv1.ConditionType, options ...MergeOption) *clusterv1.Condition {
   241  	conditionsInScope := make([]localizedCondition, 0, len(from))
   242  	for i := range from {
   243  		condition := Get(from[i], clusterv1.ReadyCondition)
   244  
   245  		conditionsInScope = append(conditionsInScope, localizedCondition{
   246  			Condition: condition,
   247  			Getter:    from[i],
   248  		})
   249  	}
   250  
   251  	mergeOpt := &mergeOptions{
   252  		addStepCounter: true,
   253  		stepCounter:    len(from),
   254  	}
   255  	for _, o := range options {
   256  		o(mergeOpt)
   257  	}
   258  	return merge(conditionsInScope, targetCondition, mergeOpt)
   259  }