diff --git a/pkg/api/v1/endpoints/util_test.go b/pkg/api/v1/endpoints/util_test.go index 5b50e267612..897c0b76c56 100644 --- a/pkg/api/v1/endpoints/util_test.go +++ b/pkg/api/v1/endpoints/util_test.go @@ -20,9 +20,9 @@ import ( "reflect" "testing" - "github.com/davecgh/go-spew/spew" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/dump" ) func podRef(uid string) *v1.ObjectReference { @@ -458,7 +458,7 @@ func TestPackSubsets(t *testing.T) { for _, tc := range testCases { result := RepackSubsets(tc.given) if !reflect.DeepEqual(result, SortSubsets(tc.expect)) { - t.Errorf("case %q: expected %s, got %s", tc.name, spew.Sprintf("%#v", SortSubsets(tc.expect)), spew.Sprintf("%#v", result)) + t.Errorf("case %q: expected %s, got %s", tc.name, dump.Pretty(SortSubsets(tc.expect)), dump.Pretty(result)) } } } diff --git a/pkg/apis/apps/validation/validation_test.go b/pkg/apis/apps/validation/validation_test.go index b245ac5ae2d..07b4ae0818a 100644 --- a/pkg/apis/apps/validation/validation_test.go +++ b/pkg/apis/apps/validation/validation_test.go @@ -21,12 +21,12 @@ import ( "strings" "testing" - "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/validation/field" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -2993,7 +2993,7 @@ func TestValidateDeploymentStatus(t *testing.T) { errs := ValidateDeploymentStatus(&status, field.NewPath("status")) if hasErr := len(errs) > 0; hasErr != test.expectedErr { - errString := spew.Sprintf("%#v", errs) + errString := dump.Pretty(errs) t.Errorf("%s: expected error: %t, got error: %t\nerrors: %s", test.name, test.expectedErr, hasErr, errString) } } @@ -3064,7 +3064,7 @@ func TestValidateDeploymentStatusUpdate(t *testing.T) { errs := ValidateDeploymentStatusUpdate(to, from) if hasErr := len(errs) > 0; hasErr != test.expectedErr { - errString := spew.Sprintf("%#v", errs) + errString := dump.Pretty(errs) t.Errorf("%s: expected error: %t, got error: %t\nerrors: %s", test.name, test.expectedErr, hasErr, errString) } } diff --git a/pkg/kubelet/cm/cpumanager/state/checkpoint.go b/pkg/kubelet/cm/cpumanager/state/checkpoint.go index ca6d2fc90a3..eb2bfa27eaf 100644 --- a/pkg/kubelet/cm/cpumanager/state/checkpoint.go +++ b/pkg/kubelet/cm/cpumanager/state/checkpoint.go @@ -18,11 +18,11 @@ package state import ( "encoding/json" + "fmt" "hash/fnv" "strings" - "github.com/davecgh/go-spew/spew" - + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/errors" @@ -102,21 +102,14 @@ func (cp *CPUManagerCheckpointV1) VerifyChecksum() error { return nil } - printer := spew.ConfigState{ - Indent: " ", - SortKeys: true, - DisableMethods: true, - SpewKeys: true, - } - ck := cp.Checksum cp.Checksum = 0 - object := printer.Sprintf("%#v", cp) + object := dump.ForHash(cp) object = strings.Replace(object, "CPUManagerCheckpointV1", "CPUManagerCheckpoint", 1) cp.Checksum = ck hash := fnv.New32a() - printer.Fprintf(hash, "%v", object) + fmt.Fprintf(hash, "%v", object) if cp.Checksum != checksum.Checksum(hash.Sum32()) { return errors.ErrCorruptCheckpoint } diff --git a/pkg/kubelet/cm/devicemanager/checkpoint/checkpointv1.go b/pkg/kubelet/cm/devicemanager/checkpoint/checkpointv1.go index 9014ebfc1fd..d26b972f7be 100644 --- a/pkg/kubelet/cm/devicemanager/checkpoint/checkpointv1.go +++ b/pkg/kubelet/cm/devicemanager/checkpoint/checkpointv1.go @@ -18,11 +18,11 @@ package checkpoint import ( "encoding/json" + "fmt" "hash/fnv" "strings" - "github.com/davecgh/go-spew/spew" - + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/errors" @@ -48,18 +48,11 @@ type checkpointDataV1 struct { // We need this special code path to be able to correctly validate the checksum k8s 1.19 wrote. // credits to https://github.com/kubernetes/kubernetes/pull/102717/commits/353f93895118d2ffa2d59a29a1fbc225160ea1d6 func (cp checkpointDataV1) checksum() checksum.Checksum { - printer := spew.ConfigState{ - Indent: " ", - SortKeys: true, - DisableMethods: true, - SpewKeys: true, - } - - object := printer.Sprintf("%#v", cp) + object := dump.ForHash(cp) object = strings.Replace(object, "checkpointDataV1", "checkpointData", 1) object = strings.Replace(object, "PodDevicesEntryV1", "PodDevicesEntry", -1) hash := fnv.New32a() - printer.Fprintf(hash, "%v", object) + fmt.Fprintf(hash, "%v", object) return checksum.Checksum(hash.Sum32()) } diff --git a/pkg/util/hash/hash.go b/pkg/util/hash/hash.go index 803f066a440..0962e5cfb5b 100644 --- a/pkg/util/hash/hash.go +++ b/pkg/util/hash/hash.go @@ -17,9 +17,10 @@ limitations under the License. package hash import ( + "fmt" "hash" - "github.com/davecgh/go-spew/spew" + "k8s.io/apimachinery/pkg/util/dump" ) // DeepHashObject writes specified object to hash using the spew library @@ -27,11 +28,5 @@ import ( // ensuring the hash does not change when a pointer changes. func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) { hasher.Reset() - printer := spew.ConfigState{ - Indent: " ", - SortKeys: true, - DisableMethods: true, - SpewKeys: true, - } - printer.Fprintf(hasher, "%#v", objectToWrite) + fmt.Fprintf(hasher, "%v", dump.ForHash(objectToWrite)) } diff --git a/pkg/util/hash/hash_test.go b/pkg/util/hash/hash_test.go index abce506a503..695775c5a0e 100644 --- a/pkg/util/hash/hash_test.go +++ b/pkg/util/hash/hash_test.go @@ -21,7 +21,7 @@ import ( "hash/adler32" "testing" - "github.com/davecgh/go-spew/spew" + "k8s.io/apimachinery/pkg/util/dump" ) type A struct { @@ -93,7 +93,7 @@ func TestDeepHashObject(t *testing.T) { } func toString(obj interface{}) string { - return spew.Sprintf("%#v", obj) + return dump.Pretty(obj) } type wheel struct { diff --git a/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/roundtrip.go b/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/roundtrip.go index 61868c82a68..2edd86f101b 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/roundtrip.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/roundtrip.go @@ -24,7 +24,6 @@ import ( "strings" "testing" - "github.com/davecgh/go-spew/spew" //nolint:staticcheck //iccheck // SA1019 Keep using deprecated module; it still seems to be maintained and the api of the recommended replacement differs "github.com/golang/protobuf/proto" fuzz "github.com/google/gofuzz" @@ -41,6 +40,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/runtime/serializer/protobuf" "k8s.io/apimachinery/pkg/util/diff" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/sets" ) @@ -299,7 +299,6 @@ func roundTripOfExternalType(t *testing.T, scheme *runtime.Scheme, codecFactory // // external -> json/protobuf -> external. func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object runtime.Object) { - printer := spew.ConfigState{DisableMethods: true} original := object // deep copy the original object @@ -307,8 +306,8 @@ func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object name := reflect.TypeOf(object).Elem().Name() if !apiequality.Semantic.DeepEqual(original, object) { t.Errorf("%v: DeepCopy altered the object, diff: %v", name, diff.ObjectReflectDiff(original, object)) - t.Errorf("%s", spew.Sdump(original)) - t.Errorf("%s", spew.Sdump(object)) + t.Errorf("%s", dump.Pretty(original)) + t.Errorf("%s", dump.Pretty(object)) return } @@ -316,9 +315,9 @@ func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object data, err := runtime.Encode(codec, object) if err != nil { if runtime.IsNotRegisteredError(err) { - t.Logf("%v: not registered: %v (%s)", name, err, printer.Sprintf("%#v", object)) + t.Logf("%v: not registered: %v (%s)", name, err, dump.Pretty(object)) } else { - t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", object)) + t.Errorf("%v: %v (%s)", name, err, dump.Pretty(object)) } return } @@ -335,9 +334,9 @@ func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object secondData, err := runtime.Encode(codec, object) if err != nil { if runtime.IsNotRegisteredError(err) { - t.Logf("%v: not registered: %v (%s)", name, err, printer.Sprintf("%#v", object)) + t.Logf("%v: not registered: %v (%s)", name, err, dump.Pretty(object)) } else { - t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", object)) + t.Errorf("%v: %v (%s)", name, err, dump.Pretty(object)) } return } @@ -345,20 +344,20 @@ func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object // serialization to the wire must be stable to ensure that we don't write twice to the DB // when the object hasn't changed. if !bytes.Equal(data, secondData) { - t.Errorf("%v: serialization is not stable: %s", name, printer.Sprintf("%#v", object)) + t.Errorf("%v: serialization is not stable: %s", name, dump.Pretty(object)) } // decode (deserialize) the encoded data back into an object obj2, err := runtime.Decode(codec, data) if err != nil { - t.Errorf("%v: %v\nCodec: %#v\nData: %s\nSource: %#v", name, err, codec, dataAsString(data), printer.Sprintf("%#v", object)) + t.Errorf("%v: %v\nCodec: %#v\nData: %s\nSource: %#v", name, err, codec, dataAsString(data), dump.Pretty(object)) panic("failed") } // ensure that the object produced from decoding the encoded data is equal // to the original object if !apiequality.Semantic.DeepEqual(original, obj2) { - t.Errorf("%v: diff: %v\nCodec: %#v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectReflectDiff(original, obj2), codec, printer.Sprintf("%#v", original), dataAsString(data), printer.Sprintf("%#v", obj2)) + t.Errorf("%v: diff: %v\nCodec: %#v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectReflectDiff(original, obj2), codec, dump.Pretty(original), dataAsString(data), dump.Pretty(obj2)) return } diff --git a/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go b/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go index ec4002e38a2..85066aea8e9 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go @@ -23,8 +23,8 @@ import ( "strings" "text/tabwriter" - "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/util/dump" ) // StringDiff diffs a and b and returns a human readable diff. @@ -75,13 +75,8 @@ func ObjectReflectDiff(a, b interface{}) string { // ObjectGoPrintSideBySide prints a and b as textual dumps side by side, // enabling easy visual scanning for mismatches. func ObjectGoPrintSideBySide(a, b interface{}) string { - s := spew.ConfigState{ - Indent: " ", - // Extra deep spew. - DisableMethods: true, - } - sA := s.Sdump(a) - sB := s.Sdump(b) + sA := dump.Pretty(a) + sB := dump.Pretty(b) linesA := strings.Split(sA, "\n") linesB := strings.Split(sB, "\n") diff --git a/staging/src/k8s.io/apimachinery/pkg/util/dump/dump.go b/staging/src/k8s.io/apimachinery/pkg/util/dump/dump.go new file mode 100644 index 00000000000..cf61ef76aed --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/util/dump/dump.go @@ -0,0 +1,54 @@ +/* +Copyright 2021 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 dump + +import ( + "github.com/davecgh/go-spew/spew" +) + +var prettyPrintConfig = &spew.ConfigState{ + Indent: " ", + DisableMethods: true, + DisablePointerAddresses: true, + DisableCapacities: true, +} + +// The config MUST NOT be changed because that could change the result of a hash operation +var prettyPrintConfigForHash = &spew.ConfigState{ + Indent: " ", + SortKeys: true, + DisableMethods: true, + SpewKeys: true, + DisablePointerAddresses: true, + DisableCapacities: true, +} + +// Pretty wrap the spew.Sdump with Indent, and disabled methods like error() and String() +// The output may change over time, so for guaranteed output please take more direct control +func Pretty(a interface{}) string { + return prettyPrintConfig.Sdump(a) +} + +// ForHash keeps the original Spew.Sprintf format to ensure the same checksum +func ForHash(a interface{}) string { + return prettyPrintConfigForHash.Sprintf("%#v", a) +} + +// OneLine outputs the object in one line +func OneLine(a interface{}) string { + return prettyPrintConfig.Sprintf("%#v", a) +} diff --git a/staging/src/k8s.io/apimachinery/pkg/util/dump/dump_test.go b/staging/src/k8s.io/apimachinery/pkg/util/dump/dump_test.go new file mode 100644 index 00000000000..b597f5c6324 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/util/dump/dump_test.go @@ -0,0 +1,310 @@ +/* +Copyright 2021 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 dump + +import ( + "fmt" + "testing" +) + +func ptrint(i int) *int { + return &i +} + +func ptrstr(s string) *string { + return &s +} + +// custom type to test Stringer interface on non-pointer receiver. +type customString string + +// String implements the Stringer interface for testing invocation +func (s customString) String() string { + return "custom string " + string(s) +} + +// custom type to test error interface on non-pointer receiver. +type customError int + +// Error implements the error interface for testing invocation +func (e customError) Error() string { + return fmt.Sprintf("custom error: %d", int(e)) +} + +// custom type to test Stringer interface on pointer receiver. +type pCustomString string + +// String implements the Stringer interface for testing invocation +func (s *pCustomString) String() string { + return "custom string " + string(*s) +} + +// custom type to test error interface on pointer receiver. +type pCustomError int + +// Error implements the error interface for testing invocation +func (e *pCustomError) Error() string { + return fmt.Sprintf("custom error: %d", int(*e)) +} + +// embed is used to test embedded structures. +type embed struct { + s string +} + +// embedwrap is used to test embedded structures. +type embedwrap struct { + embed + e *embed +} + +func TestPretty(t *testing.T) { + tcs := customString("test") + tpcs := pCustomString("&test") + + tce := customError(0) + tpce := pCustomError(0) + + teb := embed{"test"} + tebw := embedwrap{teb, &teb} + + testCases := []struct { + a interface{} + want string + }{ + {int8(93), "(int8) 93\n"}, + {int16(93), "(int16) 93\n"}, + {int32(93), "(int32) 93\n"}, + {int64(93), "(int64) 93\n"}, + {int(-93), "(int) -93\n"}, + {int8(-93), "(int8) -93\n"}, + {int16(-93), "(int16) -93\n"}, + {int32(-93), "(int32) -93\n"}, + {int64(-93), "(int64) -93\n"}, + {uint(93), "(uint) 93\n"}, + {uint8(93), "(uint8) 93\n"}, + {uint16(93), "(uint16) 93\n"}, + {uint32(93), "(uint32) 93\n"}, + {uint64(93), "(uint64) 93\n"}, + {uintptr(93), "(uintptr) 0x5d\n"}, + {ptrint(93), "(*int)(93)\n"}, + {float32(93.76), "(float32) 93.76\n"}, + {float64(93.76), "(float64) 93.76\n"}, + {complex64(93i), "(complex64) (0+93i)\n"}, + {complex128(93i), "(complex128) (0+93i)\n"}, + {bool(true), "(bool) true\n"}, + {bool(false), "(bool) false\n"}, + {string("test"), "(string) (len=4) \"test\"\n"}, + {ptrstr("test"), "(*string)((len=4) \"test\")\n"}, + {[1]string{"arr"}, "([1]string) (len=1) {\n (string) (len=3) \"arr\"\n}\n"}, + {[]string{"slice"}, "([]string) (len=1) {\n (string) (len=5) \"slice\"\n}\n"}, + {tcs, "(dump.customString) (len=4) \"test\"\n"}, + {&tcs, "(*dump.customString)((len=4) \"test\")\n"}, + {tpcs, "(dump.pCustomString) (len=5) \"&test\"\n"}, + {&tpcs, "(*dump.pCustomString)((len=5) \"&test\")\n"}, + {tce, "(dump.customError) 0\n"}, + {&tce, "(*dump.customError)(0)\n"}, + {tpce, "(dump.pCustomError) 0\n"}, + {&tpce, "(*dump.pCustomError)(0)\n"}, + { + struct { + arr [1]string + slice []string + m map[string]int + }{ + [1]string{"arr"}, + []string{"slice"}, + map[string]int{"one": 1}, + }, + "(struct { arr [1]string; slice []string; m map[string]int }) {\n arr: ([1]string) (len=1) {\n (string) (len=3) \"arr\"\n },\n slice: ([]string) (len=1) {\n (string) (len=5) \"slice\"\n },\n m: (map[string]int) (len=1) {\n (string) (len=3) \"one\": (int) 1\n }\n}\n", + }, + {teb, "(dump.embed) {\n s: (string) (len=4) \"test\"\n}\n"}, + {tebw, "(dump.embedwrap) {\n embed: (dump.embed) {\n s: (string) (len=4) \"test\"\n },\n e: (*dump.embed)({\n s: (string) (len=4) \"test\"\n })\n}\n"}, + {map[string]int{}, "(map[string]int) {\n}\n"}, + {map[string]int{"one": 1}, "(map[string]int) (len=1) {\n (string) (len=3) \"one\": (int) 1\n}\n"}, + {map[string]interface{}{"one": 1}, "(map[string]interface {}) (len=1) {\n (string) (len=3) \"one\": (int) 1\n}\n"}, + {map[string]customString{"key": tcs}, "(map[string]dump.customString) (len=1) {\n (string) (len=3) \"key\": (dump.customString) (len=4) \"test\"\n}\n"}, + {map[string]customError{"key": tce}, "(map[string]dump.customError) (len=1) {\n (string) (len=3) \"key\": (dump.customError) 0\n}\n"}, + {map[string]embed{"key": teb}, "(map[string]dump.embed) (len=1) {\n (string) (len=3) \"key\": (dump.embed) {\n s: (string) (len=4) \"test\"\n }\n}\n"}, + {map[string]embedwrap{"key": tebw}, "(map[string]dump.embedwrap) (len=1) {\n (string) (len=3) \"key\": (dump.embedwrap) {\n embed: (dump.embed) {\n s: (string) (len=4) \"test\"\n },\n e: (*dump.embed)({\n s: (string) (len=4) \"test\"\n })\n }\n}\n"}, + } + + for i, tc := range testCases { + s := Pretty(tc.a) + if tc.want != s { + t.Errorf("[%d]:\n\texpected %q\n\tgot %q", i, tc.want, s) + } + } +} + +func TestForHash(t *testing.T) { + tcs := customString("test") + tpcs := pCustomString("&test") + + tce := customError(0) + tpce := pCustomError(0) + + teb := embed{"test"} + tebw := embedwrap{teb, &teb} + + testCases := []struct { + a interface{} + want string + }{ + {int8(93), "(int8)93"}, + {int16(93), "(int16)93"}, + {int32(93), "(int32)93"}, + {int64(93), "(int64)93"}, + {int(-93), "(int)-93"}, + {int8(-93), "(int8)-93"}, + {int16(-93), "(int16)-93"}, + {int32(-93), "(int32)-93"}, + {int64(-93), "(int64)-93"}, + {uint(93), "(uint)93"}, + {uint8(93), "(uint8)93"}, + {uint16(93), "(uint16)93"}, + {uint32(93), "(uint32)93"}, + {uint64(93), "(uint64)93"}, + {uintptr(93), "(uintptr)0x5d"}, + {ptrint(93), "(*int)93"}, + {float32(93.76), "(float32)93.76"}, + {float64(93.76), "(float64)93.76"}, + {complex64(93i), "(complex64)(0+93i)"}, + {complex128(93i), "(complex128)(0+93i)"}, + {bool(true), "(bool)true"}, + {bool(false), "(bool)false"}, + {string("test"), "(string)test"}, + {ptrstr("test"), "(*string)test"}, + {[1]string{"arr"}, "([1]string)[arr]"}, + {[]string{"slice"}, "([]string)[slice]"}, + {tcs, "(dump.customString)test"}, + {&tcs, "(*dump.customString)test"}, + {tpcs, "(dump.pCustomString)&test"}, + {&tpcs, "(*dump.pCustomString)&test"}, + {tce, "(dump.customError)0"}, + {&tce, "(*dump.customError)0"}, + {tpce, "(dump.pCustomError)0"}, + {&tpce, "(*dump.pCustomError)0"}, + { + struct { + arr [1]string + slice []string + m map[string]int + }{ + [1]string{"arr"}, + []string{"slice"}, + map[string]int{"one": 1}, + }, + "(struct { arr [1]string; slice []string; m map[string]int }){arr:([1]string)[arr] slice:([]string)[slice] m:(map[string]int)map[one:1]}", + }, + {teb, "(dump.embed){s:(string)test}"}, + {tebw, "(dump.embedwrap){embed:(dump.embed){s:(string)test} e:(*dump.embed){s:(string)test}}"}, + {map[string]int{}, "(map[string]int)map[]"}, + {map[string]int{"one": 1, "two": 2}, "(map[string]int)map[one:1 two:2]"}, + {map[string]interface{}{"one": 1}, "(map[string]interface {})map[one:(int)1]"}, + {map[string]customString{"key": tcs}, "(map[string]dump.customString)map[key:test]"}, + {map[string]customError{"key": tce}, "(map[string]dump.customError)map[key:0]"}, + {map[string]embed{"key": teb}, "(map[string]dump.embed)map[key:{s:(string)test}]"}, + {map[string]embedwrap{"key": tebw}, "(map[string]dump.embedwrap)map[key:{embed:(dump.embed){s:(string)test} e:(*dump.embed){s:(string)test}}]"}, + } + + for i, tc := range testCases { + s := ForHash(tc.a) + if tc.want != s { + t.Errorf("[%d]:\n\texpected %q\n\tgot %q", i, tc.want, s) + } + } +} + +func TestOneLine(t *testing.T) { + tcs := customString("test") + tpcs := pCustomString("&test") + + tce := customError(0) + tpce := pCustomError(0) + + teb := embed{"test"} + tebw := embedwrap{teb, &teb} + + testCases := []struct { + a interface{} + want string + }{ + {int8(93), "(int8)93"}, + {int16(93), "(int16)93"}, + {int32(93), "(int32)93"}, + {int64(93), "(int64)93"}, + {int(-93), "(int)-93"}, + {int8(-93), "(int8)-93"}, + {int16(-93), "(int16)-93"}, + {int32(-93), "(int32)-93"}, + {int64(-93), "(int64)-93"}, + {uint(93), "(uint)93"}, + {uint8(93), "(uint8)93"}, + {uint16(93), "(uint16)93"}, + {uint32(93), "(uint32)93"}, + {uint64(93), "(uint64)93"}, + {uintptr(93), "(uintptr)0x5d"}, + {ptrint(93), "(*int)93"}, + {float32(93.76), "(float32)93.76"}, + {float64(93.76), "(float64)93.76"}, + {complex64(93i), "(complex64)(0+93i)"}, + {complex128(93i), "(complex128)(0+93i)"}, + {bool(true), "(bool)true"}, + {bool(false), "(bool)false"}, + {string("test"), "(string)test"}, + {ptrstr("test"), "(*string)test"}, + {[1]string{"arr"}, "([1]string)[arr]"}, + {[]string{"slice"}, "([]string)[slice]"}, + {tcs, "(dump.customString)test"}, + {&tcs, "(*dump.customString)test"}, + {tpcs, "(dump.pCustomString)&test"}, + {&tpcs, "(*dump.pCustomString)&test"}, + {tce, "(dump.customError)0"}, + {&tce, "(*dump.customError)0"}, + {tpce, "(dump.pCustomError)0"}, + {&tpce, "(*dump.pCustomError)0"}, + { + struct { + arr [1]string + slice []string + m map[string]int + }{ + [1]string{"arr"}, + []string{"slice"}, + map[string]int{"one": 1}, + }, + "(struct { arr [1]string; slice []string; m map[string]int }){arr:([1]string)[arr] slice:([]string)[slice] m:(map[string]int)map[one:1]}", + }, + {teb, "(dump.embed){s:(string)test}"}, + {tebw, "(dump.embedwrap){embed:(dump.embed){s:(string)test} e:(*dump.embed){s:(string)test}}"}, + {map[string]int{}, "(map[string]int)map[]"}, + {map[string]int{"one": 1}, "(map[string]int)map[one:1]"}, + {map[string]interface{}{"one": 1}, "(map[string]interface {})map[one:(int)1]"}, + {map[string]customString{"key": tcs}, "(map[string]dump.customString)map[key:test]"}, + {map[string]customError{"key": tce}, "(map[string]dump.customError)map[key:0]"}, + {map[string]embed{"key": teb}, "(map[string]dump.embed)map[key:{s:(string)test}]"}, + {map[string]embedwrap{"key": tebw}, "(map[string]dump.embedwrap)map[key:{embed:(dump.embed){s:(string)test} e:(*dump.embed){s:(string)test}}]"}, + } + + for i, tc := range testCases { + s := OneLine(tc.a) + if tc.want != s { + t.Errorf("[%d]:\n\texpected %q\n\tgot %q", i, tc.want, s) + } + } +} diff --git a/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go b/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go index a20efd18715..25626cf3af2 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go @@ -20,7 +20,7 @@ import ( "fmt" "reflect" - "github.com/davecgh/go-spew/spew" + "k8s.io/apimachinery/pkg/util/dump" "sigs.k8s.io/yaml" ) @@ -76,7 +76,7 @@ func ToYAMLOrError(v interface{}) string { func toYAML(v interface{}) (string, error) { y, err := yaml.Marshal(v) if err != nil { - return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v)) + return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, dump.Pretty(v)) } return string(y), nil diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go index 5331b237a70..b471f5cc648 100644 --- a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go @@ -32,12 +32,12 @@ import ( "sync" "time" - "github.com/davecgh/go-spew/spew" "golang.org/x/term" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/util/dump" utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/client-go/pkg/apis/clientauthentication" "k8s.io/client-go/pkg/apis/clientauthentication/install" @@ -81,8 +81,6 @@ func newCache() *cache { return &cache{m: make(map[string]*Authenticator)} } -var spewConfig = &spew.ConfigState{DisableMethods: true, Indent: " "} - func cacheKey(conf *api.ExecConfig, cluster *clientauthentication.Cluster) string { key := struct { conf *api.ExecConfig @@ -91,7 +89,7 @@ func cacheKey(conf *api.ExecConfig, cluster *clientauthentication.Cluster) strin conf: conf, cluster: cluster, } - return spewConfig.Sprint(key) + return dump.Pretty(key) } type cache struct { diff --git a/staging/src/k8s.io/client-go/tools/watch/retrywatcher.go b/staging/src/k8s.io/client-go/tools/watch/retrywatcher.go index e4806d2ea12..d81dc43570d 100644 --- a/staging/src/k8s.io/client-go/tools/watch/retrywatcher.go +++ b/staging/src/k8s.io/client-go/tools/watch/retrywatcher.go @@ -24,10 +24,9 @@ import ( "net/http" "time" - "github.com/davecgh/go-spew/spew" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" @@ -191,7 +190,7 @@ func (rw *RetryWatcher) doReceive() (bool, time.Duration) { errObject := apierrors.FromObject(event.Object) statusErr, ok := errObject.(*apierrors.StatusError) if !ok { - klog.Error(spew.Sprintf("Received an error which is not *metav1.Status but %#+v", event.Object)) + klog.Error(fmt.Sprintf("Received an error which is not *metav1.Status but %s", dump.Pretty(event.Object))) // Retry unknown errors return false, 0 } @@ -220,7 +219,7 @@ func (rw *RetryWatcher) doReceive() (bool, time.Duration) { // Log here so we have a record of hitting the unexpected error // and we can whitelist some error codes if we missed any that are expected. - klog.V(5).Info(spew.Sprintf("Retrying after unexpected error: %#+v", event.Object)) + klog.V(5).Info(fmt.Sprintf("Retrying after unexpected error: %s", dump.Pretty(event.Object))) // Retry return false, statusDelay diff --git a/test/e2e/apps/deployment.go b/test/e2e/apps/deployment.go index 6d702e6677a..6798995445e 100644 --- a/test/e2e/apps/deployment.go +++ b/test/e2e/apps/deployment.go @@ -24,7 +24,6 @@ import ( "strings" "time" - "github.com/davecgh/go-spew/spew" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/fields" @@ -40,6 +39,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/intstr" utilrand "k8s.io/apimachinery/pkg/util/rand" "k8s.io/apimachinery/pkg/util/wait" @@ -626,7 +626,7 @@ func failureTrap(ctx context.Context, c clientset.Interface, ns string) { for i := range deployments.Items { d := deployments.Items[i] - framework.Logf(spew.Sprintf("Deployment %q:\n%+v\n", d.Name, d)) + framework.Logf("Deployment %q:\n%s\n", d.Name, dump.Pretty(d)) _, allOldRSs, newRS, err := testutil.GetAllReplicaSets(&d, c) if err != nil { framework.Logf("Could not list ReplicaSets for Deployment %q: %v", d.Name, err) @@ -650,7 +650,7 @@ func failureTrap(ctx context.Context, c clientset.Interface, ns string) { return } for _, rs := range rss.Items { - framework.Logf(spew.Sprintf("ReplicaSet %q:\n%+v\n", rs.Name, rs)) + framework.Logf("ReplicaSet %q:\n%s\n", rs.Name, dump.Pretty(rs)) selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector) if err != nil { framework.Logf("failed to get selector of ReplicaSet %s: %v", rs.Name, err) @@ -662,7 +662,7 @@ func failureTrap(ctx context.Context, c clientset.Interface, ns string) { continue } for _, pod := range podList.Items { - framework.Logf(spew.Sprintf("pod: %q:\n%+v\n", pod.Name, pod)) + framework.Logf("pod: %q:\n%s\n", pod.Name, dump.Pretty(pod)) } } } diff --git a/test/e2e/storage/testsuites/volumeperf.go b/test/e2e/storage/testsuites/volumeperf.go index 528c30a6e19..ed7e3ef987c 100644 --- a/test/e2e/storage/testsuites/volumeperf.go +++ b/test/e2e/storage/testsuites/volumeperf.go @@ -22,13 +22,13 @@ import ( "sync" "time" - "github.com/davecgh/go-spew/spew" "github.com/onsi/ginkgo/v2" v1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/watch" clientset "k8s.io/client-go/kubernetes" @@ -205,7 +205,7 @@ func (t *volumePerformanceTestSuite) DefineTests(driver storageframework.TestDri ginkgo.By("Calculating performance metrics for provisioning operations") createPerformanceStats(provisioningStats, l.options.ProvisioningOptions.Count, l.pvcs) - ginkgo.By(fmt.Sprintf("Validating performance metrics for provisioning operations against baseline %v", spew.Sdump(l.options.ProvisioningOptions.ExpectedMetrics))) + ginkgo.By(fmt.Sprintf("Validating performance metrics for provisioning operations against baseline %v", dump.Pretty(l.options.ProvisioningOptions.ExpectedMetrics))) errList := validatePerformanceStats(provisioningStats.operationMetrics, l.options.ProvisioningOptions.ExpectedMetrics) framework.ExpectNoError(errors.NewAggregate(errList), "while validating performance metrics") }) @@ -240,7 +240,7 @@ func createPerformanceStats(stats *performanceStats, provisionCount int, pvcs [] // validatePerformanceStats validates if test performance metrics meet the baseline target func validatePerformanceStats(operationMetrics *storageframework.Metrics, baselineMetrics *storageframework.Metrics) []error { var errList []error - framework.Logf("Metrics to evaluate: %+v", spew.Sdump(operationMetrics)) + framework.Logf("Metrics to evaluate: %+v", dump.Pretty(operationMetrics)) if operationMetrics.AvgLatency > baselineMetrics.AvgLatency { err := fmt.Errorf("expected latency to be less than %v but calculated latency %v", baselineMetrics.AvgLatency, operationMetrics.AvgLatency) diff --git a/test/integration/client/exec_test.go b/test/integration/client/exec_test.go index 45faf5ce902..b9616b80889 100644 --- a/test/integration/client/exec_test.go +++ b/test/integration/client/exec_test.go @@ -33,10 +33,10 @@ import ( "testing" "time" - "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/rand" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" @@ -612,7 +612,7 @@ func (is *informerSpy) waitForEvents(t *testing.T, wantEvents bool) { if err != nil { t.Fatalf("wanted no events, but got error: %v", err) } else { - t.Fatalf("wanted no events, but got some: %s", spew.Sprintf("%#v", is)) + t.Fatalf("wanted no events, but got some: %s", dump.Pretty(is)) } } } diff --git a/test/utils/deployment.go b/test/utils/deployment.go index 84687f36b83..b7bb7b7fe59 100644 --- a/test/utils/deployment.go +++ b/test/utils/deployment.go @@ -21,11 +21,10 @@ import ( "fmt" "time" - "github.com/davecgh/go-spew/spew" - apps "k8s.io/api/apps/v1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/dump" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" podutil "k8s.io/kubernetes/pkg/api/v1/pod" @@ -37,7 +36,7 @@ type LogfFn func(format string, args ...interface{}) func LogReplicaSetsOfDeployment(deployment *apps.Deployment, allOldRSs []*apps.ReplicaSet, newRS *apps.ReplicaSet, logf LogfFn) { if newRS != nil { - logf(spew.Sprintf("New ReplicaSet %q of Deployment %q:\n%+v", newRS.Name, deployment.Name, *newRS)) + logf("New ReplicaSet %q of Deployment %q:\n%s", newRS.Name, deployment.Name, dump.Pretty(*newRS)) } else { logf("New ReplicaSet of Deployment %q is nil.", deployment.Name) } @@ -45,7 +44,7 @@ func LogReplicaSetsOfDeployment(deployment *apps.Deployment, allOldRSs []*apps.R logf("All old ReplicaSets of Deployment %q:", deployment.Name) } for i := range allOldRSs { - logf(spew.Sprintf("%+v", *allOldRSs[i])) + logf(dump.Pretty(*allOldRSs[i])) } } @@ -65,7 +64,7 @@ func LogPodsOfDeployment(c clientset.Interface, deployment *apps.Deployment, rsL if podutil.IsPodAvailable(&pod, minReadySeconds, metav1.Now()) { availability = "available" } - logf(spew.Sprintf("Pod %q is %s:\n%+v", pod.Name, availability, pod)) + logf("Pod %q is %s:\n%s", pod.Name, availability, dump.Pretty(pod)) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 3e9bf905103..0fea6324711 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1355,6 +1355,7 @@ k8s.io/apimachinery/pkg/selection k8s.io/apimachinery/pkg/types k8s.io/apimachinery/pkg/util/cache k8s.io/apimachinery/pkg/util/diff +k8s.io/apimachinery/pkg/util/dump k8s.io/apimachinery/pkg/util/duration k8s.io/apimachinery/pkg/util/errors k8s.io/apimachinery/pkg/util/framer