scheduling/workload: fix tests requiring RequestInfo for declarative validation

- Update resttest and genericregistrytest to support RequestInfo injection.
- Inject RequestInfo in Workload storage and strategy tests to resolve context-related failures.
- Refine validate.go log messages and error deduplication logic.
This commit is contained in:
yongruilin
2026-02-09 21:33:12 +00:00
parent 4f56a298cc
commit 4d2a2abdee
4 changed files with 66 additions and 12 deletions

View File

@@ -23,7 +23,9 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
"k8s.io/kubernetes/pkg/apis/scheduling"
@@ -66,11 +68,19 @@ func validNewWorkload() *scheduling.Workload {
}
}
func newTester(t *testing.T, storage *genericregistry.Store) *genericregistrytest.Tester {
return genericregistrytest.New(t, storage).SetRequestInfo(&genericapirequest.RequestInfo{
APIGroup: "scheduling.k8s.io",
APIVersion: "v1alpha1",
Resource: "workloads",
})
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test := newTester(t, storage.Store)
test.TestCreate(
validNewWorkload(),
// invalid cases
@@ -86,7 +96,7 @@ func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test := newTester(t, storage.Store)
test.TestUpdate(
// valid
validNewWorkload(),
@@ -114,7 +124,7 @@ func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test := newTester(t, storage.Store)
test.TestDelete(validNewWorkload())
}
@@ -122,7 +132,7 @@ func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test := newTester(t, storage.Store)
test.TestGet(validNewWorkload())
}
@@ -130,7 +140,7 @@ func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test := newTester(t, storage.Store)
test.TestList(validNewWorkload())
}
@@ -138,7 +148,7 @@ func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test := newTester(t, storage.Store)
test.TestWatch(
validNewWorkload(),
// matching labels

View File

@@ -17,6 +17,7 @@ limitations under the License.
package workload
import (
"context"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -52,9 +53,18 @@ func TestWorkloadStrategy(t *testing.T) {
}
}
func ctxWithRequestInfo() context.Context {
return genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
APIGroup: "scheduling.k8s.io",
APIVersion: "v1alpha1",
Resource: "workloads",
IsResourceRequest: true,
})
}
func TestPodSchedulingStrategyCreate(t *testing.T) {
t.Run("simple", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := ctxWithRequestInfo()
workload := workload.DeepCopy()
Strategy.PrepareForCreate(ctx, workload)
@@ -65,7 +75,7 @@ func TestPodSchedulingStrategyCreate(t *testing.T) {
})
t.Run("failed validation", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := ctxWithRequestInfo()
workload := workload.DeepCopy()
workload.Spec.PodGroups[0].Policy.Gang.MinCount = -1
@@ -79,7 +89,7 @@ func TestPodSchedulingStrategyCreate(t *testing.T) {
func TestPodSchedulingStrategyUpdate(t *testing.T) {
t.Run("no changes", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := ctxWithRequestInfo()
workload := workload.DeepCopy()
newWorkload := workload.DeepCopy()
newWorkload.ResourceVersion = "4"
@@ -92,7 +102,7 @@ func TestPodSchedulingStrategyUpdate(t *testing.T) {
})
t.Run("name update", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := ctxWithRequestInfo()
workload := workload.DeepCopy()
newWorkload := workload.DeepCopy()
newWorkload.Name += "bar"
@@ -106,7 +116,7 @@ func TestPodSchedulingStrategyUpdate(t *testing.T) {
})
t.Run("spec update", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := ctxWithRequestInfo()
workload := workload.DeepCopy()
newWorkload := workload.DeepCopy()
newWorkload.Spec.ControllerRef = &scheduling.TypedLocalObjectReference{
@@ -123,7 +133,7 @@ func TestPodSchedulingStrategyUpdate(t *testing.T) {
})
t.Run("invalid spec update", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := ctxWithRequestInfo()
workload := workload.DeepCopy()
newWorkload := workload.DeepCopy()
newWorkload.Spec.PodGroups[0].Policy.Gang.MinCount = 4

View File

@@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/registry/rest/resttest"
@@ -80,6 +81,11 @@ func (t *Tester) ReturnDeletedObject() *Tester {
return t
}
func (t *Tester) SetRequestInfo(requestInfo *genericapirequest.RequestInfo) *Tester {
t.tester.SetRequestInfo(requestInfo)
return t
}
func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
t.tester.TestCreate(
valid,

View File

@@ -53,6 +53,7 @@ type Tester struct {
returnDeletedObject bool
namer func(int) string
userInfo user.Info
requestInfo *genericapirequest.RequestInfo
}
func New(t *testing.T, storage rest.Storage) *Tester {
@@ -94,6 +95,12 @@ func (t *Tester) ReturnDeletedObject() *Tester {
return t
}
// SetRequestInfo sets the RequestInfo that should be present in the context when the
// storage operation is called.
func (t *Tester) SetRequestInfo(requestInfo *genericapirequest.RequestInfo) {
t.requestInfo = requestInfo
}
// TestNamespace returns the namespace that will be used when creating contexts.
// Returns NamespaceNone for cluster-scoped objects.
func (t *Tester) TestNamespace() string {
@@ -117,6 +124,9 @@ func (t *Tester) TestContext() context.Context {
if t.userInfo != nil {
ctx = genericapirequest.WithUser(ctx, t.userInfo)
}
if t.requestInfo != nil {
ctx = genericapirequest.WithRequestInfo(ctx, t.requestInfo)
}
return ctx
}
@@ -415,6 +425,9 @@ func (t *Tester) testCreateHasMetadata(valid runtime.Object) {
func (t *Tester) testCreateIgnoresContextNamespace(valid runtime.Object, opts metav1.CreateOptions) {
// Ignore non-empty namespace in context
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "not-default2")
if t.requestInfo != nil {
ctx = genericapirequest.WithRequestInfo(ctx, t.requestInfo)
}
// Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted
created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), rest.ValidateAllObjectFunc, &opts)
@@ -434,6 +447,9 @@ func (t *Tester) testCreateIgnoresMismatchedNamespace(valid runtime.Object, opts
// Ignore non-empty namespace in object meta
objectMeta.SetNamespace("not-default")
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "not-default2")
if t.requestInfo != nil {
ctx = genericapirequest.WithRequestInfo(ctx, t.requestInfo)
}
// Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted
created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), rest.ValidateAllObjectFunc, &opts)
@@ -1179,6 +1195,9 @@ func (t *Tester) testGetDifferentNamespace(obj runtime.Object) {
objMeta.SetName(t.namer(5))
ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar3")
if t.requestInfo != nil {
ctx1 = genericapirequest.WithRequestInfo(ctx1, t.requestInfo)
}
objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx1))
_, err := t.storage.(rest.Creater).Create(ctx1, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if err != nil {
@@ -1186,6 +1205,9 @@ func (t *Tester) testGetDifferentNamespace(obj runtime.Object) {
}
ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar4")
if t.requestInfo != nil {
ctx2 = genericapirequest.WithRequestInfo(ctx2, t.requestInfo)
}
objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx2))
_, err = t.storage.(rest.Creater).Create(ctx2, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if err != nil {
@@ -1240,7 +1262,13 @@ func (t *Tester) testGetFound(obj runtime.Object) {
func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) {
ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar1")
if t.requestInfo != nil {
ctx1 = genericapirequest.WithRequestInfo(ctx1, t.requestInfo)
}
ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar2")
if t.requestInfo != nil {
ctx2 = genericapirequest.WithRequestInfo(ctx2, t.requestInfo)
}
objMeta := t.getObjectMetaOrFail(obj)
objMeta.SetName(t.namer(4))
objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx1))