diff --git a/pkg/fieldpath/fieldpath.go b/pkg/fieldpath/fieldpath.go index 20fa8eaaaae..f8466d40348 100644 --- a/pkg/fieldpath/fieldpath.go +++ b/pkg/fieldpath/fieldpath.go @@ -18,26 +18,36 @@ package fieldpath import ( "fmt" + "sort" + "strconv" "strings" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" ) // FormatMap formats map[string]string to a string. func FormatMap(m map[string]string) (fmtStr string) { // output with keys in sorted order to provide stable output - keys := sets.NewString() - for key := range m { - keys.Insert(key) + keys := make([]string, 0, len(m)) + var grow int + for k, v := range m { + keys = append(keys, k) + // why add 4: (for =, \n, " and ") + grow += len(k) + len(v) + 4 } - for _, key := range keys.List() { - fmtStr += fmt.Sprintf("%v=%q\n", key, m[key]) + sort.Strings(keys) + // allocate space to avoid expansion + dst := make([]byte, 0, grow) + for _, key := range keys { + if len(dst) > 0 { + dst = append(dst, '\n') + } + dst = append(dst, key...) + dst = append(dst, '=') + dst = strconv.AppendQuote(dst, m[key]) } - fmtStr = strings.TrimSuffix(fmtStr, "\n") - - return + return string(dst) } // ExtractFieldPathAsString extracts the field from the given object diff --git a/pkg/fieldpath/fieldpath_test.go b/pkg/fieldpath/fieldpath_test.go index 0aa6ad56d72..9377bf58fdd 100644 --- a/pkg/fieldpath/fieldpath_test.go +++ b/pkg/fieldpath/fieldpath_test.go @@ -24,6 +24,30 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func BenchmarkFormatMap(b *testing.B) { + var s string + m := map[string]string{ + "spec.pod.beta.kubernetes.io/statefulset-index": "1", + "Www.k8s.io/test": "1", + "foo": "bar", + "flannel.alpha.coreos.com/backend-data": `{"VNI":1,"VtepMAC":"ce:f9:c7:a4:de:64"}`, + "flannel.alpha.coreos.com/backend-type": "vxlan", + "flannel.alpha.coreos.com/kube-subnet-manager": "true", + "flannel.alpha.coreos.com/public-ip": "192.168.19.160", + "management.cattle.io/pod-limits": `{"cpu":"11400m","memory":"7965Mi"}`, + "management.cattle.io/pod-requests": `{"cpu":"2482m","memory":"7984Mi","pods":"26"}`, + "node.alpha.kubernetes.io/ttl": "0", + "volumes.kubernetes.io/controller-managed-attach-detach": "true", + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + s = FormatMap(m) + } + // Avoid compiler optimizations + _ = s +} + func TestExtractFieldPathAsString(t *testing.T) { cases := []struct { name string @@ -268,3 +292,72 @@ func TestSplitMaybeSubscriptedPath(t *testing.T) { } } } + +// TestFormatMap +func TestFormatMap(t *testing.T) { + type args struct { + m map[string]string + } + tests := []struct { + name string + args args + wantFmtStr string + }{ + { + name: "nil", + args: args{ + m: nil, + }, + wantFmtStr: "", + }, + { + name: "label", + args: args{ + m: map[string]string{ + "beta.kubernetes.io/os": "linux", + "kubernetes.io/arch": "amd64", + "kubernetes.io/hostname": "master01", + "kubernetes.io/os": "linux", + "node-role.kubernetes.io/control-plane": "true", + "node-role.kubernetes.io/master": "true", + }, + }, + wantFmtStr: `beta.kubernetes.io/os="linux" +kubernetes.io/arch="amd64" +kubernetes.io/hostname="master01" +kubernetes.io/os="linux" +node-role.kubernetes.io/control-plane="true" +node-role.kubernetes.io/master="true"`, + }, + { + name: "annotation", + args: args{ + m: map[string]string{ + "flannel.alpha.coreos.com/backend-data": `{"VNI":1,"VtepMAC":"ce:f9:c7:a4:de:64"}`, + "flannel.alpha.coreos.com/backend-type": "vxlan", + "flannel.alpha.coreos.com/kube-subnet-manager": "true", + "flannel.alpha.coreos.com/public-ip": "192.168.19.160", + "management.cattle.io/pod-limits": `{"cpu":"11400m","memory":"7965Mi"}`, + "management.cattle.io/pod-requests": `{"cpu":"2482m","memory":"7984Mi","pods":"26"}`, + "node.alpha.kubernetes.io/ttl": "0", + "volumes.kubernetes.io/controller-managed-attach-detach": "true", + }, + }, + wantFmtStr: `flannel.alpha.coreos.com/backend-data="{\"VNI\":1,\"VtepMAC\":\"ce:f9:c7:a4:de:64\"}" +flannel.alpha.coreos.com/backend-type="vxlan" +flannel.alpha.coreos.com/kube-subnet-manager="true" +flannel.alpha.coreos.com/public-ip="192.168.19.160" +management.cattle.io/pod-limits="{\"cpu\":\"11400m\",\"memory\":\"7965Mi\"}" +management.cattle.io/pod-requests="{\"cpu\":\"2482m\",\"memory\":\"7984Mi\",\"pods\":\"26\"}" +node.alpha.kubernetes.io/ttl="0" +volumes.kubernetes.io/controller-managed-attach-detach="true"`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotFmtStr := FormatMap(tt.args.m); gotFmtStr != tt.wantFmtStr { + t.Errorf("FormatMap() = %v, want %v", gotFmtStr, tt.wantFmtStr) + } + }) + } +}