Skip to content

Commit

Permalink
chore(pd): add uts for pd (#6026)
Browse files Browse the repository at this point in the history
Signed-off-by: liubo02 <[email protected]>
  • Loading branch information
liubog2008 authored Jan 10, 2025
1 parent dadd7c5 commit 286680b
Show file tree
Hide file tree
Showing 29 changed files with 2,778 additions and 71 deletions.
1 change: 0 additions & 1 deletion .github/licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ header:
- '**/go.work.sum'
- '**/LICENSE'
- third_party/**
- pkg/**/*mock.go
- '**/OWNERS'
- OWNERS_ALIASES
comment: on-failure
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ PD_API_PATH = $(ROOT)/pkg/timanager/apis/pd
GO_MODULE := github.com/pingcap/tidb-operator
OVERLAY_PKG_DIR = $(ROOT)/pkg/overlay
BOILERPLATE_FILE = $(ROOT)/hack/boilerplate/boilerplate.go.txt
MOCK_BOILERPLATE_FILE = $(ROOT)/hack/boilerplate/boilerplate.txt

KIND_VERSION ?= v0.24.0

Expand Down Expand Up @@ -93,7 +94,7 @@ tidy:

gengo: GEN_DIR ?= ./...
gengo: bin/mockgen
GOBIN=$(BIN_DIR) GO_MODULE=$(GO_MODULE) go generate $(GEN_DIR)
BOILERPLATE_FILE=${MOCK_BOILERPLATE_FILE} GOBIN=$(BIN_DIR) GO_MODULE=$(GO_MODULE) go generate $(GEN_DIR)

.PHONY: license
license: bin/license-eye
Expand Down
13 changes: 13 additions & 0 deletions hack/boilerplate/boilerplate.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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.
6 changes: 3 additions & 3 deletions pkg/controllers/pd/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ func (r *Reconciler) NewRunner(state *tasks.ReconcileContext, reporter task.Task
),

common.TaskContextPDSlice(state, r.Client),
tasks.TaskConfigMap(state, r.Logger, r.Client),
tasks.TaskConfigMap(state, r.Client),
tasks.TaskPVC(state, r.Logger, r.Client, r.VolumeModifier),
tasks.TaskPod(state, r.Logger, r.Client),
tasks.TaskPod(state, r.Client),
// If pd client has not been registered yet, do not update status of the pd
task.IfBreak(tasks.CondPDClientIsNotRegisterred(state),
tasks.TaskStatusUnknown(),
),
tasks.TaskStatus(state, r.Logger, r.Client),
tasks.TaskStatus(state, r.Client),
)

return runner
Expand Down
5 changes: 3 additions & 2 deletions pkg/controllers/pd/tasks/cm.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package tasks
import (
"context"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand All @@ -30,7 +29,7 @@ import (
"github.com/pingcap/tidb-operator/pkg/utils/toml"
)

func TaskConfigMap(state *ReconcileContext, _ logr.Logger, c client.Client) task.Task {
func TaskConfigMap(state *ReconcileContext, c client.Client) task.Task {
return task.NameTaskFunc("ConfigMap", func(ctx context.Context) task.Result {
// TODO: DON'T add bootstrap config back
// We need to check current config and forbid adding bootstrap cfg back
Expand All @@ -40,6 +39,7 @@ func TaskConfigMap(state *ReconcileContext, _ logr.Logger, c client.Client) task
if err := decoder.Decode([]byte(state.PD().Spec.Config), &cfg); err != nil {
return task.Fail().With("pd config cannot be decoded: %v", err)
}

if err := cfg.Overlay(state.Cluster(), state.PD(), state.PDSlice()); err != nil {
return task.Fail().With("cannot generate pd config: %v", err)
}
Expand All @@ -49,6 +49,7 @@ func TaskConfigMap(state *ReconcileContext, _ logr.Logger, c client.Client) task
return task.Fail().With("pd config cannot be encoded: %v", err)
}

// TODO(liubo02): avoid decode toml twice
hash, err := hasher.GenerateHash(state.PD().Spec.Config)
if err != nil {
return task.Fail().With("failed to generate hash for `pd.spec.config`: %v", err)
Expand Down
182 changes: 182 additions & 0 deletions pkg/controllers/pd/tasks/cm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// 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 tasks

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

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

const fakePDAddr = "any string, useless in test"

func TestTaskConfigMap(t *testing.T) {
cases := []struct {
desc string
state *ReconcileContext
objs []client.Object
unexpectedErr bool
invalidConfig bool

expectedStatus task.Status
}{
{
desc: "no config",
state: &ReconcileContext{
State: &state{
pd: fake.FakeObj[v1alpha1.PD]("aaa-xxx"),
cluster: fake.FakeObj("cluster", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Status.PD = fakePDAddr
return obj
}),
},
},
expectedStatus: task.SComplete,
},
{
desc: "invalid config",
state: &ReconcileContext{
State: &state{
pd: fake.FakeObj("aaa-xxx", func(obj *v1alpha1.PD) *v1alpha1.PD {
obj.Spec.Config = `invalid`
return obj
}),
cluster: fake.FakeObj("cluster", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Status.PD = fakePDAddr
return obj
}),
},
},
invalidConfig: true,
expectedStatus: task.SFail,
},
{
desc: "with managed field",
state: &ReconcileContext{
State: &state{
pd: fake.FakeObj("aaa-xxx", func(obj *v1alpha1.PD) *v1alpha1.PD {
obj.Spec.Config = `name = 'xxx'`
return obj
}),
cluster: fake.FakeObj("cluster", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Status.PD = fakePDAddr
return obj
}),
},
},
invalidConfig: true,
expectedStatus: task.SFail,
},
{
desc: "has config map",
state: &ReconcileContext{
State: &state{
pd: fake.FakeObj("aaa-xxx", func(obj *v1alpha1.PD) *v1alpha1.PD {
return obj
}),
cluster: fake.FakeObj("cluster", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Status.PD = fakePDAddr
return obj
}),
},
},
objs: []client.Object{
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "aaa-pd-xxx",
},
},
},
expectedStatus: task.SComplete,
},
{
desc: "update config map failed",
state: &ReconcileContext{
State: &state{
pd: fake.FakeObj("aaa-xxx", func(obj *v1alpha1.PD) *v1alpha1.PD {
return obj
}),
cluster: fake.FakeObj("cluster", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Status.PD = fakePDAddr
return obj
}),
},
},
objs: []client.Object{
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "aaa-pd-xxx",
},
},
},
unexpectedErr: true,

expectedStatus: task.SFail,
},
}

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

ctx := context.Background()
var objs []client.Object
objs = append(objs, c.state.PD(), c.state.Cluster())
for _, pd := range c.state.PDSlice() {
objs = append(objs, pd)
}
fc := client.NewFakeClient(objs...)
for _, obj := range c.objs {
require.NoError(tt, fc.Apply(ctx, obj), c.desc)
}

if c.unexpectedErr {
// cannot update svc
fc.WithError("patch", "*", errors.NewInternalError(fmt.Errorf("fake internal err")))
}

res, done := task.RunTask(ctx, TaskConfigMap(c.state, fc))
assert.Equal(tt, c.expectedStatus.String(), res.Status().String(), res.Message())
assert.False(tt, done, c.desc)

if !c.invalidConfig {
// config hash should be set
assert.NotEmpty(tt, c.state.ConfigHash, c.desc)
}

if c.expectedStatus == task.SComplete {
cm := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "aaa-pd-xxx",
},
}
require.NoError(tt, fc.Get(ctx, client.ObjectKeyFromObject(&cm), &cm), c.desc)
assert.Equal(tt, c.state.ConfigHash, cm.Labels[v1alpha1.LabelKeyConfigHash], c.desc)
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/controllers/pd/tasks/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TaskFinalizerDel(state *ReconcileContext, c client.Client) task.Task {
// get member info successfully and the member still exists
case state.IsAvailable && state.MemberID != "":
// TODO: check whether quorum will be lost?
if err := state.PDClient.Underlay().DeleteMember(ctx, state.PD().Name); err != nil {
if err := state.PDClient.Underlay().DeleteMember(ctx, state.MemberID); err != nil {
return task.Fail().With("cannot delete member: %v", err)
}

Expand Down
Loading

0 comments on commit 286680b

Please sign in to comment.