func (r *PytorchJobReconciler) UpdateJobStatusInApiServer(job interface{}, jobStatus *v1.JobStatus) error {
	pytorchJob, ok := job.(*training.PyTorchJob)
	if !ok {
		return fmt.Errorf("%+v is not a type of PytorchJob", pytorchJob)
	}
	var jobCpy *training.PyTorchJob
	jobCpy = pytorchJob.DeepCopy()
	jobCpy.Status = *jobStatus.DeepCopy()
	return r.Status().Update(context.Background(), jobCpy)
}
UpdateJobStatusInApiServer updates the job status in API server
package pytorch
import (
	"context"
	"fmt"
	training "github.com/alibaba/kubedl/apis/training/v1alpha1"
	"github.com/alibaba/kubedl/pkg/job_controller"
	v1 "github.com/alibaba/kubedl/pkg/job_controller/api/v1"
	"github.com/alibaba/kubedl/pkg/util"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)
func (r *PytorchJobReconciler) GetJobFromInformerCache(namespace, name string) (metav1.Object, error) {
	job := &training.PyTorchJob{}
	err := r.Get(context.Background(), types.NamespacedName{Namespace: namespace, Name: name}, job)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Error(err, "pytorch job not found", "namespace", namespace, "name", name)
		} else {
			log.Error(err, "failed to get job from api-server", "namespace", namespace, "name", name)
		}
		return nil, err
	}
	return job, nil
}
func (r *PytorchJobReconciler) GetJobFromAPIClient(namespace, name string) (metav1.Object, error) {
	job := &training.PyTorchJob{}
	clientReader, err := util.GetClientReaderFromClient(r.Client)
	if err != nil {
		return nil, err
	}
	err = clientReader.Get(context.Background(), types.NamespacedName{Namespace: namespace, Name: name}, job)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Error(err, "pytorch job not found", "namespace", namespace, "name", name)
		} else {
			log.Error(err, "failed to get job from api-server", "namespace", namespace, "name", name)
		}
		return nil, err
	}
	return job, nil
}
func (r *PytorchJobReconciler) DeleteJob(job interface{}) error {
	pytorchJob, ok := job.(*training.PyTorchJob)
	if !ok {
		return fmt.Errorf("%+v is not a type of PytorchJob", pytorchJob)
	}
	if err := r.Delete(context.Background(), pytorchJob); err != nil {
		r.recorder.Eventf(pytorchJob, corev1.EventTypeWarning, job_controller.FailedDeleteJobReason, "Error deleting: %v", err)
		log.Error(err, "failed to delete job", "namespace", pytorchJob.Namespace, "name", pytorchJob.Name)
		return err
	}
	r.recorder.Eventf(pytorchJob, corev1.EventTypeNormal, job_controller.SuccessfulDeleteJobReason, "Deleted job: %v", pytorchJob.Name)
	log.Info("job deleted", "namespace", pytorchJob.Namespace, "name", pytorchJob.Name)
	return nil
}
func (r *PytorchJobReconciler) UpdateJobStatus(job interface{}, replicas map[v1.ReplicaType]*v1.ReplicaSpec, jobStatus *v1.JobStatus, restart bool) error {
	pytorchJob, ok := job.(*training.PyTorchJob)
	if !ok {
		return fmt.Errorf("%+v is not a type of PytorchJob", pytorchJob)
	}
	return r.updateGeneralJobStatus(pytorchJob, replicas, jobStatus, restart)
}