github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/dataprotection/action/action_job.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package action 21 22 import ( 23 "fmt" 24 25 batchv1 "k8s.io/api/batch/v1" 26 corev1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 ref "k8s.io/client-go/tools/reference" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 31 32 dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1" 33 ctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 34 "github.com/1aal/kubeblocks/pkg/dataprotection/types" 35 "github.com/1aal/kubeblocks/pkg/dataprotection/utils" 36 ) 37 38 // JobAction is an action that creates a batch job. 39 type JobAction struct { 40 // Name is the Name of the action. 41 Name string 42 43 // Owner is the owner of the job. 44 Owner client.Object 45 46 // ObjectMeta is the metadata of the job. 47 ObjectMeta metav1.ObjectMeta 48 49 // PodSpec is the 50 PodSpec *corev1.PodSpec 51 52 // BackOffLimit is the number of retries before considering a JobAction as failed. 53 BackOffLimit *int32 54 } 55 56 func (j *JobAction) GetName() string { 57 return j.Name 58 } 59 60 func (j *JobAction) Type() dpv1alpha1.ActionType { 61 return dpv1alpha1.ActionTypeJob 62 } 63 64 func (j *JobAction) Execute(ctx Context) (*dpv1alpha1.ActionStatus, error) { 65 sb := newStatusBuilder(j) 66 handleErr := func(err error) (*dpv1alpha1.ActionStatus, error) { 67 return sb.withErr(err).build(), err 68 } 69 70 if err := j.validate(); err != nil { 71 return handleErr(err) 72 } 73 74 key := client.ObjectKey{ 75 Namespace: j.ObjectMeta.Namespace, 76 Name: j.ObjectMeta.Name, 77 } 78 original := batchv1.Job{} 79 exists, err := ctrlutil.CheckResourceExists(ctx.Ctx, ctx.Client, key, &original) 80 if err != nil { 81 return handleErr(err) 82 } 83 84 // job exists, check job status and set action status accordingly 85 if exists { 86 objRef, _ := ref.GetReference(ctx.Scheme, &original) 87 sb = sb.startTimestamp(&original.CreationTimestamp).objectRef(objRef) 88 _, finishedType, msg := utils.IsJobFinished(&original) 89 switch finishedType { 90 case batchv1.JobComplete: 91 return sb.phase(dpv1alpha1.ActionPhaseCompleted). 92 completionTimestamp(nil). 93 build(), nil 94 case batchv1.JobFailed: 95 return sb.phase(dpv1alpha1.ActionPhaseFailed). 96 completionTimestamp(nil). 97 reason(msg). 98 build(), nil 99 } 100 // job is running 101 return handleErr(nil) 102 } 103 104 // job doesn't exist, create it 105 job := &batchv1.Job{ 106 ObjectMeta: j.ObjectMeta, 107 Spec: batchv1.JobSpec{ 108 Template: corev1.PodTemplateSpec{ 109 ObjectMeta: j.ObjectMeta, 110 Spec: *j.PodSpec, 111 }, 112 BackoffLimit: j.BackOffLimit, 113 }, 114 } 115 116 controllerutil.AddFinalizer(job, types.DataProtectionFinalizerName) 117 if err = utils.SetControllerReference(j.Owner, job, ctx.Scheme); err != nil { 118 return handleErr(err) 119 } 120 msg := fmt.Sprintf("creating job %s/%s", job.Namespace, job.Name) 121 ctx.Recorder.Event(j.Owner, corev1.EventTypeNormal, "CreatingJob", msg) 122 return handleErr(client.IgnoreAlreadyExists(ctx.Client.Create(ctx.Ctx, job))) 123 } 124 125 func (j *JobAction) validate() error { 126 if j.ObjectMeta.Name == "" { 127 return fmt.Errorf("name is required") 128 } 129 if j.PodSpec == nil { 130 return fmt.Errorf("PodSpec is required") 131 } 132 if j.BackOffLimit == nil { 133 j.BackOffLimit = &types.DefaultBackOffLimit 134 } 135 return nil 136 } 137 138 var _ Action = &JobAction{}