Skip to content

Commit

Permalink
feat: add vcctl job list queueName filter
Browse files Browse the repository at this point in the history
Signed-off-by: googs1025 <[email protected]>
  • Loading branch information
googs1025 committed Jun 15, 2024
1 parent b97654a commit dc5526e
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 98 deletions.
45 changes: 37 additions & 8 deletions pkg/cli/job/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
type listFlags struct {
util.CommonFlags

QueueName string
Namespace string
SchedulerName string
allNamespace bool
Expand Down Expand Up @@ -83,6 +84,7 @@ var listJobFlags = &listFlags{}
func InitListFlags(cmd *cobra.Command) {
util.InitFlags(cmd, &listJobFlags.CommonFlags)

cmd.Flags().StringVarP(&listJobFlags.QueueName, "queue", "q", "", "list job with specified queue name")
cmd.Flags().StringVarP(&listJobFlags.Namespace, "namespace", "n", "default", "the namespace of job")
cmd.Flags().StringVarP(&listJobFlags.SchedulerName, "scheduler", "S", "", "list job with specified scheduler name")
cmd.Flags().BoolVarP(&listJobFlags.allNamespace, "all-namespaces", "", false, "list jobs in all namespaces")
Expand All @@ -104,11 +106,33 @@ func ListJobs(ctx context.Context) error {
return err
}

if len(jobs.Items) == 0 {
// define the filter callback function based on different flags
filterFunc := func(job v1alpha1.Job) bool {
// filter by QueueName if specified
if listJobFlags.QueueName != "" && listJobFlags.QueueName != job.Spec.Queue {
return false
}
// filter by SchedulerName if specified
if listJobFlags.SchedulerName != "" && listJobFlags.SchedulerName != job.Spec.SchedulerName {
return false
}
// filter by selector if specified
if listJobFlags.selector != "" && !strings.Contains(job.Name, listJobFlags.selector) {
return false
}
// filter by namespace if specified
if listJobFlags.Namespace != "" && listJobFlags.Namespace != job.Namespace {
return false
}
return true
}
filteredJobs := filterJobs(jobs, filterFunc)

if len(filteredJobs.Items) == 0 {
fmt.Printf("No resources found\n")
return nil
}
PrintJobs(jobs, os.Stdout)
PrintJobs(filteredJobs, os.Stdout)

return nil
}
Expand All @@ -133,12 +157,6 @@ func PrintJobs(jobs *v1alpha1.JobList, writer io.Writer) {
}

for _, job := range jobs.Items {
if listJobFlags.SchedulerName != "" && listJobFlags.SchedulerName != job.Spec.SchedulerName {
continue
}
if !strings.Contains(job.Name, listJobFlags.selector) {
continue
}
replicas := int32(0)
for _, ts := range job.Spec.Tasks {
replicas += ts.Replicas
Expand Down Expand Up @@ -177,3 +195,14 @@ func getMaxLen(jobs *v1alpha1.JobList) []int {

return []int{maxNameLen + 3, maxNamespaceLen + 3}
}

// filterJobs filters jobs based on the provided filter callback function.
func filterJobs(jobs *v1alpha1.JobList, filterFunc func(job v1alpha1.Job) bool) *v1alpha1.JobList {
filteredJobs := &v1alpha1.JobList{}
for _, job := range jobs.Items {
if filterFunc(job) {
filteredJobs.Items = append(filteredJobs.Items, job)
}
}
return filteredJobs
}
254 changes: 213 additions & 41 deletions pkg/cli/job/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,66 +18,236 @@ package job

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"volcano.sh/volcano/pkg/cli/util"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/spf13/cobra"

"volcano.sh/apis/pkg/apis/batch/v1alpha1"
"volcano.sh/volcano/pkg/cli/util"
)

func TestListJob(t *testing.T) {
response := v1alpha1.JobList{}
response.Items = append(response.Items, v1alpha1.Job{})

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
val, err := json.Marshal(response)
if err == nil {
w.Write(val)
}

})

server := httptest.NewServer(handler)
defer server.Close()
var (
namespaceFilter = "kube-system"
schedulerFilter = "volcano"
queueFilter = "test-queue"
selectorFilter = "test-job"
)

testCases := []struct {
Name string
ExpectValue error
AllNamespace bool
Selector string
Name string
Response interface{}
AllNamespace bool
Scheduler string
Selector string
QueueName string
Namespace string
ExpectedErr error
ExpectedOutput string
}{
{
Name: "ListJob",
ExpectValue: nil,
Name: "Normal Case",
Response: &v1alpha1.JobList{
Items: []v1alpha1.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: "default",
},
},
},
},
ExpectedErr: nil,
ExpectedOutput: `Name Creation Phase JobType Replicas Min Pending Running Succeeded Failed Unknown RetryCount
test-job 0001-01-01 Batch 0 0 0 0 0 0 0 0`,
},
{
Name: "ListAllNamespaceJob",
ExpectValue: nil,
Name: "Normal Case with queueName filter",
QueueName: queueFilter,
Response: &v1alpha1.JobList{
Items: []v1alpha1.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: "default",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-queue",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: queueFilter,
},
},
},
},
ExpectedErr: nil,
ExpectedOutput: `Name Creation Phase JobType Replicas Min Pending Running Succeeded Failed Unknown RetryCount
test-queue 0001-01-01 Batch 0 0 0 0 0 0 0 0`,
},
{
Name: "Normal Case with namespace filter",
Namespace: namespaceFilter,
Response: &v1alpha1.JobList{
Items: []v1alpha1.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Namespace: namespaceFilter,
},
Spec: v1alpha1.JobSpec{
Queue: "default",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-queue",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: "test-queue",
},
},
},
},
ExpectedErr: nil,
ExpectedOutput: `Name Creation Phase JobType Replicas Min Pending Running Succeeded Failed Unknown RetryCount
test-job 0001-01-01 Batch 0 0 0 0 0 0 0 0`,
},
{
Name: "Normal Case with all namespace filter",
AllNamespace: true,
Response: &v1alpha1.JobList{
Items: []v1alpha1.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Namespace: "kube-sysyem",
},
Spec: v1alpha1.JobSpec{
Queue: "default",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-queue",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: "test-queue",
},
},
},
},
ExpectedErr: nil,
ExpectedOutput: `Namespace Name Creation Phase JobType Replicas Min Pending Running Succeeded Failed Unknown RetryCount
kube-sysyem test-job 0001-01-01 Batch 0 0 0 0 0 0 0 0
default test-queue 0001-01-01 Batch 0 0 0 0 0 0 0 0`,
},
}

for i, testcase := range testCases {
listJobFlags = &listFlags{
CommonFlags: util.CommonFlags{
Master: server.URL,
{
Name: "Normal Case with scheduler filter",
Scheduler: schedulerFilter,
Response: &v1alpha1.JobList{
Items: []v1alpha1.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Namespace: "kube-sysyem",
},
Spec: v1alpha1.JobSpec{
Queue: "default",
SchedulerName: "test-scheduler",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-queue",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: "test-queue",
SchedulerName: schedulerFilter,
},
},
},
},
ExpectedErr: nil,
ExpectedOutput: `Name Creation Phase JobType Replicas Min Pending Running Succeeded Failed Unknown RetryCount
test-queue 0001-01-01 Batch 0 0 0 0 0 0 0 0`,
},
{
Name: "Normal Case with selector filter",
Selector: selectorFilter,
Response: &v1alpha1.JobList{
Items: []v1alpha1.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: selectorFilter,
Namespace: "kube-sysyem",
},
Spec: v1alpha1.JobSpec{
Queue: "default",
SchedulerName: "test-scheduler",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-queue",
Namespace: "default",
},
Spec: v1alpha1.JobSpec{
Queue: "test-queue",
SchedulerName: schedulerFilter,
},
},
},
},
Namespace: "test",
allNamespace: testcase.AllNamespace,
selector: testcase.Selector,
}

err := ListJobs(context.TODO())
if err != nil {
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, err)
}
ExpectedErr: nil,
ExpectedOutput: `Name Creation Phase JobType Replicas Min Pending Running Succeeded Failed Unknown RetryCount
test-job 0001-01-01 Batch 0 0 0 0 0 0 0 0`,
},
}

for _, testcase := range testCases {
t.Run(testcase.Name, func(t *testing.T) {

server := util.CreateTestServer(testcase.Response)
defer server.Close()

listJobFlags = &listFlags{
CommonFlags: util.CommonFlags{
Master: server.URL,
},
Namespace: testcase.Namespace,
allNamespace: testcase.AllNamespace,
selector: testcase.Selector,
SchedulerName: testcase.Scheduler,
QueueName: testcase.QueueName,
}
r, oldStdout := util.RedirectStdout()
defer r.Close()
err := ListJobs(context.TODO())
gotOutput := util.CaptureOutput(r, oldStdout)
if !reflect.DeepEqual(err, testcase.ExpectedErr) {
t.Fatalf("test case: %s failed: got: %v, want: %v", testcase.Name, err, testcase.ExpectedErr)
}
if gotOutput != testcase.ExpectedOutput {
t.Errorf("test case: %s failed: got: %s, want: %s", testcase.Name, gotOutput, testcase.ExpectedOutput)
}
})
}
}

func TestInitListFlags(t *testing.T) {
Expand All @@ -96,5 +266,7 @@ func TestInitListFlags(t *testing.T) {
if cmd.Flag("selector") == nil {
t.Errorf("Could not find the flag selector")
}

if cmd.Flag("queue") == nil {
t.Errorf("Could not find the flag queue")
}
}
Loading

0 comments on commit dc5526e

Please sign in to comment.