volcano.sh/volcano@v1.9.0/pkg/controllers/job/job_controller_util.go (about)

     1  /*
     2  Copyright 2017 The Volcano 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 job
    18  
    19  import (
    20  	"fmt"
    21  	"strconv"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	"k8s.io/klog/v2"
    27  
    28  	batch "volcano.sh/apis/pkg/apis/batch/v1alpha1"
    29  	"volcano.sh/apis/pkg/apis/bus/v1alpha1"
    30  	"volcano.sh/apis/pkg/apis/helpers"
    31  	schedulingv2 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    32  	"volcano.sh/volcano/pkg/controllers/apis"
    33  	jobhelpers "volcano.sh/volcano/pkg/controllers/job/helpers"
    34  )
    35  
    36  // MakePodName append podname,jobname,taskName and index and returns the string.
    37  func MakePodName(jobName string, taskName string, index int) string {
    38  	return fmt.Sprintf(jobhelpers.PodNameFmt, jobName, taskName, index)
    39  }
    40  
    41  func createJobPod(job *batch.Job, template *v1.PodTemplateSpec, topologyPolicy batch.NumaPolicy, ix int, jobForwarding bool) *v1.Pod {
    42  	templateCopy := template.DeepCopy()
    43  
    44  	pod := &v1.Pod{
    45  		ObjectMeta: metav1.ObjectMeta{
    46  			Name:      jobhelpers.MakePodName(job.Name, template.Name, ix),
    47  			Namespace: job.Namespace,
    48  			OwnerReferences: []metav1.OwnerReference{
    49  				*metav1.NewControllerRef(job, helpers.JobKind),
    50  			},
    51  			Labels:      templateCopy.Labels,
    52  			Annotations: templateCopy.Annotations,
    53  		},
    54  		Spec: templateCopy.Spec,
    55  	}
    56  
    57  	// If no scheduler name in Pod, use scheduler name from Job.
    58  	if len(pod.Spec.SchedulerName) == 0 {
    59  		pod.Spec.SchedulerName = job.Spec.SchedulerName
    60  	}
    61  
    62  	volumeMap := make(map[string]string)
    63  	for _, volume := range job.Spec.Volumes {
    64  		vcName := volume.VolumeClaimName
    65  		name := fmt.Sprintf("%s-%s", job.Name, jobhelpers.GenRandomStr(12))
    66  		if _, ok := volumeMap[vcName]; !ok {
    67  			volume := v1.Volume{
    68  				Name: name,
    69  				VolumeSource: v1.VolumeSource{
    70  					PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
    71  						ClaimName: vcName,
    72  					},
    73  				},
    74  			}
    75  			pod.Spec.Volumes = append(pod.Spec.Volumes, volume)
    76  			volumeMap[vcName] = name
    77  		} else {
    78  			// duplicate volumes, should be prevented
    79  			continue
    80  		}
    81  
    82  		for i, c := range pod.Spec.Containers {
    83  			vm := v1.VolumeMount{
    84  				MountPath: volume.MountPath,
    85  				Name:      name,
    86  			}
    87  			pod.Spec.Containers[i].VolumeMounts = append(c.VolumeMounts, vm)
    88  		}
    89  	}
    90  
    91  	tsKey := templateCopy.Name
    92  	if len(tsKey) == 0 {
    93  		tsKey = batch.DefaultTaskSpec
    94  	}
    95  
    96  	if len(pod.Annotations) == 0 {
    97  		pod.Annotations = make(map[string]string)
    98  	}
    99  
   100  	index := strconv.Itoa(ix)
   101  	pod.Annotations[batch.TaskIndex] = index
   102  	pod.Annotations[batch.TaskSpecKey] = tsKey
   103  	pgName := job.Name + "-" + string(job.UID)
   104  	pod.Annotations[schedulingv2.KubeGroupNameAnnotationKey] = pgName
   105  	pod.Annotations[batch.JobNameKey] = job.Name
   106  	pod.Annotations[batch.QueueNameKey] = job.Spec.Queue
   107  	pod.Annotations[batch.JobVersion] = fmt.Sprintf("%d", job.Status.Version)
   108  	pod.Annotations[batch.PodTemplateKey] = fmt.Sprintf("%s-%s", job.Name, template.Name)
   109  
   110  	if topologyPolicy != "" {
   111  		pod.Annotations[schedulingv2.NumaPolicyKey] = string(topologyPolicy)
   112  	}
   113  
   114  	if len(job.Annotations) > 0 {
   115  		if value, found := job.Annotations[schedulingv2.PodPreemptable]; found {
   116  			pod.Annotations[schedulingv2.PodPreemptable] = value
   117  		}
   118  		if value, found := job.Annotations[schedulingv2.CooldownTime]; found {
   119  			pod.Annotations[schedulingv2.CooldownTime] = value
   120  		}
   121  		if value, found := job.Annotations[schedulingv2.RevocableZone]; found {
   122  			pod.Annotations[schedulingv2.RevocableZone] = value
   123  		}
   124  
   125  		if value, found := job.Annotations[schedulingv2.JDBMinAvailable]; found {
   126  			pod.Annotations[schedulingv2.JDBMinAvailable] = value
   127  		} else if value, found := job.Annotations[schedulingv2.JDBMaxUnavailable]; found {
   128  			pod.Annotations[schedulingv2.JDBMaxUnavailable] = value
   129  		}
   130  	}
   131  
   132  	if len(pod.Labels) == 0 {
   133  		pod.Labels = make(map[string]string)
   134  	}
   135  
   136  	// Set pod labels for Service.
   137  	pod.Labels[batch.TaskIndex] = index
   138  	pod.Labels[batch.JobNameKey] = job.Name
   139  	pod.Labels[batch.TaskSpecKey] = tsKey
   140  	pod.Labels[batch.JobNamespaceKey] = job.Namespace
   141  	pod.Labels[batch.QueueNameKey] = job.Spec.Queue
   142  	if len(job.Labels) > 0 {
   143  		if value, found := job.Labels[schedulingv2.PodPreemptable]; found {
   144  			pod.Labels[schedulingv2.PodPreemptable] = value
   145  		}
   146  		if value, found := job.Labels[schedulingv2.CooldownTime]; found {
   147  			pod.Labels[schedulingv2.CooldownTime] = value
   148  		}
   149  	}
   150  
   151  	if jobForwarding {
   152  		pod.Annotations[batch.JobForwardingKey] = "true"
   153  		pod.Labels[batch.JobForwardingKey] = "true"
   154  	}
   155  
   156  	return pod
   157  }
   158  
   159  func applyPolicies(job *batch.Job, req *apis.Request) v1alpha1.Action {
   160  	if len(req.Action) != 0 {
   161  		return req.Action
   162  	}
   163  
   164  	if req.Event == v1alpha1.OutOfSyncEvent {
   165  		return v1alpha1.SyncJobAction
   166  	}
   167  
   168  	// For all the requests triggered from discarded job resources will perform sync action instead
   169  	if req.JobVersion < job.Status.Version {
   170  		klog.Infof("Request %s is outdated, will perform sync instead.", req)
   171  		return v1alpha1.SyncJobAction
   172  	}
   173  
   174  	// Overwrite Job level policies
   175  	if len(req.TaskName) != 0 {
   176  		// Parse task level policies
   177  		for _, task := range job.Spec.Tasks {
   178  			if task.Name == req.TaskName {
   179  				for _, policy := range task.Policies {
   180  					policyEvents := getEventlist(policy)
   181  
   182  					if len(policyEvents) > 0 && len(req.Event) > 0 {
   183  						if checkEventExist(policyEvents, req.Event) || checkEventExist(policyEvents, v1alpha1.AnyEvent) {
   184  							return policy.Action
   185  						}
   186  					}
   187  
   188  					// 0 is not an error code, is prevented in validation admission controller
   189  					if policy.ExitCode != nil && *policy.ExitCode == req.ExitCode {
   190  						return policy.Action
   191  					}
   192  				}
   193  				break
   194  			}
   195  		}
   196  	}
   197  
   198  	// Parse Job level policies
   199  	for _, policy := range job.Spec.Policies {
   200  		policyEvents := getEventlist(policy)
   201  
   202  		if len(policyEvents) > 0 && len(req.Event) > 0 {
   203  			if checkEventExist(policyEvents, req.Event) || checkEventExist(policyEvents, v1alpha1.AnyEvent) {
   204  				return policy.Action
   205  			}
   206  		}
   207  
   208  		// 0 is not an error code, is prevented in validation admission controller
   209  		if policy.ExitCode != nil && *policy.ExitCode == req.ExitCode {
   210  			return policy.Action
   211  		}
   212  	}
   213  
   214  	return v1alpha1.SyncJobAction
   215  }
   216  
   217  func getEventlist(policy batch.LifecyclePolicy) []v1alpha1.Event {
   218  	policyEventsList := policy.Events
   219  	if len(policy.Event) > 0 {
   220  		policyEventsList = append(policyEventsList, policy.Event)
   221  	}
   222  	return policyEventsList
   223  }
   224  
   225  func checkEventExist(policyEvents []v1alpha1.Event, reqEvent v1alpha1.Event) bool {
   226  	for _, event := range policyEvents {
   227  		if event == reqEvent {
   228  			return true
   229  		}
   230  	}
   231  	return false
   232  }
   233  
   234  // TaskPriority structure.
   235  type TaskPriority struct {
   236  	priority int32
   237  
   238  	batch.TaskSpec
   239  }
   240  
   241  // TasksPriority is a slice of TaskPriority.
   242  type TasksPriority []TaskPriority
   243  
   244  func (p TasksPriority) Len() int { return len(p) }
   245  
   246  func (p TasksPriority) Less(i, j int) bool {
   247  	return p[i].priority > p[j].priority
   248  }
   249  
   250  func (p TasksPriority) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
   251  
   252  func isControlledBy(obj metav1.Object, gvk schema.GroupVersionKind) bool {
   253  	controllerRef := metav1.GetControllerOf(obj)
   254  	if controllerRef == nil {
   255  		return false
   256  	}
   257  	if controllerRef.APIVersion == gvk.GroupVersion().String() && controllerRef.Kind == gvk.Kind {
   258  		return true
   259  	}
   260  	return false
   261  }