sigs.k8s.io/kueue@v0.6.2/pkg/workload/admissionchecks.go (about)

     1  /*
     2  Copyright 2023 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 workload
    18  
    19  import (
    20  	"time"
    21  
    22  	apimeta "k8s.io/apimachinery/pkg/api/meta"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/util/sets"
    25  
    26  	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
    27  )
    28  
    29  // SyncAdmittedCondition sync the state of the Admitted condition
    30  // with the state of QuotaReserved and AdmissionChecks.
    31  // Return true if any change was done.
    32  func SyncAdmittedCondition(w *kueue.Workload) bool {
    33  	hasReservation := HasQuotaReservation(w)
    34  	hasAllChecksReady := HasAllChecksReady(w)
    35  	isAdmitted := IsAdmitted(w)
    36  
    37  	if isAdmitted == (hasReservation && hasAllChecksReady) {
    38  		return false
    39  	}
    40  	newCondition := metav1.Condition{
    41  		Type:    kueue.WorkloadAdmitted,
    42  		Status:  metav1.ConditionTrue,
    43  		Reason:  "Admitted",
    44  		Message: "The workload is admitted",
    45  	}
    46  	switch {
    47  	case !hasReservation && !hasAllChecksReady:
    48  		newCondition.Status = metav1.ConditionFalse
    49  		newCondition.Reason = "NoReservationNoChecks"
    50  		newCondition.Message = "The workload has no reservation and not all checks ready"
    51  	case !hasReservation:
    52  		newCondition.Status = metav1.ConditionFalse
    53  		newCondition.Reason = "NoReservation"
    54  		newCondition.Message = "The workload has no reservation"
    55  	case !hasAllChecksReady:
    56  		newCondition.Status = metav1.ConditionFalse
    57  		newCondition.Reason = "NoChecks"
    58  		newCondition.Message = "The workload has not all checks ready"
    59  	}
    60  
    61  	return apimeta.SetStatusCondition(&w.Status.Conditions, newCondition)
    62  }
    63  
    64  // FindAdmissionCheck - returns a pointer to the check identified by checkName if found in checks.
    65  func FindAdmissionCheck(checks []kueue.AdmissionCheckState, checkName string) *kueue.AdmissionCheckState {
    66  	for i := range checks {
    67  		if checks[i].Name == checkName {
    68  			return &checks[i]
    69  		}
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  // SetAdmissionCheckState - adds or updates newCheck in the provided checks list.
    76  func SetAdmissionCheckState(checks *[]kueue.AdmissionCheckState, newCheck kueue.AdmissionCheckState) {
    77  	if checks == nil {
    78  		return
    79  	}
    80  	existingCondition := FindAdmissionCheck(*checks, newCheck.Name)
    81  	if existingCondition == nil {
    82  		if newCheck.LastTransitionTime.IsZero() {
    83  			newCheck.LastTransitionTime = metav1.NewTime(time.Now())
    84  		}
    85  		*checks = append(*checks, newCheck)
    86  		return
    87  	}
    88  
    89  	if existingCondition.State != newCheck.State {
    90  		existingCondition.State = newCheck.State
    91  		if !newCheck.LastTransitionTime.IsZero() {
    92  			existingCondition.LastTransitionTime = newCheck.LastTransitionTime
    93  		} else {
    94  			existingCondition.LastTransitionTime = metav1.NewTime(time.Now())
    95  		}
    96  	}
    97  	existingCondition.Message = newCheck.Message
    98  	existingCondition.PodSetUpdates = newCheck.PodSetUpdates
    99  }
   100  
   101  // GetRejectedChecks returns the list of Rejected admission checks
   102  func GetRejectedChecks(wl *kueue.Workload) []string {
   103  	rejectedChecks := make([]string, 0, len(wl.Status.AdmissionChecks))
   104  	for i := range wl.Status.AdmissionChecks {
   105  		ac := wl.Status.AdmissionChecks[i]
   106  		if ac.State == kueue.CheckStateRejected {
   107  			rejectedChecks = append(rejectedChecks, ac.Name)
   108  		}
   109  	}
   110  	return rejectedChecks
   111  }
   112  
   113  // HasAllChecksReady returns true if all the checks of the workload are ready.
   114  func HasAllChecksReady(wl *kueue.Workload) bool {
   115  	for i := range wl.Status.AdmissionChecks {
   116  		if wl.Status.AdmissionChecks[i].State != kueue.CheckStateReady {
   117  			return false
   118  		}
   119  	}
   120  	return true
   121  }
   122  
   123  // HasAllChecks returns true if all the mustHaveChecks are present in the workload.
   124  func HasAllChecks(wl *kueue.Workload, mustHaveChecks sets.Set[string]) bool {
   125  	if mustHaveChecks.Len() == 0 {
   126  		return true
   127  	}
   128  
   129  	if mustHaveChecks.Len() > len(wl.Status.AdmissionChecks) {
   130  		return false
   131  	}
   132  
   133  	mustHaveChecks = mustHaveChecks.Clone()
   134  	for i := range wl.Status.AdmissionChecks {
   135  		mustHaveChecks.Delete(wl.Status.AdmissionChecks[i].Name)
   136  	}
   137  	return mustHaveChecks.Len() == 0
   138  }
   139  
   140  // HasRetryOrRejectedChecks returns true if any of the workloads checks are Retry or Rejected
   141  func HasRetryOrRejectedChecks(wl *kueue.Workload) bool {
   142  	for i := range wl.Status.AdmissionChecks {
   143  		state := wl.Status.AdmissionChecks[i].State
   144  		if state == kueue.CheckStateRetry || state == kueue.CheckStateRejected {
   145  			return true
   146  		}
   147  	}
   148  	return false
   149  }