github.com/giantswarm/apiextensions/v2@v2.6.2/pkg/apis/infrastructure/v1alpha2/common_cluster_status_funcs.go (about)

     1  package v1alpha2
     2  
     3  import (
     4  	"sort"
     5  	"time"
     6  
     7  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     8  )
     9  
    10  func (s CommonClusterStatus) GetCreatedCondition() CommonClusterStatusCondition {
    11  	return getCondition(s.Conditions, ClusterStatusConditionCreated)
    12  }
    13  
    14  func (s CommonClusterStatus) GetCreatingCondition() CommonClusterStatusCondition {
    15  	return getCondition(s.Conditions, ClusterStatusConditionCreating)
    16  }
    17  
    18  func (s CommonClusterStatus) GetDeletedCondition() CommonClusterStatusCondition {
    19  	return getCondition(s.Conditions, ClusterStatusConditionDeleted)
    20  }
    21  
    22  func (s CommonClusterStatus) GetDeletingCondition() CommonClusterStatusCondition {
    23  	return getCondition(s.Conditions, ClusterStatusConditionDeleting)
    24  }
    25  
    26  func (s CommonClusterStatus) GetUpdatedCondition() CommonClusterStatusCondition {
    27  	return getCondition(s.Conditions, ClusterStatusConditionUpdated)
    28  }
    29  
    30  func (s CommonClusterStatus) GetUpdatingCondition() CommonClusterStatusCondition {
    31  	return getCondition(s.Conditions, ClusterStatusConditionUpdating)
    32  }
    33  
    34  func (s CommonClusterStatus) HasCreatedCondition() bool {
    35  	return hasCondition(s.Conditions, ClusterStatusConditionCreated)
    36  }
    37  
    38  func (s CommonClusterStatus) HasCreatingCondition() bool {
    39  	return hasCondition(s.Conditions, ClusterStatusConditionCreating)
    40  }
    41  
    42  func (s CommonClusterStatus) HasDeletedCondition() bool {
    43  	return hasCondition(s.Conditions, ClusterStatusConditionDeleted)
    44  }
    45  
    46  func (s CommonClusterStatus) HasDeletingCondition() bool {
    47  	return hasCondition(s.Conditions, ClusterStatusConditionDeleting)
    48  }
    49  
    50  func (s CommonClusterStatus) HasUpdatedCondition() bool {
    51  	return hasCondition(s.Conditions, ClusterStatusConditionUpdated)
    52  }
    53  
    54  func (s CommonClusterStatus) HasUpdatingCondition() bool {
    55  	return hasCondition(s.Conditions, ClusterStatusConditionUpdating)
    56  }
    57  
    58  func (s CommonClusterStatus) HasVersion(semver string) bool {
    59  	return hasVersion(s.Versions, semver)
    60  }
    61  
    62  func (s CommonClusterStatus) LatestCondition() string {
    63  	if len(s.Conditions) == 0 {
    64  		return ""
    65  	}
    66  
    67  	sort.Sort(sort.Reverse(sortClusterStatusConditionsByDate(s.Conditions)))
    68  
    69  	return s.Conditions[0].Condition
    70  }
    71  
    72  func (s CommonClusterStatus) LatestVersion() string {
    73  	if len(s.Versions) == 0 {
    74  		return ""
    75  	}
    76  
    77  	sort.Sort(sort.Reverse(sortClusterStatusVersionsByDate(s.Versions)))
    78  
    79  	return s.Versions[0].Version
    80  }
    81  
    82  func (s CommonClusterStatus) WithCreatedCondition() []CommonClusterStatusCondition {
    83  	newCondition := CommonClusterStatusCondition{
    84  		LastTransitionTime: metav1.Now(),
    85  		Condition:          ClusterStatusConditionCreated,
    86  	}
    87  
    88  	return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
    89  }
    90  
    91  func (s CommonClusterStatus) WithCreatingCondition() []CommonClusterStatusCondition {
    92  	newCondition := CommonClusterStatusCondition{
    93  		LastTransitionTime: metav1.Now(),
    94  		Condition:          ClusterStatusConditionCreating,
    95  	}
    96  
    97  	return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
    98  }
    99  
   100  func (s CommonClusterStatus) WithDeletedCondition() []CommonClusterStatusCondition {
   101  	newCondition := CommonClusterStatusCondition{
   102  		LastTransitionTime: metav1.Now(),
   103  		Condition:          ClusterStatusConditionDeleted,
   104  	}
   105  
   106  	return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
   107  }
   108  
   109  func (s CommonClusterStatus) WithDeletingCondition() []CommonClusterStatusCondition {
   110  	newCondition := CommonClusterStatusCondition{
   111  		LastTransitionTime: metav1.Now(),
   112  		Condition:          ClusterStatusConditionDeleting,
   113  	}
   114  
   115  	return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
   116  }
   117  
   118  func (s CommonClusterStatus) WithNewVersion(version string) []CommonClusterStatusVersion {
   119  	newVersion := CommonClusterStatusVersion{
   120  		LastTransitionTime: metav1.Now(),
   121  		Version:            version,
   122  	}
   123  
   124  	return withVersion(s.Versions, newVersion, ClusterVersionLimit)
   125  }
   126  
   127  func (s CommonClusterStatus) WithUpdatedCondition() []CommonClusterStatusCondition {
   128  	newCondition := CommonClusterStatusCondition{
   129  		LastTransitionTime: metav1.Now(),
   130  		Condition:          ClusterStatusConditionUpdated,
   131  	}
   132  
   133  	return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
   134  }
   135  
   136  func (s CommonClusterStatus) WithUpdatingCondition() []CommonClusterStatusCondition {
   137  	newCondition := CommonClusterStatusCondition{
   138  		LastTransitionTime: metav1.Now(),
   139  		Condition:          ClusterStatusConditionUpdating,
   140  	}
   141  
   142  	return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
   143  }
   144  
   145  func getCondition(conditions []CommonClusterStatusCondition, condition string) CommonClusterStatusCondition {
   146  	for _, c := range conditions {
   147  		if c.Condition == condition {
   148  			return c
   149  		}
   150  	}
   151  
   152  	return CommonClusterStatusCondition{}
   153  }
   154  
   155  func getConditionForPair(a CommonClusterStatusCondition) string {
   156  	for _, p := range conditionPairs {
   157  		if p[0] == a.Condition {
   158  			return p[1]
   159  		}
   160  		if p[1] == a.Condition {
   161  			return p[0]
   162  		}
   163  	}
   164  
   165  	return ""
   166  }
   167  
   168  func hasCondition(conditions []CommonClusterStatusCondition, condition string) bool {
   169  	for _, c := range conditions {
   170  		if c.Condition == condition {
   171  			return true
   172  		}
   173  	}
   174  
   175  	return false
   176  }
   177  
   178  func hasVersion(versions []CommonClusterStatusVersion, search string) bool {
   179  	for _, v := range versions {
   180  		if v.Version == search {
   181  			return true
   182  		}
   183  	}
   184  
   185  	return false
   186  }
   187  
   188  func isConditionPair(a CommonClusterStatusCondition, b CommonClusterStatusCondition) bool {
   189  	for _, p := range conditionPairs {
   190  		if p[0] == a.Condition && p[1] == b.Condition {
   191  			return true
   192  		}
   193  		if p[1] == a.Condition && p[0] == b.Condition {
   194  			return true
   195  		}
   196  	}
   197  
   198  	return false
   199  }
   200  
   201  // withCondition takes a list of status conditions and manages the given list
   202  // according to the condition to add on top and the given limit argument. The
   203  // limit argument should always only be given by ClusterConditionLimit. Also see
   204  // the godoc there. The limit is applied to condition pairs as defined by
   205  // conditionPairs. Internally the given conditions list is copied so that the
   206  // input arguments are not manipulated by accident. One specific functionality
   207  // of withCondition is that incomplete condition pairs are completed
   208  // automatically as this may happen due to unexpected behaviour in the callers
   209  // environment. For more information on implementation details read the inline
   210  // comments of the code.
   211  func withCondition(conditions []CommonClusterStatusCondition, condition CommonClusterStatusCondition, limit int) []CommonClusterStatusCondition {
   212  	// We create a new list which acts like a copy so the input parameters are not
   213  	// manipulated. Here we also prepend the given condition and inject certain
   214  	// missing conditions in case the condition list gets out of sync
   215  	// unintendedly due to any eventual bugs. Test case 8 demonstrates that.
   216  	var newConditions []CommonClusterStatusCondition
   217  	{
   218  		if len(conditions) > 0 && conditions[0].Condition == condition.Condition {
   219  			injected := CommonClusterStatusCondition{
   220  				// The implication of unintendedly untracked conditions is that the
   221  				// automatically added condition does not obtain a reasonable timestamp.
   222  				// Here we take the timestamp of the new condition we want to track and
   223  				// substract one nano second from it to keep the order intact.
   224  				LastTransitionTime: metav1.Time{Time: condition.LastTransitionTime.Add(-(1 * time.Nanosecond))},
   225  				Condition:          getConditionForPair(condition),
   226  			}
   227  			newConditions = append(newConditions, injected)
   228  		}
   229  
   230  		newConditions = append(newConditions, condition)
   231  		newConditions = append(newConditions, conditions...)
   232  	}
   233  
   234  	// The new list is sorted to have the first item being the oldest. This is to
   235  	// have an easier grouping mechanism below. When the first item of a new pair
   236  	// is added, it would throw of the grouping when the order would be kept as
   237  	// given.
   238  	sort.Sort(sortClusterStatusConditionsByDate(newConditions))
   239  
   240  	// The conditions are grouped into their corresponding pairs of transitioning
   241  	// states. Associated Creating/Created, Updating/Updated and Deleting/Deleted
   242  	// conditions are put together.
   243  	var conditionGroups [][]CommonClusterStatusCondition
   244  	for len(newConditions) > 0 {
   245  		var g []CommonClusterStatusCondition
   246  
   247  		for _, c := range newConditions {
   248  			// If the list only contains one item anymore, we process it separately
   249  			// here and be done. Otherwhise the pruning of the list below panics due
   250  			// to the range calculations.
   251  			if len(newConditions) == 1 {
   252  				g = append(g, c)
   253  				newConditions = []CommonClusterStatusCondition{}
   254  				break
   255  			}
   256  
   257  			// Put the first item from the top of the list into the group and drop
   258  			// the grouped item from the list.
   259  			if len(g) == 0 {
   260  				g = append(g, c)
   261  				newConditions = newConditions[1:]
   262  				continue
   263  			}
   264  
   265  			// When we find the second item of the pair we are done for this group.
   266  			if len(g) == 1 {
   267  				if isConditionPair(g[0], c) {
   268  					g = append(g, c)
   269  					newConditions = newConditions[1:]
   270  				}
   271  				break
   272  			}
   273  		}
   274  
   275  		conditionGroups = append(conditionGroups, g)
   276  	}
   277  
   278  	// The pairs are now grouped. When there are only three group kinds for
   279  	// create/update/delete, conditionPairs has a length of 3. Each of the groups
   280  	// has then as many pairs as grouped together. Below these groups are limited.
   281  	var conditionPairs [][]CommonClusterStatusCondition
   282  	for len(conditionGroups) > 0 {
   283  		var p []CommonClusterStatusCondition
   284  
   285  		for _, g := range conditionGroups {
   286  			if len(p) == 0 {
   287  				p = append(p, g...)
   288  				conditionGroups = conditionGroups[1:]
   289  				continue
   290  			}
   291  
   292  			if len(g) >= 1 {
   293  				if isConditionPair(p[0], g[0]) || isConditionPair(p[1], g[0]) {
   294  					p = append(p, g...)
   295  					conditionGroups = conditionGroups[1:]
   296  				}
   297  			}
   298  		}
   299  
   300  		conditionPairs = append(conditionPairs, p)
   301  	}
   302  
   303  	// Here the list is finally flattened again and its pairs are limitted to the
   304  	// input parameter.
   305  	var limittedList []CommonClusterStatusCondition
   306  	for _, p := range conditionPairs {
   307  		// We compute the pair limit here for the total number of items. This is why
   308  		// we multiply by 2. When the limit is 5, we want to track for instance 5
   309  		// Updating/Updated pairs. Additionally when there is an item of a new pair
   310  		// and the list must be capped, the additional odd of the new item has to be
   311  		// considered when computing the limit. This results in an additional pair
   312  		// being dropped. Test case 6 demonstrates that.
   313  		l := (limit * 2) - (len(p) % 2)
   314  		if len(p) < l {
   315  			l = len(p)
   316  		}
   317  
   318  		limittedList = append(limittedList, p[len(p)-l:]...)
   319  	}
   320  
   321  	// We reverse the list order to have the item with the highest timestamp at
   322  	// the top again.
   323  	sort.Sort(sort.Reverse(sortClusterStatusConditionsByDate(limittedList)))
   324  
   325  	return limittedList
   326  }
   327  
   328  // withVersion computes a list of version history using the given list and new
   329  // version structure to append. withVersion also limits the total amount of
   330  // elements in the list by cutting off the tail with respect to the limit
   331  // parameter.
   332  func withVersion(versions []CommonClusterStatusVersion, version CommonClusterStatusVersion, limit int) []CommonClusterStatusVersion {
   333  	if hasVersion(versions, version.Version) {
   334  		return versions
   335  	}
   336  
   337  	// Create a copy to not manipulate the input list.
   338  	var newVersions []CommonClusterStatusVersion
   339  	newVersions = append(newVersions, versions...)
   340  
   341  	// Sort the versions in a way that the newest version, namely the one with the
   342  	// highest timestamp, is at the top of the list.
   343  	sort.Sort(sort.Reverse(sortClusterStatusVersionsByDate(newVersions)))
   344  
   345  	// Calculate the index for capping the list in the next step.
   346  	l := limit - 1
   347  	if len(newVersions) < l {
   348  		l = len(newVersions)
   349  	}
   350  
   351  	// Cap the list and prepend the new version.
   352  	newVersions = append([]CommonClusterStatusVersion{version}, newVersions[0:l]...)
   353  
   354  	return newVersions
   355  }