mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-20 18:31:15 +00:00
152 lines
5.3 KiB
Go
152 lines
5.3 KiB
Go
/*
|
|
Copyright 2022 The Kubernetes Authors.
|
|
|
|
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 flowcontrol
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
flowcontrol "k8s.io/api/flowcontrol/v1beta3"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
machinerytypes "k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
|
fcboot "k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap"
|
|
genericfeatures "k8s.io/apiserver/pkg/features"
|
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
flowcontrolapply "k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3"
|
|
clientset "k8s.io/client-go/kubernetes"
|
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
"k8s.io/klog/v2"
|
|
)
|
|
|
|
func TestConditionIsolation(t *testing.T) {
|
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.APIPriorityAndFairness, true)()
|
|
// NOTE: disabling the feature should fail the test
|
|
kubeConfig, closeFn := setup(t, 10, 10)
|
|
defer closeFn()
|
|
|
|
loopbackClient := clientset.NewForConfigOrDie(kubeConfig)
|
|
|
|
stopCh := make(chan struct{})
|
|
defer close(stopCh)
|
|
ctx := context.Background()
|
|
|
|
fsOrig := fcboot.SuggestedFlowSchemas[0]
|
|
t.Logf("Testing Status Condition isolation in FlowSchema %q", fsOrig.Name)
|
|
fsClient := loopbackClient.FlowcontrolV1beta3().FlowSchemas()
|
|
var dangleOrig *flowcontrol.FlowSchemaCondition
|
|
|
|
wait.PollUntil(time.Second, func() (bool, error) {
|
|
fsGot, err := fsClient.Get(ctx, fsOrig.Name, metav1.GetOptions{})
|
|
if err != nil {
|
|
klog.Errorf("Failed to fetch FlowSchema %q: %v", fsOrig.Name, err)
|
|
return false, nil
|
|
}
|
|
dangleOrig = getCondition(fsGot.Status.Conditions, flowcontrol.FlowSchemaConditionDangling)
|
|
return dangleOrig != nil, nil
|
|
}, stopCh)
|
|
|
|
ssaType := flowcontrol.FlowSchemaConditionType("test-ssa")
|
|
patchSSA := flowcontrolapply.FlowSchema(fsOrig.Name).
|
|
WithStatus(flowcontrolapply.FlowSchemaStatus().
|
|
WithConditions(flowcontrolapply.FlowSchemaCondition().
|
|
WithType(ssaType).
|
|
WithStatus(flowcontrol.ConditionTrue).
|
|
WithReason("SSA test").
|
|
WithMessage("for testing").
|
|
WithLastTransitionTime(metav1.Now()),
|
|
))
|
|
|
|
postSSA, err := fsClient.ApplyStatus(ctx, patchSSA, metav1.ApplyOptions{FieldManager: "ssa-test"})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
danglePostSSA := getCondition(postSSA.Status.Conditions, flowcontrol.FlowSchemaConditionDangling)
|
|
if danglePostSSA == nil || danglePostSSA.Status != dangleOrig.Status {
|
|
t.Errorf("Bad dangle condition after SSA, the FS is now %s", fmtFS(t, postSSA))
|
|
}
|
|
ssaPostSSA := getCondition(postSSA.Status.Conditions, ssaType)
|
|
if ssaPostSSA == nil || ssaPostSSA.Status != flowcontrol.ConditionTrue {
|
|
t.Errorf("Bad SSA condition after SSA, the FS is now %s", fmtFS(t, postSSA))
|
|
}
|
|
|
|
smpType := flowcontrol.FlowSchemaConditionType("test-smp")
|
|
smpBytes, err := makeFlowSchemaConditionPatch(flowcontrol.FlowSchemaCondition{
|
|
Type: smpType,
|
|
Status: flowcontrol.ConditionFalse,
|
|
Reason: "SMP test",
|
|
Message: "for testing too",
|
|
LastTransitionTime: metav1.Now(),
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
postSMP, err := fsClient.Patch(ctx, fsOrig.Name, machinerytypes.StrategicMergePatchType, smpBytes,
|
|
metav1.PatchOptions{FieldManager: "smp-test"}, "status")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if false /* disabled until patch annotations go into the API (see https://github.com/kubernetes/kubernetes/issues/107574) */ {
|
|
danglePostSMP := getCondition(postSMP.Status.Conditions, flowcontrol.FlowSchemaConditionDangling)
|
|
if danglePostSMP == nil || danglePostSMP.Status != dangleOrig.Status {
|
|
t.Errorf("Bad dangle condition after SMP, the FS is now %s", fmtFS(t, postSMP))
|
|
}
|
|
ssaPostSMP := getCondition(postSMP.Status.Conditions, ssaType)
|
|
if ssaPostSMP == nil || ssaPostSMP.Status != flowcontrol.ConditionTrue {
|
|
t.Errorf("Bad SSA condition after SMP, the FS is now %s", fmtFS(t, postSMP))
|
|
}
|
|
}
|
|
smpPostSMP := getCondition(postSMP.Status.Conditions, smpType)
|
|
if smpPostSMP == nil || smpPostSMP.Status != flowcontrol.ConditionFalse {
|
|
t.Errorf("Bad SMP condition after SMP, the FS is now %s", fmtFS(t, postSMP))
|
|
}
|
|
|
|
}
|
|
|
|
func getCondition(conds []flowcontrol.FlowSchemaCondition, condType flowcontrol.FlowSchemaConditionType) *flowcontrol.FlowSchemaCondition {
|
|
for _, cond := range conds {
|
|
if cond.Type == condType {
|
|
return &cond
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// makeFlowSchemaConditionPatch takes in a condition and returns the patch status as a json.
|
|
func makeFlowSchemaConditionPatch(condition flowcontrol.FlowSchemaCondition) ([]byte, error) {
|
|
o := struct {
|
|
Status flowcontrol.FlowSchemaStatus `json:"status"`
|
|
}{
|
|
Status: flowcontrol.FlowSchemaStatus{
|
|
Conditions: []flowcontrol.FlowSchemaCondition{
|
|
condition,
|
|
},
|
|
},
|
|
}
|
|
return json.Marshal(o)
|
|
}
|
|
|
|
func fmtFS(t *testing.T, fs *flowcontrol.FlowSchema) string {
|
|
asBytes, err := json.Marshal(fs)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
return string(asBytes)
|
|
}
|