Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2024 The Fluid Authors.
Copyright 2026 The Fluid Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -17,35 +17,121 @@
package dataflowaffinity

import (
datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
"github.com/fluid-cloudnative/fluid/pkg/common"
"github.com/fluid-cloudnative/fluid/pkg/utils/fake"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"reflect"
"testing"

datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
"github.com/fluid-cloudnative/fluid/pkg/common"
"github.com/fluid-cloudnative/fluid/pkg/utils/fake"
)

func TestDataOpJobReconciler_injectPodNodeLabelsToJob(t *testing.T) {
type args struct {
job *batchv1.Job
pods *v1.Pod
node *v1.Node
}
tests := []struct {
name string
args args
wantAnnotations map[string]string
wantErr bool
}{
{
name: "job with succeed pods",
args: args{
job: &batchv1.Job{
func newTestScheme() *runtime.Scheme {
testScheme := runtime.NewScheme()
_ = v1.AddToScheme(testScheme)
_ = batchv1.AddToScheme(testScheme)
_ = datav1alpha1.AddToScheme(testScheme)
return testScheme
}

var _ = Describe("DataOpJobReconciler", func() {
Describe("ControllerName", func() {
It("should return DataOpJobController", func() {
reconciler := &DataOpJobReconciler{}
Expect(reconciler.ControllerName()).To(Equal("DataOpJobController"))
})
})

Describe("ManagedResource", func() {
It("should return a Job object", func() {
reconciler := &DataOpJobReconciler{}
obj := reconciler.ManagedResource()
Expect(obj).NotTo(BeNil())
_, ok := obj.(*batchv1.Job)
Expect(ok).To(BeTrue())
})
})

Describe("NewDataOpJobReconciler", func() {
It("should create a new reconciler", func() {
reconciler := NewDataOpJobReconciler(nil, fake.NullLogger(), nil)
Expect(reconciler).NotTo(BeNil())
Expect(reconciler.Log).NotTo(BeNil())
})
})

Describe("fillCustomizedNodeAffinity", func() {
It("should fill annotations with node labels", func() {
annotationsToInject := map[string]string{}
nodeLabels := map[string]string{
common.K8sNodeNameLabelKey: "node01",
common.K8sRegionLabelKey: "region01",
common.K8sZoneLabelKey: "zone01",
"custom-label": "custom-value",

Check failure on line 73 in pkg/controllers/v1alpha1/fluidapp/dataflowaffinity/dataflowaffinity_controller_test.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "custom-label" 3 times.

See more on https://sonarcloud.io/project/issues?id=fluid-cloudnative_fluid&issues=AZzNut6obUJJ94dz8NyB&open=AZzNut6obUJJ94dz8NyB&pullRequest=5684
}
exposedLabelNames := []string{
common.K8sNodeNameLabelKey,
common.K8sRegionLabelKey,
common.K8sZoneLabelKey,
"custom-label",
}

fillCustomizedNodeAffinity(annotationsToInject, nodeLabels, exposedLabelNames)

Expect(annotationsToInject).To(HaveLen(4))
Expect(annotationsToInject[common.AnnotationDataFlowCustomizedAffinityPrefix+common.K8sNodeNameLabelKey]).To(Equal("node01"))
Expect(annotationsToInject[common.AnnotationDataFlowCustomizedAffinityPrefix+common.K8sRegionLabelKey]).To(Equal("region01"))
Expect(annotationsToInject[common.AnnotationDataFlowCustomizedAffinityPrefix+common.K8sZoneLabelKey]).To(Equal("zone01"))
Expect(annotationsToInject[common.AnnotationDataFlowCustomizedAffinityPrefix+"custom-label"]).To(Equal("custom-value"))
})

It("should skip non-existent labels", func() {
annotationsToInject := map[string]string{}
nodeLabels := map[string]string{
common.K8sNodeNameLabelKey: "node01",
}
exposedLabelNames := []string{
common.K8sNodeNameLabelKey,
"non-existent-label",
}

fillCustomizedNodeAffinity(annotationsToInject, nodeLabels, exposedLabelNames)

Expect(annotationsToInject).To(HaveLen(1))
Expect(annotationsToInject[common.AnnotationDataFlowCustomizedAffinityPrefix+common.K8sNodeNameLabelKey]).To(Equal("node01"))
})

It("should handle labels with whitespace", func() {
annotationsToInject := map[string]string{}
nodeLabels := map[string]string{
common.K8sNodeNameLabelKey: "node01",
}
exposedLabelNames := []string{
" " + common.K8sNodeNameLabelKey + " ",
}

fillCustomizedNodeAffinity(annotationsToInject, nodeLabels, exposedLabelNames)

Expect(annotationsToInject).To(HaveLen(1))
Expect(annotationsToInject[common.AnnotationDataFlowCustomizedAffinityPrefix+common.K8sNodeNameLabelKey]).To(Equal("node01"))
})
})

Describe("injectPodNodeLabelsToJob", func() {
var testScheme *runtime.Scheme

BeforeEach(func() {
testScheme = newTestScheme()
})

Context("when job has succeeded pods", func() {
It("should inject pod node labels to job annotations", func() {
job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",

Check failure on line 134 in pkg/controllers/v1alpha1/fluidapp/dataflowaffinity/dataflowaffinity_controller_test.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "test-job" 3 times.

See more on https://sonarcloud.io/project/issues?id=fluid-cloudnative_fluid&issues=AZzNut6obUJJ94dz8NyA&open=AZzNut6obUJJ94dz8NyA&pullRequest=5684
Labels: map[string]string{
common.LabelAnnotationManagedBy: common.Fluid,
},
Expand All @@ -57,10 +143,10 @@
},
},
},
},
pods: &v1.Pod{
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",

Check failure on line 149 in pkg/controllers/v1alpha1/fluidapp/dataflowaffinity/dataflowaffinity_controller_test.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "test-pod" 4 times.

See more on https://sonarcloud.io/project/issues?id=fluid-cloudnative_fluid&issues=AZzNut6obUJJ94dz8NyC&open=AZzNut6obUJJ94dz8NyC&pullRequest=5684
Labels: map[string]string{
"controller-uid": "455afc34-93b1-4e75-a6fa-8e13d2c6ca06",
},
Expand Down Expand Up @@ -91,8 +177,8 @@
Status: v1.PodStatus{
Phase: v1.PodSucceeded,
},
},
node: &v1.Node{
}
node := &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node01",
Labels: map[string]string{
Expand All @@ -102,29 +188,39 @@
"k8s.gpu": "true",
},
},
},
},
wantAnnotations: map[string]string{
common.AnnotationDataFlowCustomizedAffinityPrefix + common.K8sNodeNameLabelKey: "node01",
common.AnnotationDataFlowCustomizedAffinityPrefix + common.K8sRegionLabelKey: "region01",
common.AnnotationDataFlowCustomizedAffinityPrefix + common.K8sZoneLabelKey: "zone01",
common.AnnotationDataFlowCustomizedAffinityPrefix + "k8s.gpu": "true",
},
wantErr: false,
},
{
name: "job with failed pods",
args: args{
job: &batchv1.Job{
}

c := fake.NewFakeClientWithScheme(testScheme, job, pod, node)
reconciler := &DataOpJobReconciler{
Client: c,
Log: fake.NullLogger(),
}

err := reconciler.injectPodNodeLabelsToJob(job)
Expect(err).NotTo(HaveOccurred())

expectedAnnotations := map[string]string{
common.AnnotationDataFlowCustomizedAffinityPrefix + common.K8sNodeNameLabelKey: "node01",
common.AnnotationDataFlowCustomizedAffinityPrefix + common.K8sRegionLabelKey: "region01",
common.AnnotationDataFlowCustomizedAffinityPrefix + common.K8sZoneLabelKey: "zone01",
common.AnnotationDataFlowCustomizedAffinityPrefix + "k8s.gpu": "true",
}
Expect(job.Annotations).To(Equal(expectedAnnotations))
})
})

Context("when job has failed pods", func() {
It("should return an error", func() {
job := &batchv1.Job{
Spec: batchv1.JobSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"controller-uid": "455afc34-93b1-4e75-a6fa-8e13d2c6ca06",
},
},
},
},
pods: &v1.Pod{
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{
Expand All @@ -134,8 +230,8 @@
Status: v1.PodStatus{
Phase: v1.PodFailed,
},
},
node: &v1.Node{
}
node := &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node01",
Labels: map[string]string{
Expand All @@ -145,30 +241,114 @@
"k8s.gpu": "true",
},
},
},
},
wantErr: true,
},
}
testScheme := runtime.NewScheme()
_ = v1.AddToScheme(testScheme)
_ = batchv1.AddToScheme(testScheme)
_ = datav1alpha1.AddToScheme(testScheme)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var c = fake.NewFakeClientWithScheme(testScheme, tt.args.job, tt.args.pods, tt.args.node)
}

f := &DataOpJobReconciler{
Client: c,
Log: fake.NullLogger(),
}
err := f.injectPodNodeLabelsToJob(tt.args.job)
if (err != nil) != tt.wantErr {
t.Errorf("injectPodNodeLabelsToJob() error = %v, wantErr %v", err, tt.wantErr)
}
if err == nil && !reflect.DeepEqual(tt.args.job.Annotations, tt.wantAnnotations) {
t.Errorf("injectPodNodeLabelsToJob() got = %v, want %v", tt.args.job.Labels, tt.wantAnnotations)
}
c := fake.NewFakeClientWithScheme(testScheme, job, pod, node)
reconciler := &DataOpJobReconciler{
Client: c,
Log: fake.NullLogger(),
}

err := reconciler.injectPodNodeLabelsToJob(job)
Expect(err).To(HaveOccurred())
})
})
}
}

Context("when pod has no node name", func() {
It("should return an error", func() {
job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Labels: map[string]string{
common.LabelAnnotationManagedBy: common.Fluid,
},
},
Spec: batchv1.JobSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"controller-uid": "test-uid",

Check failure on line 269 in pkg/controllers/v1alpha1/fluidapp/dataflowaffinity/dataflowaffinity_controller_test.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "test-uid" 4 times.

See more on https://sonarcloud.io/project/issues?id=fluid-cloudnative_fluid&issues=AZzNut6obUJJ94dz8Nx_&open=AZzNut6obUJJ94dz8Nx_&pullRequest=5684
},
},
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{
"controller-uid": "test-uid",
},
},
Spec: v1.PodSpec{
NodeName: "",
},
Status: v1.PodStatus{
Phase: v1.PodSucceeded,
},
}

c := fake.NewFakeClientWithScheme(testScheme, job, pod)
reconciler := &DataOpJobReconciler{
Client: c,
Log: fake.NullLogger(),
}

err := reconciler.injectPodNodeLabelsToJob(job)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("no node name"))
})
})

Context("when job has nil annotations", func() {
It("should create annotations map and inject labels", func() {
job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "test-job",
Labels: map[string]string{
common.LabelAnnotationManagedBy: common.Fluid,
},
Annotations: nil,
},
Spec: batchv1.JobSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"controller-uid": "test-uid",
},
},
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{
"controller-uid": "test-uid",
},
},
Spec: v1.PodSpec{
NodeName: "node01",
},
Status: v1.PodStatus{
Phase: v1.PodSucceeded,
},
}
node := &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node01",
Labels: map[string]string{
common.K8sNodeNameLabelKey: "node01",
},
},
}

c := fake.NewFakeClientWithScheme(testScheme, job, pod, node)
reconciler := &DataOpJobReconciler{
Client: c,
Log: fake.NullLogger(),
}

err := reconciler.injectPodNodeLabelsToJob(job)
Expect(err).NotTo(HaveOccurred())
Expect(job.Annotations).NotTo(BeNil())
})
})
})
})
Loading
Loading