133 lines
4.0 KiB
Go
133 lines
4.0 KiB
Go
package kubernetes
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
executionv1alpha1 "git.ddupan.top/panxiao81/ayatori/api/execution/v1alpha1"
|
|
batchv1 "k8s.io/api/batch/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
)
|
|
|
|
const (
|
|
ControllerName = "execution.ayatori.ddupan.top/kubernetes"
|
|
ReferenceType = "Job"
|
|
JobUIDLabel = "execution.ayatori.ddupan.top/job-uid"
|
|
)
|
|
|
|
var ayatoriJobGVK = schema.GroupVersionKind{
|
|
Group: executionv1alpha1.GroupVersion.Group,
|
|
Version: executionv1alpha1.GroupVersion.Version,
|
|
Kind: "Job",
|
|
}
|
|
|
|
// BuildJob translates the stable execution API into the Kubernetes adapter's
|
|
// backend object. It intentionally does not accept or expose a PodSpec.
|
|
func BuildJob(
|
|
job *executionv1alpha1.Job,
|
|
parameters *executionv1alpha1.KubernetesExecutionParameters,
|
|
resources executionv1alpha1.ExecutionResourceRequirements,
|
|
) *batchv1.Job {
|
|
backoffLimit := int32(0)
|
|
controller := true
|
|
blockOwnerDeletion := true
|
|
|
|
//nolint:modernize // ObjectMeta is promoted through embedded TypeMeta; embedlit produces invalid Go here.
|
|
return &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: job.Name,
|
|
Namespace: job.Namespace,
|
|
Labels: map[string]string{
|
|
JobUIDLabel: string(job.UID),
|
|
},
|
|
OwnerReferences: []metav1.OwnerReference{{
|
|
APIVersion: ayatoriJobGVK.GroupVersion().String(),
|
|
Kind: ayatoriJobGVK.Kind,
|
|
Name: job.Name,
|
|
UID: job.UID,
|
|
Controller: &controller,
|
|
BlockOwnerDeletion: &blockOwnerDeletion,
|
|
}},
|
|
},
|
|
Spec: batchv1.JobSpec{
|
|
BackoffLimit: &backoffLimit,
|
|
Template: corev1.PodTemplateSpec{
|
|
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{JobUIDLabel: string(job.UID)}},
|
|
Spec: corev1.PodSpec{
|
|
RestartPolicy: corev1.RestartPolicyNever,
|
|
ServiceAccountName: parameters.Spec.ServiceAccountName,
|
|
RuntimeClassName: optionalString(parameters.Spec.RuntimeClassName),
|
|
NodeSelector: parameters.Spec.Scheduling.NodeSelector,
|
|
Tolerations: parameters.Spec.Scheduling.Tolerations,
|
|
SecurityContext: parameters.Spec.PodSecurityContext,
|
|
ImagePullSecrets: job.Spec.Task.ImagePullSecrets,
|
|
Containers: []corev1.Container{{
|
|
Name: "task",
|
|
Image: job.Spec.Task.Image,
|
|
ImagePullPolicy: parameters.Spec.ImagePullPolicy,
|
|
Command: job.Spec.Task.Command,
|
|
Args: job.Spec.Task.Args,
|
|
WorkingDir: job.Spec.Task.WorkingDir,
|
|
Env: environment(job.Spec.Task.Env),
|
|
Resources: resourceRequirements(resources),
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func ValidateOwnership(owner *executionv1alpha1.Job, backend *batchv1.Job) error {
|
|
if backend.Labels[JobUIDLabel] != string(owner.UID) {
|
|
return fmt.Errorf("backend Job %s/%s is not owned by Ayatori Job UID %s", backend.Namespace, backend.Name, owner.UID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func environment(values []executionv1alpha1.EnvVar) []corev1.EnvVar {
|
|
result := make([]corev1.EnvVar, 0, len(values))
|
|
for _, value := range values {
|
|
env := corev1.EnvVar{Name: value.Name}
|
|
if value.Value != nil {
|
|
env.Value = *value.Value
|
|
}
|
|
if value.ValueFrom != nil {
|
|
env.ValueFrom = &corev1.EnvVarSource{
|
|
SecretKeyRef: value.ValueFrom.SecretKeyRef,
|
|
ConfigMapKeyRef: value.ValueFrom.ConfigMapKeyRef,
|
|
}
|
|
}
|
|
result = append(result, env)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func resourceRequirements(resources executionv1alpha1.ExecutionResourceRequirements) corev1.ResourceRequirements {
|
|
return corev1.ResourceRequirements{
|
|
Requests: resourceList(resources.Requests),
|
|
Limits: resourceList(resources.Limits),
|
|
}
|
|
}
|
|
|
|
func resourceList(values executionv1alpha1.ResourceValues) corev1.ResourceList {
|
|
result := corev1.ResourceList{}
|
|
if values.CPU != nil {
|
|
result[corev1.ResourceCPU] = values.CPU.DeepCopy()
|
|
}
|
|
if values.Memory != nil {
|
|
result[corev1.ResourceMemory] = values.Memory.DeepCopy()
|
|
}
|
|
if len(result) == 0 {
|
|
return nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
func optionalString(value string) *string {
|
|
if value == "" {
|
|
return nil
|
|
}
|
|
return &value
|
|
}
|