mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
add a unit test
This commit is contained in:
parent
48d3ca23cf
commit
11653b11c1
@ -191,6 +191,7 @@ go_test(
|
|||||||
"//pkg/runtime/serializer/streaming:go_default_library",
|
"//pkg/runtime/serializer/streaming:go_default_library",
|
||||||
"//pkg/types:go_default_library",
|
"//pkg/types:go_default_library",
|
||||||
"//pkg/util/intstr:go_default_library",
|
"//pkg/util/intstr:go_default_library",
|
||||||
|
"//pkg/util/strategicpatch:go_default_library",
|
||||||
"//pkg/util/strings:go_default_library",
|
"//pkg/util/strings:go_default_library",
|
||||||
"//pkg/util/term:go_default_library",
|
"//pkg/util/term:go_default_library",
|
||||||
"//pkg/util/wait:go_default_library",
|
"//pkg/util/wait:go_default_library",
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -37,6 +38,7 @@ import (
|
|||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/util/strategicpatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestApplyExtraArgsFail(t *testing.T) {
|
func TestApplyExtraArgsFail(t *testing.T) {
|
||||||
@ -142,6 +144,58 @@ func readAndAnnotateService(t *testing.T, filename string) (string, []byte) {
|
|||||||
return annotateRuntimeObject(t, svc1, svc2, "Service")
|
return annotateRuntimeObject(t, svc1, svc2, "Service")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setFinalizersRuntimeObject(t *testing.T, originalObj, currentObj runtime.Object) (string, []byte) {
|
||||||
|
originalAccessor, err := meta.Accessor(originalObj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
originalFinalizers := []string{"a/a"}
|
||||||
|
originalAccessor.SetFinalizers(originalFinalizers)
|
||||||
|
original, err := runtime.Encode(testapi.Default.Codec(), originalObj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAccessor, err := meta.Accessor(currentObj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFinalizers := []string{"b/b"}
|
||||||
|
currentAccessor.SetFinalizers(currentFinalizers)
|
||||||
|
|
||||||
|
currentAnnotations := currentAccessor.GetAnnotations()
|
||||||
|
if currentAnnotations == nil {
|
||||||
|
currentAnnotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
currentAnnotations[annotations.LastAppliedConfigAnnotation] = string(original)
|
||||||
|
currentAccessor.SetAnnotations(currentAnnotations)
|
||||||
|
current, err := runtime.Encode(testapi.Default.Codec(), currentObj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentAccessor.GetName(), current
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAndSetFinalizersReplicationController(t *testing.T, filename string) (string, []byte) {
|
||||||
|
rc1 := readReplicationControllerFromFile(t, filename)
|
||||||
|
rc2 := readReplicationControllerFromFile(t, filename)
|
||||||
|
name, rcBytes := setFinalizersRuntimeObject(t, rc1, rc2)
|
||||||
|
return name, rcBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSMPatchVersion_1_5(t *testing.T, req *http.Request) bool {
|
||||||
|
patch, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMPatchVersion_1_5 patch should has string "mergeprimitiveslist"
|
||||||
|
return strings.Contains(string(patch), strategicpatch.MergePrimitivesListDirective)
|
||||||
|
}
|
||||||
|
|
||||||
func validatePatchApplication(t *testing.T, req *http.Request) {
|
func validatePatchApplication(t *testing.T, req *http.Request) {
|
||||||
patch, err := ioutil.ReadAll(req.Body)
|
patch, err := ioutil.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -223,6 +277,65 @@ func TestApplyObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyRetryWithSMPatchVersion_1_5(t *testing.T) {
|
||||||
|
initTestErrorHandler(t)
|
||||||
|
nameRC, currentRC := readAndSetFinalizersReplicationController(t, filenameRC)
|
||||||
|
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
|
||||||
|
|
||||||
|
firstPatch := true
|
||||||
|
retry := false
|
||||||
|
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||||
|
tf.Printer = &testPrinter{}
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
|
case p == pathRC && m == "GET":
|
||||||
|
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
|
||||||
|
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
|
||||||
|
case p == pathRC && m == "PATCH":
|
||||||
|
if firstPatch {
|
||||||
|
if !isSMPatchVersion_1_5(t, req) {
|
||||||
|
t.Fatalf("apply didn't try to send SMPatchVersion_1_5 for the first time")
|
||||||
|
}
|
||||||
|
firstPatch = false
|
||||||
|
statusErr := kubeerr.NewInternalError(fmt.Errorf("Server encountered internal error."))
|
||||||
|
bodyBytes, _ := json.Marshal(statusErr)
|
||||||
|
bodyErr := ioutil.NopCloser(bytes.NewReader(bodyBytes))
|
||||||
|
return &http.Response{StatusCode: http.StatusInternalServerError, Header: defaultHeader(), Body: bodyErr}, nil
|
||||||
|
}
|
||||||
|
retry = true
|
||||||
|
if isSMPatchVersion_1_5(t, req) {
|
||||||
|
t.Fatalf("apply didn't try to send SMPatchVersion_1_0 after SMPatchVersion_1_5 patch encounter an Internal Error (500)")
|
||||||
|
}
|
||||||
|
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
|
||||||
|
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
tf.Namespace = "test"
|
||||||
|
tf.ClientConfig = defaultClientConfig()
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
cmd := NewCmdApply(f, buf)
|
||||||
|
cmd.Flags().Set("filename", filenameRC)
|
||||||
|
cmd.Flags().Set("output", "name")
|
||||||
|
cmd.Run(cmd, []string{})
|
||||||
|
|
||||||
|
if !retry {
|
||||||
|
t.Fatalf("apply didn't retry when get Internal Error (500)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// uses the name from the file, not the response
|
||||||
|
expectRC := "replicationcontroller/" + nameRC + "\n"
|
||||||
|
if buf.String() != expectRC {
|
||||||
|
t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expectRC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyRetry(t *testing.T) {
|
func TestApplyRetry(t *testing.T) {
|
||||||
initTestErrorHandler(t)
|
initTestErrorHandler(t)
|
||||||
nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC)
|
nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC)
|
||||||
|
@ -427,8 +427,12 @@ func (f *fakeAPIFactory) UnstructuredObject() (meta.RESTMapper, runtime.ObjectTy
|
|||||||
return cmdutil.NewShortcutExpander(mapper, nil), typer, nil
|
return cmdutil.NewShortcutExpander(mapper, nil), typer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeAPIFactory) Decoder(bool) runtime.Decoder {
|
func (f *fakeAPIFactory) Decoder(toInternal bool) runtime.Decoder {
|
||||||
return testapi.Default.Codec()
|
if toInternal {
|
||||||
|
return api.Codecs.UniversalDecoder()
|
||||||
|
} else {
|
||||||
|
return api.Codecs.UniversalDeserializer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeAPIFactory) JSONEncoder() runtime.Encoder {
|
func (f *fakeAPIFactory) JSONEncoder() runtime.Encoder {
|
||||||
|
@ -45,7 +45,7 @@ const (
|
|||||||
deleteDirective = "delete"
|
deleteDirective = "delete"
|
||||||
replaceDirective = "replace"
|
replaceDirective = "replace"
|
||||||
mergeDirective = "merge"
|
mergeDirective = "merge"
|
||||||
mergePrimitivesListDirective = "mergeprimitiveslist"
|
MergePrimitivesListDirective = "mergeprimitiveslist"
|
||||||
|
|
||||||
// different versions of StrategicMergePatch
|
// different versions of StrategicMergePatch
|
||||||
SMPatchVersion_1_0 StrategicMergePatchVersion = "v1.0.0"
|
SMPatchVersion_1_0 StrategicMergePatchVersion = "v1.0.0"
|
||||||
@ -393,7 +393,7 @@ loopB:
|
|||||||
func diffListsOfScalarsIntoMap(originalScalars, modifiedScalars []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) {
|
func diffListsOfScalarsIntoMap(originalScalars, modifiedScalars []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) {
|
||||||
originalIndex, modifiedIndex := 0, 0
|
originalIndex, modifiedIndex := 0, 0
|
||||||
patch := map[string]interface{}{}
|
patch := map[string]interface{}{}
|
||||||
patch[directiveMarker] = mergePrimitivesListDirective
|
patch[directiveMarker] = MergePrimitivesListDirective
|
||||||
|
|
||||||
for originalIndex < len(originalScalars) && modifiedIndex < len(modifiedScalars) {
|
for originalIndex < len(originalScalars) && modifiedIndex < len(modifiedScalars) {
|
||||||
originalString := fmt.Sprintf("%v", originalScalars[originalIndex])
|
originalString := fmt.Sprintf("%v", originalScalars[originalIndex])
|
||||||
@ -627,7 +627,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin
|
|||||||
return map[string]interface{}{}, nil
|
return map[string]interface{}{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if v == mergePrimitivesListDirective {
|
if v == MergePrimitivesListDirective {
|
||||||
// delete the directiveMarker's key-value pair to avoid delta map and delete map
|
// delete the directiveMarker's key-value pair to avoid delta map and delete map
|
||||||
// overlaping with each other when calculating a ThreeWayDiff for list of Primitives.
|
// overlaping with each other when calculating a ThreeWayDiff for list of Primitives.
|
||||||
// Otherwise, the overlaping will cause it calling LookupPatchMetadata() which will
|
// Otherwise, the overlaping will cause it calling LookupPatchMetadata() which will
|
||||||
@ -718,7 +718,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin
|
|||||||
// the patch because getting a deep copy of a slice in golang is highly
|
// the patch because getting a deep copy of a slice in golang is highly
|
||||||
// non-trivial.
|
// non-trivial.
|
||||||
// The patch could be a map[string]interface{} representing a slice of primitives.
|
// The patch could be a map[string]interface{} representing a slice of primitives.
|
||||||
// If the patch map doesn't has the specific directiveMarker (mergePrimitivesListDirective),
|
// If the patch map doesn't has the specific directiveMarker (MergePrimitivesListDirective),
|
||||||
// it returns an error. Please check patch_test.go and find the test case named
|
// it returns an error. Please check patch_test.go and find the test case named
|
||||||
// "merge lists of scalars for list of primitives" to see what the patch looks like.
|
// "merge lists of scalars for list of primitives" to see what the patch looks like.
|
||||||
// Patch is still []interface{} for all the other types.
|
// Patch is still []interface{} for all the other types.
|
||||||
@ -731,7 +731,7 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type
|
|||||||
if patchMap, ok := patch.(map[string]interface{}); ok {
|
if patchMap, ok := patch.(map[string]interface{}); ok {
|
||||||
// We try to merge the original slice with a patch map only when the map has
|
// We try to merge the original slice with a patch map only when the map has
|
||||||
// a specific directiveMarker. Otherwise, this patch will be treated as invalid.
|
// a specific directiveMarker. Otherwise, this patch will be treated as invalid.
|
||||||
if directiveValue, ok := patchMap[directiveMarker]; ok && directiveValue == mergePrimitivesListDirective {
|
if directiveValue, ok := patchMap[directiveMarker]; ok && directiveValue == MergePrimitivesListDirective {
|
||||||
return mergeSliceOfScalarsWithPatchMap(original, patchMap)
|
return mergeSliceOfScalarsWithPatchMap(original, patchMap)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Unable to merge a slice with an invalid map")
|
return nil, fmt.Errorf("Unable to merge a slice with an invalid map")
|
||||||
@ -838,10 +838,10 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type
|
|||||||
|
|
||||||
// mergeSliceOfScalarsWithPatchMap merges the original slice with a patch map and
|
// mergeSliceOfScalarsWithPatchMap merges the original slice with a patch map and
|
||||||
// returns an uniqified and sorted slice of primitives.
|
// returns an uniqified and sorted slice of primitives.
|
||||||
// The patch map must have the specific directiveMarker (mergePrimitivesListDirective).
|
// The patch map must have the specific directiveMarker (MergePrimitivesListDirective).
|
||||||
func mergeSliceOfScalarsWithPatchMap(original []interface{}, patch map[string]interface{}) ([]interface{}, error) {
|
func mergeSliceOfScalarsWithPatchMap(original []interface{}, patch map[string]interface{}) ([]interface{}, error) {
|
||||||
// make sure the patch has the specific directiveMarker ()
|
// make sure the patch has the specific directiveMarker ()
|
||||||
if directiveValue, ok := patch[directiveMarker]; ok && directiveValue != mergePrimitivesListDirective {
|
if directiveValue, ok := patch[directiveMarker]; ok && directiveValue != MergePrimitivesListDirective {
|
||||||
return nil, fmt.Errorf("Unable to merge a slice with an invalid map")
|
return nil, fmt.Errorf("Unable to merge a slice with an invalid map")
|
||||||
}
|
}
|
||||||
delete(patch, directiveMarker)
|
delete(patch, directiveMarker)
|
||||||
@ -1181,7 +1181,7 @@ func mergingMapFieldsHaveConflicts(
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftMarker == mergePrimitivesListDirective && rightMarker == mergePrimitivesListDirective {
|
if leftMarker == MergePrimitivesListDirective && rightMarker == MergePrimitivesListDirective {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1209,7 +1209,7 @@ func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType
|
|||||||
isForListOfPrimitives := false
|
isForListOfPrimitives := false
|
||||||
if leftDirective, ok := typedLeft[directiveMarker]; ok {
|
if leftDirective, ok := typedLeft[directiveMarker]; ok {
|
||||||
if rightDirective, ok := typedRight[directiveMarker]; ok {
|
if rightDirective, ok := typedRight[directiveMarker]; ok {
|
||||||
if leftDirective == mergePrimitivesListDirective && rightDirective == rightDirective {
|
if leftDirective == MergePrimitivesListDirective && rightDirective == rightDirective {
|
||||||
isForListOfPrimitives = true
|
isForListOfPrimitives = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/pkg/kubectl/cmd/apply/rc.yaml
vendored
2
test/fixtures/pkg/kubectl/cmd/apply/rc.yaml
vendored
@ -1,6 +1,8 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ReplicationController
|
kind: ReplicationController
|
||||||
metadata:
|
metadata:
|
||||||
|
finalizers:
|
||||||
|
- b/b
|
||||||
name: test-rc
|
name: test-rc
|
||||||
labels:
|
labels:
|
||||||
name: test-rc
|
name: test-rc
|
||||||
|
Loading…
Reference in New Issue
Block a user