Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests for Controller Revision #297

Merged
merged 9 commits into from
Jan 3, 2025
23 changes: 18 additions & 5 deletions pkg/utils/revision/revision_utils.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
Copyright 2025.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package revision

import (
Expand Down Expand Up @@ -97,7 +113,8 @@ func GetRevision(ctx context.Context, k8sClient client.Client, lws *leaderworker
return nil, nil
}
selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: map[string]string{
leaderworkerset.RevisionKey: revisionKey,
leaderworkerset.SetNameLabelKey: lws.Name,
leaderworkerset.RevisionKey: revisionKey,
}})
if err != nil {
return nil, err
Expand Down Expand Up @@ -170,10 +187,6 @@ func EqualRevision(lhs *appsv1.ControllerRevision, rhs *appsv1.ControllerRevisio
return lhs == rhs
}

if GetRevisionKey(lhs) == GetRevisionKey(rhs) {
return true
}

return bytes.Equal(lhs.Data.Raw, rhs.Data.Raw) && apiequality.Semantic.DeepEqual(lhs.Data.Object, rhs.Data.Object)
}

Expand Down
226 changes: 223 additions & 3 deletions pkg/utils/revision/revision_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/ptr"
Expand All @@ -31,7 +32,7 @@ import (
func TestApplyRevision(t *testing.T) {
client := fake.NewClientBuilder().Build()

lws := BuildLeaderWorkerSet("default")
lws := BuildLeaderWorkerSet("default").Obj()
revision, err := NewRevision(context.TODO(), client, lws, "")
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -77,7 +78,151 @@ func TestApplyRevision(t *testing.T) {
}
}

func BuildLeaderWorkerSet(nsName string) *leaderworkerset.LeaderWorkerSet {
func TestEqualRevision(t *testing.T) {
client := fake.NewClientBuilder().Build()
tests := []struct {
name string
leftLws *leaderworkerset.LeaderWorkerSet
rightLws *leaderworkerset.LeaderWorkerSet
leftRevisionKey string
rightRevisionKey string
equal bool
}{
{
name: "same LeaderWorkerTemplate, networkConfig, should be equal",
leftLws: BuildLeaderWorkerSet("default").Obj(),
rightLws: BuildLeaderWorkerSet("default").Obj(),
leftRevisionKey: "",
rightRevisionKey: "",
equal: true,
},
{
name: "same LeaderWorkerTemplate, networkConfig, different revisionKey, should be equal",
leftLws: BuildLeaderWorkerSet("default").Obj(),
rightLws: BuildLeaderWorkerSet("default").Obj(),
leftRevisionKey: "",
rightRevisionKey: "templateHash",
equal: true,
},
{
name: "left nil, right nil, should be equal",
leftLws: nil,
rightLws: nil,
leftRevisionKey: "",
rightRevisionKey: "",
equal: true,
},
{
name: "semantically same LeaderWorkerTemplate, different fields set, same networkConfig, should be equal",
leftLws: BuildLeaderWorkerSet("default").WorkerTemplateSpec(MakeWorkerPodSpecWithVolumeAndNilImage()).Obj(),
rightLws: BuildLeaderWorkerSet("default").WorkerTemplateSpec(MakeWorkerPodSpecWithVolume()).Obj(),
leftRevisionKey: "",
rightRevisionKey: "",
equal: true,
},
{
name: "left nil, right non-nil, should not be equal",
leftLws: nil,
rightLws: BuildLeaderWorkerSet("default").Obj(),
leftRevisionKey: "",
rightRevisionKey: "",
equal: false,
},
{
name: "same LeaderWorkerTemplate, different networkConfig, should not be equal",
leftLws: BuildLeaderWorkerSet("default").SubdomainPolicy(leaderworkerset.SubdomainUniquePerReplica).Obj(),
Edwinhr716 marked this conversation as resolved.
Show resolved Hide resolved
rightLws: BuildLeaderWorkerSet("default").Obj(),
leftRevisionKey: "",
rightRevisionKey: "",
equal: false,
},
Edwinhr716 marked this conversation as resolved.
Show resolved Hide resolved
{
name: "different LeaderWorkerTemplate, same networkConfig, should not be equal",
leftLws: BuildLeaderWorkerSet("default").Obj(),
rightLws: BuildLeaderWorkerSet("default").WorkerTemplateSpec(MakeLeaderPodSpec()).Obj(),
leftRevisionKey: "",
rightRevisionKey: "",
equal: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var leftRevision *appsv1.ControllerRevision
var rightRevision *appsv1.ControllerRevision
var err error
if tc.leftLws != nil {
leftRevision, err = NewRevision(context.TODO(), client, tc.leftLws, tc.leftRevisionKey)
if err != nil {
t.Fatal(err)
}
}
if tc.rightLws != nil {
rightRevision, err = NewRevision(context.TODO(), client, tc.rightLws, tc.rightRevisionKey)
if err != nil {
t.Fatal(err)
}
}
equal := EqualRevision(leftRevision, rightRevision)
if tc.equal != equal {
t.Errorf("Expected equality between controller revisions to be %t, but was %t", tc.equal, equal)
}
})
}
}

func TestGetHighestRevision(t *testing.T) {
client := fake.NewClientBuilder().Build()
lws := BuildLeaderWorkerSet("default").Obj()
revision1, err := NewRevision(context.TODO(), client, lws, "")
if err != nil {
t.Fatal(err)
}
revision2 := revision1.DeepCopy()
revision2.Revision = 2
revision3 := revision2.DeepCopy()
revision3.Revision = 3
tests := []struct {
name string
revisions []*appsv1.ControllerRevision
expectedRevision *appsv1.ControllerRevision
}{
{
name: "empty revision list, returns nil",
revisions: []*appsv1.ControllerRevision{},
expectedRevision: nil,
},
{
name: "only one revision in list, returns it",
revisions: []*appsv1.ControllerRevision{revision1},
expectedRevision: revision1,
},
{
name: "returns the revision with highest revision number",
revisions: []*appsv1.ControllerRevision{revision2, revision3, revision2},
expectedRevision: revision3,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
revision := getHighestRevision(tc.revisions)
if tc.expectedRevision == nil {
if revision != nil {
t.Errorf("Expected revision to be nil")
}
} else {
if tc.expectedRevision.Revision != revision.Revision {
t.Errorf("Expected revision number to be %d, but it was %d", tc.expectedRevision.Revision, revision.Revision)
}
}
})
}
}

type LeaderWorkerSetWrapper struct {
leaderworkerset.LeaderWorkerSet
}

func BuildLeaderWorkerSet(nsName string) *LeaderWorkerSetWrapper {
lws := leaderworkerset.LeaderWorkerSet{}
lws.Name = "test-sample"
lws.Namespace = nsName
Expand All @@ -102,7 +247,30 @@ func BuildLeaderWorkerSet(nsName string) *leaderworkerset.LeaderWorkerSet {
SubdomainPolicy: &subdomainPolicy,
}

return &lws
return &LeaderWorkerSetWrapper{
lws,
}
}

func (lwsWrapper *LeaderWorkerSetWrapper) Obj() *leaderworkerset.LeaderWorkerSet {
return &lwsWrapper.LeaderWorkerSet
}

func (lwsWrapper *LeaderWorkerSetWrapper) SubdomainPolicy(subdomainPolicy leaderworkerset.SubdomainPolicy) *LeaderWorkerSetWrapper {
lwsWrapper.Spec.NetworkConfig = &leaderworkerset.NetworkConfig{
SubdomainPolicy: &subdomainPolicy,
}
return lwsWrapper
}

func (lwsWrapper *LeaderWorkerSetWrapper) MaxUnavailable(value int) *LeaderWorkerSetWrapper {
lwsWrapper.Spec.RolloutStrategy.RollingUpdateConfiguration.MaxUnavailable = intstr.FromInt(value)
return lwsWrapper
}

func (lwsWrapper *LeaderWorkerSetWrapper) MaxSurge(value int) *LeaderWorkerSetWrapper {
lwsWrapper.Spec.RolloutStrategy.RollingUpdateConfiguration.MaxSurge = intstr.FromInt(value)
return lwsWrapper
}

func MakeLeaderPodSpec() corev1.PodSpec {
Expand Down Expand Up @@ -132,3 +300,55 @@ func MakeWorkerPodSpec() corev1.PodSpec {
},
}
}

func MakeWorkerPodSpecWithVolume() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "leader",
Image: "nginx:1.14.2",
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
Protocol: "TCP",
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "dshm",
},
},
}
}

func MakeWorkerPodSpecWithVolumeAndNilImage() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "leader",
Image: "nginx:1.14.2",
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
Protocol: "TCP",
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "dshm",
VolumeSource: corev1.VolumeSource{
Image: nil,
},
},
},
}
}

func (lwsWrapper *LeaderWorkerSetWrapper) WorkerTemplateSpec(spec corev1.PodSpec) *LeaderWorkerSetWrapper {
lwsWrapper.Spec.LeaderWorkerTemplate.WorkerTemplate.Spec = spec
return lwsWrapper
}
Loading