package cli import ( "context" "strings" models "github.com/portainer/portainer/api/http/models/kubernetes" "github.com/portainer/portainer/api/internal/errorlist" batchv1 "k8s.io/api/batch/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // GetCronJobs returns all cronjobs in the given namespace // If the user is a kube admin, it returns all cronjobs in the namespace // Otherwise, it returns only the cronjobs in the non-admin namespaces func (kcl *KubeClient) GetCronJobs(namespace string) ([]models.K8sCronJob, error) { if kcl.IsKubeAdmin { return kcl.fetchCronJobs(namespace) } return kcl.fetchCronJobsForNonAdmin(namespace) } // fetchCronJobsForNonAdmin returns all cronjobs in the given namespace // It returns only the cronjobs in the non-admin namespaces func (kcl *KubeClient) fetchCronJobsForNonAdmin(namespace string) ([]models.K8sCronJob, error) { cronJobs, err := kcl.fetchCronJobs(namespace) if err != nil { return nil, err } nonAdminNamespaceSet := kcl.buildNonAdminNamespacesMap() results := make([]models.K8sCronJob, 0) for _, cronJob := range cronJobs { if _, ok := nonAdminNamespaceSet[cronJob.Namespace]; ok { results = append(results, cronJob) } } return results, nil } // fetchCronJobs returns all cronjobs in the given namespace // It returns all cronjobs in the namespace func (kcl *KubeClient) fetchCronJobs(namespace string) ([]models.K8sCronJob, error) { cronJobs, err := kcl.cli.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err } jobs, err := kcl.cli.BatchV1().Jobs(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err } results := make([]models.K8sCronJob, 0) for _, cronJob := range cronJobs.Items { results = append(results, kcl.parseCronJob(cronJob, jobs)) } return results, nil } // parseCronJob converts a batchv1.CronJob object to a models.K8sCronJob object. func (kcl *KubeClient) parseCronJob(cronJob batchv1.CronJob, jobsList *batchv1.JobList) models.K8sCronJob { jobs, err := kcl.getCronJobExecutions(cronJob.Name, jobsList) if err != nil { return models.K8sCronJob{} } timezone := "" if cronJob.Spec.TimeZone != nil { timezone = *cronJob.Spec.TimeZone } suspend := false if cronJob.Spec.Suspend != nil { suspend = *cronJob.Spec.Suspend } return models.K8sCronJob{ Id: string(cronJob.UID), Name: cronJob.Name, Namespace: cronJob.Namespace, Command: strings.Join(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Command, " "), Schedule: cronJob.Spec.Schedule, Timezone: timezone, Suspend: suspend, Jobs: jobs, IsSystem: kcl.isSystemCronJob(cronJob.Namespace), } } func (kcl *KubeClient) isSystemCronJob(namespace string) bool { return kcl.isSystemNamespace(namespace) } // DeleteCronJobs deletes the provided list of cronjobs in its namespace // it returns an error if any of the cronjobs are not found or if there is an error deleting the cronjobs func (kcl *KubeClient) DeleteCronJobs(payload models.K8sCronJobDeleteRequests) error { var errors []error for namespace := range payload { for _, cronJobName := range payload[namespace] { client := kcl.cli.BatchV1().CronJobs(namespace) _, err := client.Get(context.Background(), cronJobName, metav1.GetOptions{}) if err != nil { if k8serrors.IsNotFound(err) { continue } errors = append(errors, err) } if err := client.Delete(context.Background(), cronJobName, metav1.DeleteOptions{}); err != nil { errors = append(errors, err) } } } return errorlist.Combine(errors) }