-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from warjiang/feature/cronjob-api
feat: add api for cronjob resources
- Loading branch information
Showing
8 changed files
with
455 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package deployment | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
"github.com/karmada-io/dashboard/cmd/api/app/router" | ||
"github.com/karmada-io/dashboard/cmd/api/app/types/common" | ||
"github.com/karmada-io/dashboard/pkg/client" | ||
"github.com/karmada-io/dashboard/pkg/resource/cronjob" | ||
"github.com/karmada-io/dashboard/pkg/resource/event" | ||
) | ||
|
||
func handleGetCronJob(c *gin.Context) { | ||
namespace := common.ParseNamespacePathParameter(c) | ||
dataSelect := common.ParseDataSelectPathParameter(c) | ||
k8sClient := client.InClusterClientForKarmadaApiServer() | ||
result, err := cronjob.GetCronJobList(k8sClient, namespace, dataSelect) | ||
if err != nil { | ||
common.Fail(c, err) | ||
return | ||
} | ||
common.Success(c, result) | ||
} | ||
|
||
func handleGetCronJobDetail(c *gin.Context) { | ||
namespace := c.Param("namespace") | ||
name := c.Param("statefulset") | ||
k8sClient := client.InClusterClientForKarmadaApiServer() | ||
result, err := cronjob.GetCronJobDetail(k8sClient, namespace, name) | ||
if err != nil { | ||
common.Fail(c, err) | ||
return | ||
} | ||
common.Success(c, result) | ||
} | ||
|
||
func handleGetCronJobEvents(c *gin.Context) { | ||
namespace := c.Param("namespace") | ||
name := c.Param("statefulset") | ||
k8sClient := client.InClusterClientForKarmadaApiServer() | ||
dataSelect := common.ParseDataSelectPathParameter(c) | ||
result, err := event.GetResourceEvents(k8sClient, dataSelect, namespace, name) | ||
if err != nil { | ||
common.Fail(c, err) | ||
return | ||
} | ||
common.Success(c, result) | ||
} | ||
func init() { | ||
r := router.V1() | ||
r.GET("/cronjob", handleGetCronJob) | ||
r.GET("/cronjob/:namespace", handleGetCronJob) | ||
r.GET("/cronjob/:namespace/:statefulset", handleGetCronJobDetail) | ||
r.GET("/cronjob/:namespace/:statefulset/event", handleGetCronJobEvents) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package cronjob | ||
|
||
import ( | ||
"github.com/karmada-io/dashboard/pkg/dataselect" | ||
"github.com/karmada-io/dashboard/pkg/resource/common" | ||
batch "k8s.io/api/batch/v1" | ||
) | ||
|
||
// The code below allows to perform complex data section on []batch.CronJob | ||
|
||
type CronJobCell batch.CronJob | ||
|
||
func (self CronJobCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue { | ||
switch name { | ||
case dataselect.NameProperty: | ||
return dataselect.StdComparableString(self.ObjectMeta.Name) | ||
case dataselect.CreationTimestampProperty: | ||
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time) | ||
case dataselect.NamespaceProperty: | ||
return dataselect.StdComparableString(self.ObjectMeta.Namespace) | ||
default: | ||
// if name is not supported then just return a constant dummy value, sort will have no effect. | ||
return nil | ||
} | ||
} | ||
|
||
func ToCells(std []batch.CronJob) []dataselect.DataCell { | ||
cells := make([]dataselect.DataCell, len(std)) | ||
for i := range std { | ||
cells[i] = CronJobCell(std[i]) | ||
} | ||
return cells | ||
} | ||
|
||
func FromCells(cells []dataselect.DataCell) []batch.CronJob { | ||
std := make([]batch.CronJob, len(cells)) | ||
for i := range std { | ||
std[i] = batch.CronJob(cells[i].(CronJobCell)) | ||
} | ||
return std | ||
} | ||
|
||
func getStatus(list *batch.CronJobList) common.ResourceStatus { | ||
info := common.ResourceStatus{} | ||
if list == nil { | ||
return info | ||
} | ||
|
||
for _, cronJob := range list.Items { | ||
if cronJob.Spec.Suspend != nil && !(*cronJob.Spec.Suspend) { | ||
info.Running++ | ||
} else { | ||
info.Failed++ | ||
} | ||
} | ||
|
||
return info | ||
} | ||
|
||
func getContainerImages(cronJob *batch.CronJob) []string { | ||
podSpec := cronJob.Spec.JobTemplate.Spec.Template.Spec | ||
result := make([]string, 0) | ||
|
||
for _, container := range podSpec.Containers { | ||
result = append(result, container.Image) | ||
} | ||
|
||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package cronjob | ||
|
||
import ( | ||
"context" | ||
|
||
batch "k8s.io/api/batch/v1" | ||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
k8sClient "k8s.io/client-go/kubernetes" | ||
) | ||
|
||
// CronJobDetail contains Cron Job details. | ||
type CronJobDetail struct { | ||
// Extends list item structure. | ||
CronJob `json:",inline"` | ||
|
||
ConcurrencyPolicy string `json:"concurrencyPolicy"` | ||
StartingDeadLineSeconds *int64 `json:"startingDeadlineSeconds"` | ||
|
||
// List of non-critical errors, that occurred during resource retrieval. | ||
Errors []error `json:"errors"` | ||
} | ||
|
||
// GetCronJobDetail gets Cron Job details. | ||
func GetCronJobDetail(client k8sClient.Interface, namespace, name string) (*CronJobDetail, error) { | ||
|
||
rawObject, err := client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
cj := toCronJobDetail(rawObject) | ||
return &cj, nil | ||
} | ||
|
||
func toCronJobDetail(cj *batch.CronJob) CronJobDetail { | ||
return CronJobDetail{ | ||
CronJob: toCronJob(cj), | ||
ConcurrencyPolicy: string(cj.Spec.ConcurrencyPolicy), | ||
StartingDeadLineSeconds: cj.Spec.StartingDeadlineSeconds, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package cronjob | ||
|
||
import ( | ||
"github.com/karmada-io/dashboard/pkg/dataselect" | ||
"github.com/karmada-io/dashboard/pkg/resource/common" | ||
"github.com/karmada-io/dashboard/pkg/resource/event" | ||
client "k8s.io/client-go/kubernetes" | ||
) | ||
|
||
// GetCronJobEvents gets events associated to cron job. | ||
func GetCronJobEvents(client client.Interface, dsQuery *dataselect.DataSelectQuery, namespace, name string) ( | ||
*common.EventList, error) { | ||
|
||
raw, err := event.GetEvents(client, namespace, name) | ||
if err != nil { | ||
return event.EmptyEventList, err | ||
} | ||
|
||
events := event.CreateEventList(raw, dsQuery) | ||
return &events, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package cronjob | ||
|
||
import ( | ||
"context" | ||
"github.com/karmada-io/dashboard/pkg/common/errors" | ||
"github.com/karmada-io/dashboard/pkg/common/types" | ||
"github.com/karmada-io/dashboard/pkg/dataselect" | ||
"github.com/karmada-io/dashboard/pkg/resource/common" | ||
"github.com/karmada-io/dashboard/pkg/resource/job" | ||
|
||
batch "k8s.io/api/batch/v1" | ||
meta "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
apimachinery "k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apimachinery/pkg/util/rand" | ||
client "k8s.io/client-go/kubernetes" | ||
) | ||
|
||
const ( | ||
CronJobAPIVersion = "v1" | ||
CronJobKindName = "cronjob" | ||
) | ||
|
||
var emptyJobList = &job.JobList{ | ||
Jobs: make([]job.Job, 0), | ||
Errors: make([]error, 0), | ||
ListMeta: types.ListMeta{ | ||
TotalItems: 0, | ||
}, | ||
} | ||
|
||
// GetCronJobJobs returns list of jobs owned by cron job. | ||
func GetCronJobJobs(client client.Interface, | ||
dsQuery *dataselect.DataSelectQuery, namespace, name string, active bool) (*job.JobList, error) { | ||
|
||
cronJob, err := client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, meta.GetOptions{}) | ||
if err != nil { | ||
return emptyJobList, err | ||
} | ||
|
||
channels := &common.ResourceChannels{ | ||
JobList: common.GetJobListChannel(client, common.NewSameNamespaceQuery(namespace), 1), | ||
PodList: common.GetPodListChannel(client, common.NewSameNamespaceQuery(namespace), 1), | ||
EventList: common.GetEventListChannel(client, common.NewSameNamespaceQuery(namespace), 1), | ||
} | ||
|
||
jobs := <-channels.JobList.List | ||
err = <-channels.JobList.Error | ||
nonCriticalErrors, criticalError := errors.ExtractErrors(err) | ||
if criticalError != nil { | ||
return emptyJobList, nil | ||
} | ||
|
||
pods := <-channels.PodList.List | ||
err = <-channels.PodList.Error | ||
nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) | ||
if criticalError != nil { | ||
return emptyJobList, criticalError | ||
} | ||
|
||
events := <-channels.EventList.List | ||
err = <-channels.EventList.Error | ||
nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) | ||
if criticalError != nil { | ||
return emptyJobList, criticalError | ||
} | ||
|
||
jobs.Items = filterJobsByOwnerUID(cronJob.UID, jobs.Items) | ||
jobs.Items = filterJobsByState(active, jobs.Items) | ||
|
||
return job.ToJobList(jobs.Items, pods.Items, events.Items, nonCriticalErrors, dsQuery), nil | ||
} | ||
|
||
// TriggerCronJob manually triggers a cron job and creates a new job. | ||
func TriggerCronJob(client client.Interface, | ||
namespace, name string) error { | ||
|
||
cronJob, err := client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, meta.GetOptions{}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
annotations := make(map[string]string) | ||
annotations["cronjob.kubernetes.io/instantiate"] = "manual" | ||
|
||
labels := make(map[string]string) | ||
for k, v := range cronJob.Spec.JobTemplate.Labels { | ||
labels[k] = v | ||
} | ||
|
||
//job name cannot exceed DNS1053LabelMaxLength (52 characters) | ||
var newJobName string | ||
if len(cronJob.Name) < 42 { | ||
newJobName = cronJob.Name + "-manual-" + rand.String(3) | ||
} else { | ||
newJobName = cronJob.Name[0:41] + "-manual-" + rand.String(3) | ||
} | ||
|
||
jobToCreate := &batch.Job{ | ||
ObjectMeta: meta.ObjectMeta{ | ||
Name: newJobName, | ||
Namespace: namespace, | ||
Annotations: annotations, | ||
Labels: labels, | ||
OwnerReferences: []meta.OwnerReference{{ | ||
APIVersion: CronJobAPIVersion, | ||
Kind: CronJobKindName, | ||
Name: cronJob.Name, | ||
UID: cronJob.UID, | ||
}}, | ||
}, | ||
Spec: cronJob.Spec.JobTemplate.Spec, | ||
} | ||
|
||
_, err = client.BatchV1().Jobs(namespace).Create(context.TODO(), jobToCreate, meta.CreateOptions{}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func filterJobsByOwnerUID(UID apimachinery.UID, jobs []batch.Job) (matchingJobs []batch.Job) { | ||
for _, j := range jobs { | ||
for _, i := range j.OwnerReferences { | ||
if i.UID == UID { | ||
matchingJobs = append(matchingJobs, j) | ||
break | ||
} | ||
} | ||
} | ||
return | ||
} | ||
|
||
func filterJobsByState(active bool, jobs []batch.Job) (matchingJobs []batch.Job) { | ||
for _, j := range jobs { | ||
if active && j.Status.Active > 0 { | ||
matchingJobs = append(matchingJobs, j) | ||
} else if !active && j.Status.Active == 0 { | ||
matchingJobs = append(matchingJobs, j) | ||
} else { | ||
//sup | ||
} | ||
} | ||
return | ||
} |
Oops, something went wrong.