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

feat(common): add ut for common tasks and refactor task utils #6010

Merged
merged 4 commits into from
Dec 25, 2024
Merged
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
29 changes: 18 additions & 11 deletions pkg/client/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@ import "sigs.k8s.io/controller-runtime/pkg/client"

// Add alias of client.XXX to avoid import
// two client pkgs
type Object = client.Object
type ObjectList = client.ObjectList
type ObjectKey = client.ObjectKey

type Options = client.Options
type DeleteOption = client.DeleteOption

type MatchingLabels = client.MatchingLabels
type MatchingFields = client.MatchingFields
type InNamespace = client.InNamespace
type ListOptions = client.ListOptions
type (
Object = client.Object
ObjectList = client.ObjectList
ObjectKey = client.ObjectKey
)

type (
Options = client.Options
DeleteOption = client.DeleteOption
ListOption = client.ListOption
)

type (
MatchingLabels = client.MatchingLabels
MatchingFields = client.MatchingFields
InNamespace = client.InNamespace
ListOptions = client.ListOptions
)

type PropagationPolicy = client.PropagationPolicy

Expand Down
75 changes: 46 additions & 29 deletions pkg/client/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,34 @@ func (*fakeParser) Type(schema.GroupVersionKind) *typed.ParseableType {
return &typed.DeducedParseableType
}

func NewFakeClient(objs ...client.Object) Client {
c := newFakeUnderlayClient(objs...)
type FakeClient interface {
Client
WithError(verb, resource string, err error)
}

func NewFakeClient(objs ...client.Object) FakeClient {
fc := newFakeUnderlayClient(objs...)

return &applier{
WithWatch: c,
parser: &fakeParser{},
return &fakeClient{
Client: &applier{
WithWatch: fc,
parser: &fakeParser{},
},
fc: fc,
}
}

type fakeClient struct {
Client
fc *fakeUnderlayClient
}

func (fc *fakeClient) WithError(verb, resource string, err error) {
fc.fc.PrependReactor(verb, resource, func(_ testing.Action) (bool, runtime.Object, error) {
return true, nil, err
})
}

type fakeUnderlayClient struct {
testing.Fake
tracker testing.ObjectTracker
Expand All @@ -67,7 +86,7 @@ type fakeUnderlayClient struct {

var _ client.WithWatch = &fakeUnderlayClient{}

func newFakeUnderlayClient(objs ...client.Object) client.WithWatch {
func newFakeUnderlayClient(objs ...client.Object) *fakeUnderlayClient {
t := testing.NewObjectTracker(scheme.Scheme, scheme.Codecs.UniversalDecoder())
for _, obj := range objs {
if err := t.Add(obj); err != nil {
Expand Down Expand Up @@ -536,47 +555,45 @@ func (c *fakeUnderlayClient) PatchReactionFunc(action *testing.PatchActionImpl)

switch action.GetPatchType() {
case types.JSONPatchType:
patch, err2 := jsonpatch.DecodePatch(action.GetPatch())
if err2 != nil {
return true, nil, err2
patch, err := jsonpatch.DecodePatch(action.GetPatch())
if err != nil {
return true, nil, err
}
modified, err2 := patch.Apply(old)
if err2 != nil {
return true, nil, err2
modified, err := patch.Apply(old)
if err != nil {
return true, nil, err
}

//nolint:gocritic // use := shadow err
if err2 = json.Unmarshal(modified, obj); err2 != nil {
return true, nil, err2
if err := json.Unmarshal(modified, obj); err != nil {
return true, nil, err
}
case types.MergePatchType:
modified, err2 := jsonpatch.MergePatch(old, action.GetPatch())
if err2 != nil {
return true, nil, err2
modified, err := jsonpatch.MergePatch(old, action.GetPatch())
if err != nil {
return true, nil, err
}

//nolint:gocritic // use := shadow err
if err2 = json.Unmarshal(modified, obj); err2 != nil {
return true, nil, err2
if err := json.Unmarshal(modified, obj); err != nil {
return true, nil, err
}
case types.StrategicMergePatchType:
mergedByte, err2 := strategicpatch.StrategicMergePatch(old, action.GetPatch(), obj)
if err2 != nil {
return true, nil, err2
mergedByte, err := strategicpatch.StrategicMergePatch(old, action.GetPatch(), obj)
if err != nil {
return true, nil, err
}
//nolint:gocritic // use := shadow err
if err2 = json.Unmarshal(mergedByte, obj); err2 != nil {
return true, nil, err2
if err := json.Unmarshal(mergedByte, obj); err != nil {
return true, nil, err
}
case types.ApplyPatchType:
patchObj := &unstructured.Unstructured{Object: map[string]any{}}
if err = yaml.Unmarshal(action.GetPatch(), &patchObj.Object); err != nil {
if err := yaml.Unmarshal(action.GetPatch(), &patchObj.Object); err != nil {
return true, nil, fmt.Errorf("error decoding YAML: %w", err)
}
obj, err = manager.Apply(obj, patchObj, "tidb-operator", true)
ret, err := manager.Apply(obj, patchObj, "tidb-operator", true)
if err != nil {
return true, nil, err
}
obj = ret

default:
return true, nil, fmt.Errorf("PatchType is not supported")
Expand Down
8 changes: 4 additions & 4 deletions pkg/controllers/cluster/tasks/finalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ func TestFinalizer(t *testing.T) {
},
{
desc: "removed finalizer",
cluster: fake.FakeObj[v1alpha1.Cluster]("test",
fake.SetDeleteTimestamp[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
cluster: fake.FakeObj("test",
fake.DeleteNow[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
expected: task.Complete().Break().With("removed finalizer"),
},
{
desc: "deleting components",
cluster: fake.FakeObj[v1alpha1.Cluster]("test",
fake.SetDeleteTimestamp[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
cluster: fake.FakeObj("test",
fake.DeleteNow[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
pdGroup: fake.FakeObj[v1alpha1.PDGroup]("pd-group"),
expected: task.Fail().With("deleting components"),
hasFinalizer: true,
Expand Down
16 changes: 8 additions & 8 deletions pkg/controllers/common/cond.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ package common

import "github.com/pingcap/tidb-operator/pkg/utils/task/v3"

func CondPDHasBeenDeleted(ctx PDGetter) task.Condition {
func CondPDHasBeenDeleted(ctx PDState) task.Condition {
return task.CondFunc(func() bool {
return ctx.GetPD() == nil
return ctx.PD() == nil
})
}

func CondPDIsDeleting(ctx PDGetter) task.Condition {
func CondPDIsDeleting(ctx PDState) task.Condition {
return task.CondFunc(func() bool {
return !ctx.GetPD().GetDeletionTimestamp().IsZero()
return !ctx.PD().GetDeletionTimestamp().IsZero()
})
}

func CondClusterIsSuspending(ctx ClusterGetter) task.Condition {
func CondClusterIsSuspending(ctx ClusterState) task.Condition {
return task.CondFunc(func() bool {
return ctx.GetCluster().ShouldSuspendCompute()
return ctx.Cluster().ShouldSuspendCompute()
})
}

func CondClusterIsPaused(ctx ClusterGetter) task.Condition {
func CondClusterIsPaused(ctx ClusterState) task.Condition {
return task.CondFunc(func() bool {
return ctx.GetCluster().ShouldPauseReconcile()
return ctx.Cluster().ShouldPauseReconcile()
})
}
162 changes: 162 additions & 0 deletions pkg/controllers/common/cond_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright 2024 PingCAP, Inc.
//
// 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 common

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/pingcap/tidb-operator/apis/core/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/utils/fake"
)

func TestCondPDHasBeenDeleted(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.PD]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.PD]{
obj: fake.FakeObj[v1alpha1.PD]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.PD]{},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakePDState{s: c.state}
cond := CondPDHasBeenDeleted(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}

func TestCondPDIsDeleting(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.PD]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.PD]{
obj: fake.FakeObj[v1alpha1.PD]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.PD]{
obj: fake.FakeObj("test", fake.DeleteNow[v1alpha1.PD]()),
},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakePDState{s: c.state}
cond := CondPDIsDeleting(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}

func TestCondClusterIsSuspending(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.Cluster]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj[v1alpha1.Cluster]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj("test", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Spec.SuspendAction = &v1alpha1.SuspendAction{
SuspendCompute: true,
}
return obj
}),
},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakeClusterState{s: c.state}
cond := CondClusterIsSuspending(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}

func TestCondClusterIsPaused(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.Cluster]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj[v1alpha1.Cluster]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj("test", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Spec.Paused = true
return obj
}),
},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakeClusterState{s: c.state}
cond := CondClusterIsPaused(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}
Loading
Loading