...

Source file src/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go

Documentation: k8s.io/kubernetes/pkg/fieldpath

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fieldpath
    18  
    19  import (
    20  	"strings"
    21  	"testing"
    22  
    23  	"k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  )
    26  
    27  func BenchmarkFormatMap(b *testing.B) {
    28  	var s string
    29  	m := map[string]string{
    30  		"spec.pod.beta.kubernetes.io/statefulset-index": "1",
    31  		"Www.k8s.io/test":                                        "1",
    32  		"foo":                                                    "bar",
    33  		"flannel.alpha.coreos.com/backend-data":                  `{"VNI":1,"VtepMAC":"ce:f9:c7:a4:de:64"}`,
    34  		"flannel.alpha.coreos.com/backend-type":                  "vxlan",
    35  		"flannel.alpha.coreos.com/kube-subnet-manager":           "true",
    36  		"flannel.alpha.coreos.com/public-ip":                     "192.168.19.160",
    37  		"management.cattle.io/pod-limits":                        `{"cpu":"11400m","memory":"7965Mi"}`,
    38  		"management.cattle.io/pod-requests":                      `{"cpu":"2482m","memory":"7984Mi","pods":"26"}`,
    39  		"node.alpha.kubernetes.io/ttl":                           "0",
    40  		"volumes.kubernetes.io/controller-managed-attach-detach": "true",
    41  	}
    42  	b.ReportAllocs()
    43  	b.ResetTimer()
    44  	for i := 0; i < b.N; i++ {
    45  		s = FormatMap(m)
    46  	}
    47  	// Avoid compiler optimizations
    48  	_ = s
    49  }
    50  
    51  func TestExtractFieldPathAsString(t *testing.T) {
    52  	cases := []struct {
    53  		name                    string
    54  		fieldPath               string
    55  		obj                     interface{}
    56  		expectedValue           string
    57  		expectedMessageFragment string
    58  	}{
    59  		{
    60  			name:                    "not an API object",
    61  			fieldPath:               "metadata.name",
    62  			obj:                     "",
    63  			expectedMessageFragment: "object does not implement the Object interfaces",
    64  		},
    65  		{
    66  			name:      "ok - namespace",
    67  			fieldPath: "metadata.namespace",
    68  			obj: &v1.Pod{
    69  				ObjectMeta: metav1.ObjectMeta{
    70  					Namespace: "object-namespace",
    71  				},
    72  			},
    73  			expectedValue: "object-namespace",
    74  		},
    75  		{
    76  			name:      "ok - name",
    77  			fieldPath: "metadata.name",
    78  			obj: &v1.Pod{
    79  				ObjectMeta: metav1.ObjectMeta{
    80  					Name: "object-name",
    81  				},
    82  			},
    83  			expectedValue: "object-name",
    84  		},
    85  		{
    86  			name:      "ok - labels",
    87  			fieldPath: "metadata.labels",
    88  			obj: &v1.Pod{
    89  				ObjectMeta: metav1.ObjectMeta{
    90  					Labels: map[string]string{"key": "value"},
    91  				},
    92  			},
    93  			expectedValue: "key=\"value\"",
    94  		},
    95  		{
    96  			name:      "ok - labels bslash n",
    97  			fieldPath: "metadata.labels",
    98  			obj: &v1.Pod{
    99  				ObjectMeta: metav1.ObjectMeta{
   100  					Labels: map[string]string{"key": "value\n"},
   101  				},
   102  			},
   103  			expectedValue: "key=\"value\\n\"",
   104  		},
   105  		{
   106  			name:      "ok - annotations",
   107  			fieldPath: "metadata.annotations",
   108  			obj: &v1.Pod{
   109  				ObjectMeta: metav1.ObjectMeta{
   110  					Annotations: map[string]string{"builder": "john-doe"},
   111  				},
   112  			},
   113  			expectedValue: "builder=\"john-doe\"",
   114  		},
   115  		{
   116  			name:      "ok - annotation",
   117  			fieldPath: "metadata.annotations['spec.pod.beta.kubernetes.io/statefulset-index']",
   118  			obj: &v1.Pod{
   119  				ObjectMeta: metav1.ObjectMeta{
   120  					Annotations: map[string]string{"spec.pod.beta.kubernetes.io/statefulset-index": "1"},
   121  				},
   122  			},
   123  			expectedValue: "1",
   124  		},
   125  		{
   126  			name:      "ok - annotation",
   127  			fieldPath: "metadata.annotations['Www.k8s.io/test']",
   128  			obj: &v1.Pod{
   129  				ObjectMeta: metav1.ObjectMeta{
   130  					Annotations: map[string]string{"Www.k8s.io/test": "1"},
   131  				},
   132  			},
   133  			expectedValue: "1",
   134  		},
   135  		{
   136  			name:      "ok - uid",
   137  			fieldPath: "metadata.uid",
   138  			obj: &v1.Pod{
   139  				ObjectMeta: metav1.ObjectMeta{
   140  					UID: "b70b3269-858e-12a8-9cf2-1232a194038a",
   141  				},
   142  			},
   143  			expectedValue: "b70b3269-858e-12a8-9cf2-1232a194038a",
   144  		},
   145  		{
   146  			name:      "ok - label",
   147  			fieldPath: "metadata.labels['something']",
   148  			obj: &v1.Pod{
   149  				ObjectMeta: metav1.ObjectMeta{
   150  					Labels: map[string]string{
   151  						"something": "label value",
   152  					},
   153  				},
   154  			},
   155  			expectedValue: "label value",
   156  		},
   157  		{
   158  			name:      "invalid expression",
   159  			fieldPath: "metadata.whoops",
   160  			obj: &v1.Pod{
   161  				ObjectMeta: metav1.ObjectMeta{
   162  					Namespace: "object-namespace",
   163  				},
   164  			},
   165  			expectedMessageFragment: "unsupported fieldPath",
   166  		},
   167  		{
   168  			name:      "invalid annotation key",
   169  			fieldPath: "metadata.annotations['invalid~key']",
   170  			obj: &v1.Pod{
   171  				ObjectMeta: metav1.ObjectMeta{
   172  					Annotations: map[string]string{"foo": "bar"},
   173  				},
   174  			},
   175  			expectedMessageFragment: "invalid key subscript in metadata.annotations",
   176  		},
   177  		{
   178  			name:      "invalid label key",
   179  			fieldPath: "metadata.labels['Www.k8s.io/test']",
   180  			obj: &v1.Pod{
   181  				ObjectMeta: metav1.ObjectMeta{
   182  					Annotations: map[string]string{"foo": "bar"},
   183  				},
   184  			},
   185  			expectedMessageFragment: "invalid key subscript in metadata.labels",
   186  		},
   187  		{
   188  			name:                    "invalid subscript",
   189  			fieldPath:               "metadata.notexisting['something']",
   190  			obj:                     &v1.Pod{},
   191  			expectedMessageFragment: "fieldPath \"metadata.notexisting['something']\" does not support subscript",
   192  		},
   193  	}
   194  
   195  	for _, tc := range cases {
   196  		actual, err := ExtractFieldPathAsString(tc.obj, tc.fieldPath)
   197  		if err != nil {
   198  			if tc.expectedMessageFragment != "" {
   199  				if !strings.Contains(err.Error(), tc.expectedMessageFragment) {
   200  					t.Errorf("%v: unexpected error message: %q, expected to contain %q", tc.name, err, tc.expectedMessageFragment)
   201  				}
   202  			} else {
   203  				t.Errorf("%v: unexpected error: %v", tc.name, err)
   204  			}
   205  		} else if tc.expectedMessageFragment != "" {
   206  			t.Errorf("%v: expected error: %v", tc.name, tc.expectedMessageFragment)
   207  		} else if e := tc.expectedValue; e != "" && e != actual {
   208  			t.Errorf("%v: unexpected result; got %q, expected %q", tc.name, actual, e)
   209  		}
   210  	}
   211  }
   212  
   213  func TestSplitMaybeSubscriptedPath(t *testing.T) {
   214  	cases := []struct {
   215  		fieldPath         string
   216  		expectedPath      string
   217  		expectedSubscript string
   218  		expectedOK        bool
   219  	}{
   220  		{
   221  			fieldPath:         "metadata.annotations['key']",
   222  			expectedPath:      "metadata.annotations",
   223  			expectedSubscript: "key",
   224  			expectedOK:        true,
   225  		},
   226  		{
   227  			fieldPath:         "metadata.annotations['a[b']c']",
   228  			expectedPath:      "metadata.annotations",
   229  			expectedSubscript: "a[b']c",
   230  			expectedOK:        true,
   231  		},
   232  		{
   233  			fieldPath:         "metadata.labels['['key']",
   234  			expectedPath:      "metadata.labels",
   235  			expectedSubscript: "['key",
   236  			expectedOK:        true,
   237  		},
   238  		{
   239  			fieldPath:         "metadata.labels['key']']",
   240  			expectedPath:      "metadata.labels",
   241  			expectedSubscript: "key']",
   242  			expectedOK:        true,
   243  		},
   244  		{
   245  			fieldPath:         "metadata.labels['']",
   246  			expectedPath:      "metadata.labels",
   247  			expectedSubscript: "",
   248  			expectedOK:        true,
   249  		},
   250  		{
   251  			fieldPath:         "metadata.labels[' ']",
   252  			expectedPath:      "metadata.labels",
   253  			expectedSubscript: " ",
   254  			expectedOK:        true,
   255  		},
   256  		{
   257  			fieldPath:  "metadata.labels[ 'key' ]",
   258  			expectedOK: false,
   259  		},
   260  		{
   261  			fieldPath:  "metadata.labels[]",
   262  			expectedOK: false,
   263  		},
   264  		{
   265  			fieldPath:  "metadata.labels[']",
   266  			expectedOK: false,
   267  		},
   268  		{
   269  			fieldPath:  "metadata.labels['key']foo",
   270  			expectedOK: false,
   271  		},
   272  		{
   273  			fieldPath:  "['key']",
   274  			expectedOK: false,
   275  		},
   276  		{
   277  			fieldPath:  "metadata.labels",
   278  			expectedOK: false,
   279  		},
   280  	}
   281  	for _, tc := range cases {
   282  		path, subscript, ok := SplitMaybeSubscriptedPath(tc.fieldPath)
   283  		if !ok {
   284  			if tc.expectedOK {
   285  				t.Errorf("SplitMaybeSubscriptedPath(%q) expected to return (_, _, true)", tc.fieldPath)
   286  			}
   287  			continue
   288  		}
   289  		if path != tc.expectedPath || subscript != tc.expectedSubscript {
   290  			t.Errorf("SplitMaybeSubscriptedPath(%q) = (%q, %q, true), expect (%q, %q, true)",
   291  				tc.fieldPath, path, subscript, tc.expectedPath, tc.expectedSubscript)
   292  		}
   293  	}
   294  }
   295  
   296  // TestFormatMap
   297  func TestFormatMap(t *testing.T) {
   298  	type args struct {
   299  		m map[string]string
   300  	}
   301  	tests := []struct {
   302  		name       string
   303  		args       args
   304  		wantFmtStr string
   305  	}{
   306  		{
   307  			name: "nil",
   308  			args: args{
   309  				m: nil,
   310  			},
   311  			wantFmtStr: "",
   312  		},
   313  		{
   314  			name: "label",
   315  			args: args{
   316  				m: map[string]string{
   317  					"beta.kubernetes.io/os":                 "linux",
   318  					"kubernetes.io/arch":                    "amd64",
   319  					"kubernetes.io/hostname":                "master01",
   320  					"kubernetes.io/os":                      "linux",
   321  					"node-role.kubernetes.io/control-plane": "true",
   322  					"node-role.kubernetes.io/master":        "true",
   323  				},
   324  			},
   325  			wantFmtStr: `beta.kubernetes.io/os="linux"
   326  kubernetes.io/arch="amd64"
   327  kubernetes.io/hostname="master01"
   328  kubernetes.io/os="linux"
   329  node-role.kubernetes.io/control-plane="true"
   330  node-role.kubernetes.io/master="true"`,
   331  		},
   332  		{
   333  			name: "annotation",
   334  			args: args{
   335  				m: map[string]string{
   336  					"flannel.alpha.coreos.com/backend-data":                  `{"VNI":1,"VtepMAC":"ce:f9:c7:a4:de:64"}`,
   337  					"flannel.alpha.coreos.com/backend-type":                  "vxlan",
   338  					"flannel.alpha.coreos.com/kube-subnet-manager":           "true",
   339  					"flannel.alpha.coreos.com/public-ip":                     "192.168.19.160",
   340  					"management.cattle.io/pod-limits":                        `{"cpu":"11400m","memory":"7965Mi"}`,
   341  					"management.cattle.io/pod-requests":                      `{"cpu":"2482m","memory":"7984Mi","pods":"26"}`,
   342  					"node.alpha.kubernetes.io/ttl":                           "0",
   343  					"volumes.kubernetes.io/controller-managed-attach-detach": "true",
   344  				},
   345  			},
   346  			wantFmtStr: `flannel.alpha.coreos.com/backend-data="{\"VNI\":1,\"VtepMAC\":\"ce:f9:c7:a4:de:64\"}"
   347  flannel.alpha.coreos.com/backend-type="vxlan"
   348  flannel.alpha.coreos.com/kube-subnet-manager="true"
   349  flannel.alpha.coreos.com/public-ip="192.168.19.160"
   350  management.cattle.io/pod-limits="{\"cpu\":\"11400m\",\"memory\":\"7965Mi\"}"
   351  management.cattle.io/pod-requests="{\"cpu\":\"2482m\",\"memory\":\"7984Mi\",\"pods\":\"26\"}"
   352  node.alpha.kubernetes.io/ttl="0"
   353  volumes.kubernetes.io/controller-managed-attach-detach="true"`,
   354  		},
   355  	}
   356  	for _, tt := range tests {
   357  		t.Run(tt.name, func(t *testing.T) {
   358  			if gotFmtStr := FormatMap(tt.args.m); gotFmtStr != tt.wantFmtStr {
   359  				t.Errorf("FormatMap() = %v, want %v", gotFmtStr, tt.wantFmtStr)
   360  			}
   361  		})
   362  	}
   363  }
   364  

View as plain text