mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-18 19:10:21 +00:00
198 lines
6.8 KiB
Go
198 lines
6.8 KiB
Go
|
/*
|
||
|
Copyright 2018 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 fake
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
"k8s.io/apimachinery/pkg/api/equality"
|
||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
|
"k8s.io/apimachinery/pkg/runtime"
|
||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
|
"k8s.io/apimachinery/pkg/types"
|
||
|
"k8s.io/apimachinery/pkg/util/diff"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
testGroup = "testgroup"
|
||
|
testVersion = "testversion"
|
||
|
testResource = "testkinds"
|
||
|
testNamespace = "testns"
|
||
|
testName = "testname"
|
||
|
testKind = "TestKind"
|
||
|
testAPIVersion = "testgroup/testversion"
|
||
|
)
|
||
|
|
||
|
func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
|
||
|
return &unstructured.Unstructured{
|
||
|
Object: map[string]interface{}{
|
||
|
"apiVersion": apiVersion,
|
||
|
"kind": kind,
|
||
|
"metadata": map[string]interface{}{
|
||
|
"namespace": namespace,
|
||
|
"name": name,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func newUnstructuredWithSpec(spec map[string]interface{}) *unstructured.Unstructured {
|
||
|
u := newUnstructured(testAPIVersion, testKind, testNamespace, testName)
|
||
|
u.Object["spec"] = spec
|
||
|
return u
|
||
|
}
|
||
|
|
||
|
func TestList(t *testing.T) {
|
||
|
scheme := runtime.NewScheme()
|
||
|
|
||
|
client := NewSimpleDynamicClient(scheme,
|
||
|
newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
|
||
|
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
|
||
|
newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
|
||
|
newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
|
||
|
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"),
|
||
|
)
|
||
|
listFirst, err := client.Resource(schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}).List(metav1.ListOptions{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
expected := []unstructured.Unstructured{
|
||
|
*newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
|
||
|
*newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
|
||
|
*newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
|
||
|
}
|
||
|
if !equality.Semantic.DeepEqual(listFirst.Items, expected) {
|
||
|
t.Fatal(diff.ObjectGoPrintDiff(expected, listFirst.Items))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type patchTestCase struct {
|
||
|
name string
|
||
|
object runtime.Object
|
||
|
patchType types.PatchType
|
||
|
patchBytes []byte
|
||
|
wantErrMsg string
|
||
|
expectedPatchedObject runtime.Object
|
||
|
}
|
||
|
|
||
|
func (tc *patchTestCase) runner(t *testing.T) {
|
||
|
client := NewSimpleDynamicClient(runtime.NewScheme(), tc.object)
|
||
|
resourceInterface := client.Resource(schema.GroupVersionResource{Group: testGroup, Version: testVersion, Resource: testResource}).Namespace(testNamespace)
|
||
|
|
||
|
got, recErr := resourceInterface.Patch(testName, tc.patchType, tc.patchBytes, metav1.UpdateOptions{})
|
||
|
|
||
|
if err := tc.verifyErr(recErr); err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
if err := tc.verifyResult(got); err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// verifyErr verifies that the given error returned from Patch is the error
|
||
|
// expected by the test case.
|
||
|
func (tc *patchTestCase) verifyErr(err error) error {
|
||
|
if tc.wantErrMsg != "" && err == nil {
|
||
|
return fmt.Errorf("want error, got nil")
|
||
|
}
|
||
|
|
||
|
if tc.wantErrMsg == "" && err != nil {
|
||
|
return fmt.Errorf("want no error, got %v", err)
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
if want, got := tc.wantErrMsg, err.Error(); want != got {
|
||
|
return fmt.Errorf("incorrect error: want: %q got: %q", want, got)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (tc *patchTestCase) verifyResult(result *unstructured.Unstructured) error {
|
||
|
if tc.expectedPatchedObject == nil && result == nil {
|
||
|
return nil
|
||
|
}
|
||
|
if !equality.Semantic.DeepEqual(result, tc.expectedPatchedObject) {
|
||
|
return fmt.Errorf("unexpected diff in received object: %s", diff.ObjectGoPrintDiff(tc.expectedPatchedObject, result))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func TestPatch(t *testing.T) {
|
||
|
testCases := []patchTestCase{
|
||
|
{
|
||
|
name: "jsonpatch fails with merge type",
|
||
|
object: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
patchType: types.StrategicMergePatchType,
|
||
|
patchBytes: []byte(`[]`),
|
||
|
wantErrMsg: "invalid JSON document",
|
||
|
}, {
|
||
|
name: "jsonpatch works with empty patch",
|
||
|
object: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
patchType: types.JSONPatchType,
|
||
|
// No-op
|
||
|
patchBytes: []byte(`[]`),
|
||
|
expectedPatchedObject: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
}, {
|
||
|
name: "jsonpatch works with simple change patch",
|
||
|
object: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
patchType: types.JSONPatchType,
|
||
|
// change spec.foo from bar to foobar
|
||
|
patchBytes: []byte(`[{"op": "replace", "path": "/spec/foo", "value": "foobar"}]`),
|
||
|
expectedPatchedObject: newUnstructuredWithSpec(map[string]interface{}{"foo": "foobar"}),
|
||
|
}, {
|
||
|
name: "jsonpatch works with simple addition",
|
||
|
object: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
patchType: types.JSONPatchType,
|
||
|
// add spec.newvalue = dummy
|
||
|
patchBytes: []byte(`[{"op": "add", "path": "/spec/newvalue", "value": "dummy"}]`),
|
||
|
expectedPatchedObject: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar", "newvalue": "dummy"}),
|
||
|
}, {
|
||
|
name: "jsonpatch works with simple deletion",
|
||
|
object: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar", "toremove": "shouldnotbehere"}),
|
||
|
patchType: types.JSONPatchType,
|
||
|
// remove spec.newvalue = dummy
|
||
|
patchBytes: []byte(`[{"op": "remove", "path": "/spec/toremove"}]`),
|
||
|
expectedPatchedObject: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
}, {
|
||
|
name: "strategic merge patch fails with JSONPatch",
|
||
|
object: newUnstructuredWithSpec(map[string]interface{}{"foo": "bar"}),
|
||
|
patchType: types.StrategicMergePatchType,
|
||
|
// add spec.newvalue = dummy
|
||
|
patchBytes: []byte(`[{"op": "add", "path": "/spec/newvalue", "value": "dummy"}]`),
|
||
|
wantErrMsg: "invalid JSON document",
|
||
|
}, {
|
||
|
name: "merge patch fails as unsupported",
|
||
|
object: newUnstructured(testAPIVersion, testKind, testNamespace, testName),
|
||
|
patchType: types.MergePatchType,
|
||
|
patchBytes: []byte(`{}`),
|
||
|
wantErrMsg: "PatchType is not supported",
|
||
|
},
|
||
|
// TODO: Add tests for strategic merge using v1.Pod for example to ensure the test cases
|
||
|
// demonstrate expected use cases.
|
||
|
}
|
||
|
|
||
|
for _, tc := range testCases {
|
||
|
t.Run(tc.name, tc.runner)
|
||
|
}
|
||
|
}
|