Skip to content

Commit

Permalink
Merge pull request #111 from warjiang/feature/job-api
Browse files Browse the repository at this point in the history
feat: add api for job resource
  • Loading branch information
karmada-bot authored Sep 9, 2024
2 parents 25c298d + 0f09825 commit a085f04
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/api/app/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/configmap"
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/daemonset"
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/deployment"
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/job"
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/namespace"
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/overview"
_ "github.com/karmada-io/dashboard/cmd/api/app/routes/propagationpolicy"
Expand Down
54 changes: 54 additions & 0 deletions cmd/api/app/routes/job/handler.go
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/event"
"github.com/karmada-io/dashboard/pkg/resource/job"
)

func handleGetJob(c *gin.Context) {
namespace := common.ParseNamespacePathParameter(c)
dataSelect := common.ParseDataSelectPathParameter(c)
k8sClient := client.InClusterClientForKarmadaApiServer()
result, err := job.GetJobList(k8sClient, namespace, dataSelect)
if err != nil {
common.Fail(c, err)
return
}
common.Success(c, result)
}

func handleGetJobDetail(c *gin.Context) {
namespace := c.Param("namespace")
name := c.Param("statefulset")
k8sClient := client.InClusterClientForKarmadaApiServer()
result, err := job.GetJobDetail(k8sClient, namespace, name)
if err != nil {
common.Fail(c, err)
return
}
common.Success(c, result)
}

func handleGetJobEvents(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("/job", handleGetJob)
r.GET("/job/:namespace", handleGetJob)
r.GET("/job/:namespace/:statefulset", handleGetJobDetail)
r.GET("/job/:namespace/:statefulset/event", handleGetJobEvents)
}
26 changes: 26 additions & 0 deletions pkg/resource/common/resourcechannels.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,32 @@ type JobListChannel struct {
Error chan error
}

// GetJobListChannel returns a pair of channels to a Job list and errors that both must be read numReads times.
func GetJobListChannel(client client.Interface,
nsQuery *NamespaceQuery, numReads int) JobListChannel {
channel := JobListChannel{
List: make(chan *batch.JobList, numReads),
Error: make(chan error, numReads),
}

go func() {
list, err := client.BatchV1().Jobs(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []batch.Job
for _, item := range list.Items {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
list.Items = filteredItems
for i := 0; i < numReads; i++ {
channel.List <- list
channel.Error <- err
}
}()

return channel
}

// CronJobListChannel is a list and error channels to Cron Jobs.
type CronJobListChannel struct {
List chan *batch.CronJobList
Expand Down
67 changes: 67 additions & 0 deletions pkg/resource/job/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package job

import (
"github.com/karmada-io/dashboard/pkg/dataselect"
"github.com/karmada-io/dashboard/pkg/resource/common"
batch "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
)

// The code below allows to perform complex data section on []batch.Job

type JobCell batch.Job

func (self JobCell) 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.Job) []dataselect.DataCell {
cells := make([]dataselect.DataCell, len(std))
for i := range std {
cells[i] = JobCell(std[i])
}
return cells
}

func FromCells(cells []dataselect.DataCell) []batch.Job {
std := make([]batch.Job, len(cells))
for i := range std {
std[i] = batch.Job(cells[i].(JobCell))
}
return std
}

func getStatus(list *batch.JobList, pods []v1.Pod) common.ResourceStatus {
info := common.ResourceStatus{}
if list == nil {
return info
}

for _, job := range list.Items {
matchingPods := common.FilterPodsForJob(job, pods)
podInfo := common.GetPodInfo(job.Status.Active, job.Spec.Completions, matchingPods)
jobStatus := getJobStatus(&job)

if jobStatus.Status == JobStatusFailed {
info.Failed++
} else if jobStatus.Status == JobStatusComplete {
info.Succeeded++
} else if podInfo.Running > 0 {
info.Running++
} else {
info.Pending++
}
}

return info
}
48 changes: 48 additions & 0 deletions pkg/resource/job/detail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package job

import (
"context"
"github.com/karmada-io/dashboard/pkg/common/errors"
"github.com/karmada-io/dashboard/pkg/resource/common"

batch "k8s.io/api/batch/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sClient "k8s.io/client-go/kubernetes"
)

// JobDetail is a presentation layer view of Kubernetes Job resource.
type JobDetail struct {
// Extends list item structure.
Job `json:",inline"`

// Completions specifies the desired number of successfully finished pods the job should be run with.
Completions *int32 `json:"completions"`

// List of non-critical errors, that occurred during resource retrieval.
Errors []error `json:"errors"`
}

// GetJobDetail gets job details.
func GetJobDetail(client k8sClient.Interface, namespace, name string) (*JobDetail, error) {
jobData, err := client.BatchV1().Jobs(namespace).Get(context.TODO(), name, metaV1.GetOptions{})
if err != nil {
return nil, err
}

podInfo, err := getJobPodInfo(client, jobData)
nonCriticalErrors, criticalError := errors.ExtractErrors(err)
if criticalError != nil {
return nil, criticalError
}

job := toJobDetail(jobData, *podInfo, nonCriticalErrors)
return &job, nil
}

func toJobDetail(job *batch.Job, podInfo common.PodInfo, nonCriticalErrors []error) JobDetail {
return JobDetail{
Job: toJob(job, &podInfo),
Completions: job.Spec.Completions,
Errors: nonCriticalErrors,
}
}
21 changes: 21 additions & 0 deletions pkg/resource/job/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package job

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"
)

// GetJobEvents gets events associated to job.
func GetJobEvents(client client.Interface, dsQuery *dataselect.DataSelectQuery, namespace, name string) (
*common.EventList, error) {

jobEvents, err := event.GetEvents(client, namespace, name)
if err != nil {
return event.EmptyEventList, err
}

events := event.CreateEventList(jobEvents, dsQuery)
return &events, nil
}
Loading

0 comments on commit a085f04

Please sign in to comment.