...

Source file src/k8s.io/kubectl/pkg/cmd/portforward/portforward_test.go

Documentation: k8s.io/kubectl/pkg/cmd/portforward

     1  /*
     2  Copyright 2014 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 portforward
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"net/url"
    24  	"reflect"
    25  	"testing"
    26  
    27  	"github.com/spf13/cobra"
    28  
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	"k8s.io/apimachinery/pkg/util/intstr"
    33  	"k8s.io/cli-runtime/pkg/genericiooptions"
    34  	"k8s.io/client-go/rest/fake"
    35  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    36  	"k8s.io/kubectl/pkg/scheme"
    37  )
    38  
    39  type fakePortForwarder struct {
    40  	method string
    41  	url    *url.URL
    42  	pfErr  error
    43  }
    44  
    45  func (f *fakePortForwarder) ForwardPorts(method string, url *url.URL, opts PortForwardOptions) error {
    46  	f.method = method
    47  	f.url = url
    48  	return f.pfErr
    49  }
    50  
    51  func testPortForward(t *testing.T, flags map[string]string, args []string) {
    52  	version := "v1"
    53  
    54  	tests := []struct {
    55  		name            string
    56  		podPath, pfPath string
    57  		pod             *corev1.Pod
    58  		pfErr           bool
    59  	}{
    60  		{
    61  			name:    "pod portforward",
    62  			podPath: "/api/" + version + "/namespaces/test/pods/foo",
    63  			pfPath:  "/api/" + version + "/namespaces/test/pods/foo/portforward",
    64  			pod:     execPod(),
    65  		},
    66  		{
    67  			name:    "pod portforward error",
    68  			podPath: "/api/" + version + "/namespaces/test/pods/foo",
    69  			pfPath:  "/api/" + version + "/namespaces/test/pods/foo/portforward",
    70  			pod:     execPod(),
    71  			pfErr:   true,
    72  		},
    73  	}
    74  	for _, test := range tests {
    75  		t.Run(test.name, func(t *testing.T) {
    76  			var err error
    77  			tf := cmdtesting.NewTestFactory().WithNamespace("test")
    78  			defer tf.Cleanup()
    79  
    80  			codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
    81  			ns := scheme.Codecs.WithoutConversion()
    82  
    83  			tf.Client = &fake.RESTClient{
    84  				VersionedAPIPath:     "/api/v1",
    85  				GroupVersion:         schema.GroupVersion{Group: "", Version: "v1"},
    86  				NegotiatedSerializer: ns,
    87  				Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
    88  					switch p, m := req.URL.Path, req.Method; {
    89  					case p == test.podPath && m == "GET":
    90  						body := cmdtesting.ObjBody(codec, test.pod)
    91  						return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
    92  					default:
    93  						t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req)
    94  						return nil, nil
    95  					}
    96  				}),
    97  			}
    98  			tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
    99  			ff := &fakePortForwarder{}
   100  			if test.pfErr {
   101  				ff.pfErr = fmt.Errorf("pf error")
   102  			}
   103  
   104  			opts := &PortForwardOptions{}
   105  			ctx, cancel := context.WithCancel(context.Background())
   106  			defer cancel()
   107  			cmd := NewCmdPortForward(tf, genericiooptions.NewTestIOStreamsDiscard())
   108  			cmd.Run = func(cmd *cobra.Command, args []string) {
   109  				if err = opts.Complete(tf, cmd, args); err != nil {
   110  					return
   111  				}
   112  				opts.PortForwarder = ff
   113  				if err = opts.Validate(); err != nil {
   114  					return
   115  				}
   116  				err = opts.RunPortForwardContext(ctx)
   117  			}
   118  
   119  			for name, value := range flags {
   120  				cmd.Flags().Set(name, value)
   121  			}
   122  			cmd.Run(cmd, args)
   123  
   124  			if test.pfErr && err != ff.pfErr {
   125  				t.Errorf("%s: Unexpected port-forward error: %v", test.name, err)
   126  			}
   127  			if !test.pfErr && err != nil {
   128  				t.Errorf("%s: Unexpected error: %v", test.name, err)
   129  			}
   130  			if test.pfErr {
   131  				return
   132  			}
   133  
   134  			if ff.url == nil || ff.url.Path != test.pfPath {
   135  				t.Errorf("%s: Did not get expected path for portforward request", test.name)
   136  			}
   137  			if ff.method != "POST" {
   138  				t.Errorf("%s: Did not get method for attach request: %s", test.name, ff.method)
   139  			}
   140  		})
   141  	}
   142  }
   143  
   144  func TestPortForward(t *testing.T) {
   145  	testPortForward(t, nil, []string{"foo", ":5000", ":1000"})
   146  }
   147  
   148  func TestTranslateServicePortToTargetPort(t *testing.T) {
   149  	cases := []struct {
   150  		name       string
   151  		svc        corev1.Service
   152  		pod        corev1.Pod
   153  		ports      []string
   154  		translated []string
   155  		err        bool
   156  	}{
   157  		{
   158  			name: "test success 1 (int port)",
   159  			svc: corev1.Service{
   160  				Spec: corev1.ServiceSpec{
   161  					Ports: []corev1.ServicePort{
   162  						{
   163  							Port:       80,
   164  							TargetPort: intstr.FromInt32(8080),
   165  						},
   166  					},
   167  				},
   168  			},
   169  			pod: corev1.Pod{
   170  				Spec: corev1.PodSpec{
   171  					Containers: []corev1.Container{
   172  						{
   173  							Ports: []corev1.ContainerPort{
   174  								{
   175  									Name:          "http",
   176  									ContainerPort: int32(8080)},
   177  							},
   178  						},
   179  					},
   180  				},
   181  			},
   182  			ports:      []string{"80"},
   183  			translated: []string{"80:8080"},
   184  			err:        false,
   185  		},
   186  		{
   187  			name: "test success 1 (int port with random local port)",
   188  			svc: corev1.Service{
   189  				Spec: corev1.ServiceSpec{
   190  					Ports: []corev1.ServicePort{
   191  						{
   192  							Port:       80,
   193  							TargetPort: intstr.FromInt32(8080),
   194  						},
   195  					},
   196  				},
   197  			},
   198  			pod: corev1.Pod{
   199  				Spec: corev1.PodSpec{
   200  					Containers: []corev1.Container{
   201  						{
   202  							Ports: []corev1.ContainerPort{
   203  								{
   204  									Name:          "http",
   205  									ContainerPort: int32(8080)},
   206  							},
   207  						},
   208  					},
   209  				},
   210  			},
   211  			ports:      []string{":80"},
   212  			translated: []string{":8080"},
   213  			err:        false,
   214  		},
   215  		{
   216  			name: "test success 1 (int port with explicit local port)",
   217  			svc: corev1.Service{
   218  				Spec: corev1.ServiceSpec{
   219  					Ports: []corev1.ServicePort{
   220  						{
   221  							Port:       8080,
   222  							TargetPort: intstr.FromInt32(8080),
   223  						},
   224  					},
   225  				},
   226  			},
   227  			pod: corev1.Pod{
   228  				Spec: corev1.PodSpec{
   229  					Containers: []corev1.Container{
   230  						{
   231  							Ports: []corev1.ContainerPort{
   232  								{
   233  									Name:          "http",
   234  									ContainerPort: int32(8080)},
   235  							},
   236  						},
   237  					},
   238  				},
   239  			},
   240  			ports:      []string{"8000:8080"},
   241  			translated: []string{"8000:8080"},
   242  			err:        false,
   243  		},
   244  		{
   245  			name: "test success 2 (clusterIP: None)",
   246  			svc: corev1.Service{
   247  				Spec: corev1.ServiceSpec{
   248  					ClusterIP: "None",
   249  					Ports: []corev1.ServicePort{
   250  						{
   251  							Port:       80,
   252  							TargetPort: intstr.FromInt32(8080),
   253  						},
   254  					},
   255  				},
   256  			},
   257  			pod: corev1.Pod{
   258  				Spec: corev1.PodSpec{
   259  					Containers: []corev1.Container{
   260  						{
   261  							Ports: []corev1.ContainerPort{
   262  								{
   263  									Name:          "http",
   264  									ContainerPort: int32(8080)},
   265  							},
   266  						},
   267  					},
   268  				},
   269  			},
   270  			ports:      []string{"80"},
   271  			translated: []string{"80"},
   272  			err:        false,
   273  		},
   274  		{
   275  			name: "test success 2 (clusterIP: None with random local port)",
   276  			svc: corev1.Service{
   277  				Spec: corev1.ServiceSpec{
   278  					ClusterIP: "None",
   279  					Ports: []corev1.ServicePort{
   280  						{
   281  							Port:       80,
   282  							TargetPort: intstr.FromInt32(8080),
   283  						},
   284  					},
   285  				},
   286  			},
   287  			pod: corev1.Pod{
   288  				Spec: corev1.PodSpec{
   289  					Containers: []corev1.Container{
   290  						{
   291  							Ports: []corev1.ContainerPort{
   292  								{
   293  									Name:          "http",
   294  									ContainerPort: int32(8080)},
   295  							},
   296  						},
   297  					},
   298  				},
   299  			},
   300  			ports:      []string{":80"},
   301  			translated: []string{":80"},
   302  			err:        false,
   303  		},
   304  		{
   305  			name: "test success 3 (named target port)",
   306  			svc: corev1.Service{
   307  				Spec: corev1.ServiceSpec{
   308  					Ports: []corev1.ServicePort{
   309  						{
   310  							Port:       80,
   311  							TargetPort: intstr.FromString("http"),
   312  						},
   313  						{
   314  							Port:       443,
   315  							TargetPort: intstr.FromString("https"),
   316  						},
   317  					},
   318  				},
   319  			},
   320  			pod: corev1.Pod{
   321  				Spec: corev1.PodSpec{
   322  					Containers: []corev1.Container{
   323  						{
   324  							Ports: []corev1.ContainerPort{
   325  								{
   326  									Name:          "http",
   327  									ContainerPort: int32(8080)},
   328  								{
   329  									Name:          "https",
   330  									ContainerPort: int32(8443)},
   331  							},
   332  						},
   333  					},
   334  				},
   335  			},
   336  			ports:      []string{"80", "443"},
   337  			translated: []string{"80:8080", "443:8443"},
   338  			err:        false,
   339  		},
   340  		{
   341  			name: "test success 3 (named target port with random local port)",
   342  			svc: corev1.Service{
   343  				Spec: corev1.ServiceSpec{
   344  					Ports: []corev1.ServicePort{
   345  						{
   346  							Port:       80,
   347  							TargetPort: intstr.FromString("http"),
   348  						},
   349  						{
   350  							Port:       443,
   351  							TargetPort: intstr.FromString("https"),
   352  						},
   353  					},
   354  				},
   355  			},
   356  			pod: corev1.Pod{
   357  				Spec: corev1.PodSpec{
   358  					Containers: []corev1.Container{
   359  						{
   360  							Ports: []corev1.ContainerPort{
   361  								{
   362  									Name:          "http",
   363  									ContainerPort: int32(8080)},
   364  								{
   365  									Name:          "https",
   366  									ContainerPort: int32(8443)},
   367  							},
   368  						},
   369  					},
   370  				},
   371  			},
   372  			ports:      []string{":80", ":443"},
   373  			translated: []string{":8080", ":8443"},
   374  			err:        false,
   375  		},
   376  		{
   377  			name: "test success 4 (named service port)",
   378  			svc: corev1.Service{
   379  				Spec: corev1.ServiceSpec{
   380  					Ports: []corev1.ServicePort{
   381  						{
   382  							Port:       80,
   383  							Name:       "http",
   384  							TargetPort: intstr.FromInt32(8080),
   385  						},
   386  						{
   387  							Port:       443,
   388  							Name:       "https",
   389  							TargetPort: intstr.FromInt32(8443),
   390  						},
   391  					},
   392  				},
   393  			},
   394  			pod: corev1.Pod{
   395  				Spec: corev1.PodSpec{
   396  					Containers: []corev1.Container{
   397  						{
   398  							Ports: []corev1.ContainerPort{
   399  								{
   400  									ContainerPort: int32(8080)},
   401  								{
   402  									ContainerPort: int32(8443)},
   403  							},
   404  						},
   405  					},
   406  				},
   407  			},
   408  			ports:      []string{"http", "https"},
   409  			translated: []string{"80:8080", "443:8443"},
   410  			err:        false,
   411  		},
   412  		{
   413  			name: "test success 4 (named service port with random local port)",
   414  			svc: corev1.Service{
   415  				Spec: corev1.ServiceSpec{
   416  					Ports: []corev1.ServicePort{
   417  						{
   418  							Port:       80,
   419  							Name:       "http",
   420  							TargetPort: intstr.FromInt32(8080),
   421  						},
   422  						{
   423  							Port:       443,
   424  							Name:       "https",
   425  							TargetPort: intstr.FromInt32(8443),
   426  						},
   427  					},
   428  				},
   429  			},
   430  			pod: corev1.Pod{
   431  				Spec: corev1.PodSpec{
   432  					Containers: []corev1.Container{
   433  						{
   434  							Ports: []corev1.ContainerPort{
   435  								{
   436  									ContainerPort: int32(8080)},
   437  								{
   438  									ContainerPort: int32(8443)},
   439  							},
   440  						},
   441  					},
   442  				},
   443  			},
   444  			ports:      []string{":http", ":https"},
   445  			translated: []string{":8080", ":8443"},
   446  			err:        false,
   447  		},
   448  		{
   449  			name: "test success 4 (named service port and named pod container port)",
   450  			svc: corev1.Service{
   451  				Spec: corev1.ServiceSpec{
   452  					Ports: []corev1.ServicePort{
   453  						{
   454  							Port:       80,
   455  							Name:       "http",
   456  							TargetPort: intstr.FromString("http"),
   457  						},
   458  					},
   459  				},
   460  			},
   461  			pod: corev1.Pod{
   462  				Spec: corev1.PodSpec{
   463  					Containers: []corev1.Container{
   464  						{
   465  							Ports: []corev1.ContainerPort{
   466  								{
   467  									Name:          "http",
   468  									ContainerPort: int32(80)},
   469  							},
   470  						},
   471  					},
   472  				},
   473  			},
   474  			ports:      []string{"http"},
   475  			translated: []string{"80"},
   476  			err:        false,
   477  		},
   478  		{
   479  			name: "test success (targetPort omitted)",
   480  			svc: corev1.Service{
   481  				Spec: corev1.ServiceSpec{
   482  					Ports: []corev1.ServicePort{
   483  						{
   484  							Port: 80,
   485  						},
   486  					},
   487  				},
   488  			},
   489  			pod: corev1.Pod{
   490  				Spec: corev1.PodSpec{
   491  					Containers: []corev1.Container{
   492  						{
   493  							Ports: []corev1.ContainerPort{
   494  								{
   495  									Name:          "http",
   496  									ContainerPort: int32(80)},
   497  							},
   498  						},
   499  					},
   500  				},
   501  			},
   502  			ports:      []string{"80"},
   503  			translated: []string{"80"},
   504  			err:        false,
   505  		},
   506  		{
   507  			name: "test success (targetPort omitted with random local port)",
   508  			svc: corev1.Service{
   509  				Spec: corev1.ServiceSpec{
   510  					Ports: []corev1.ServicePort{
   511  						{
   512  							Port: 80,
   513  						},
   514  					},
   515  				},
   516  			},
   517  			pod: corev1.Pod{
   518  				Spec: corev1.PodSpec{
   519  					Containers: []corev1.Container{
   520  						{
   521  							Ports: []corev1.ContainerPort{
   522  								{
   523  									Name:          "http",
   524  									ContainerPort: int32(80)},
   525  							},
   526  						},
   527  					},
   528  				},
   529  			},
   530  			ports:      []string{":80"},
   531  			translated: []string{":80"},
   532  			err:        false,
   533  		},
   534  		{
   535  			name: "test failure 1 (named target port lookup failure)",
   536  			svc: corev1.Service{
   537  				Spec: corev1.ServiceSpec{
   538  					Ports: []corev1.ServicePort{
   539  						{
   540  							Port:       80,
   541  							TargetPort: intstr.FromString("http"),
   542  						},
   543  					},
   544  				},
   545  			},
   546  			pod: corev1.Pod{
   547  				Spec: corev1.PodSpec{
   548  					Containers: []corev1.Container{
   549  						{
   550  							Ports: []corev1.ContainerPort{
   551  								{
   552  									Name:          "https",
   553  									ContainerPort: int32(443)},
   554  							},
   555  						},
   556  					},
   557  				},
   558  			},
   559  			ports:      []string{"80"},
   560  			translated: []string{},
   561  			err:        true,
   562  		},
   563  		{
   564  			name: "test failure 1 (named service port lookup failure)",
   565  			svc: corev1.Service{
   566  				Spec: corev1.ServiceSpec{
   567  					Ports: []corev1.ServicePort{
   568  						{
   569  							Port:       80,
   570  							TargetPort: intstr.FromString("http"),
   571  						},
   572  					},
   573  				},
   574  			},
   575  			pod: corev1.Pod{
   576  				Spec: corev1.PodSpec{
   577  					Containers: []corev1.Container{
   578  						{
   579  							Ports: []corev1.ContainerPort{
   580  								{
   581  									Name:          "http",
   582  									ContainerPort: int32(8080)},
   583  							},
   584  						},
   585  					},
   586  				},
   587  			},
   588  			ports:      []string{"https"},
   589  			translated: []string{},
   590  			err:        true,
   591  		},
   592  		{
   593  			name: "test failure 2 (service port not declared)",
   594  			svc: corev1.Service{
   595  				Spec: corev1.ServiceSpec{
   596  					Ports: []corev1.ServicePort{
   597  						{
   598  							Port:       80,
   599  							TargetPort: intstr.FromString("http"),
   600  						},
   601  					},
   602  				},
   603  			},
   604  			pod: corev1.Pod{
   605  				Spec: corev1.PodSpec{
   606  					Containers: []corev1.Container{
   607  						{
   608  							Ports: []corev1.ContainerPort{
   609  								{
   610  									Name:          "https",
   611  									ContainerPort: int32(443)},
   612  							},
   613  						},
   614  					},
   615  				},
   616  			},
   617  			ports:      []string{"443"},
   618  			translated: []string{},
   619  			err:        true,
   620  		},
   621  	}
   622  
   623  	for _, tc := range cases {
   624  		translated, err := translateServicePortToTargetPort(tc.ports, tc.svc, tc.pod)
   625  		if err != nil {
   626  			if tc.err {
   627  				continue
   628  			}
   629  
   630  			t.Errorf("%v: unexpected error: %v", tc.name, err)
   631  			continue
   632  		}
   633  
   634  		if tc.err {
   635  			t.Errorf("%v: unexpected success", tc.name)
   636  			continue
   637  		}
   638  
   639  		if !reflect.DeepEqual(translated, tc.translated) {
   640  			t.Errorf("%v: expected %v; got %v", tc.name, tc.translated, translated)
   641  		}
   642  	}
   643  }
   644  
   645  func execPod() *corev1.Pod {
   646  	return &corev1.Pod{
   647  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
   648  		Spec: corev1.PodSpec{
   649  			RestartPolicy: corev1.RestartPolicyAlways,
   650  			DNSPolicy:     corev1.DNSClusterFirst,
   651  			Containers: []corev1.Container{
   652  				{
   653  					Name: "bar",
   654  				},
   655  			},
   656  		},
   657  		Status: corev1.PodStatus{
   658  			Phase: corev1.PodRunning,
   659  		},
   660  	}
   661  }
   662  
   663  func TestConvertPodNamedPortToNumber(t *testing.T) {
   664  	cases := []struct {
   665  		name      string
   666  		pod       corev1.Pod
   667  		ports     []string
   668  		converted []string
   669  		err       bool
   670  	}{
   671  		{
   672  			name: "port number without local port",
   673  			pod: corev1.Pod{
   674  				Spec: corev1.PodSpec{
   675  					Containers: []corev1.Container{
   676  						{
   677  							Ports: []corev1.ContainerPort{
   678  								{
   679  									Name:          "http",
   680  									ContainerPort: int32(80)},
   681  							},
   682  						},
   683  					},
   684  				},
   685  			},
   686  			ports:     []string{"80"},
   687  			converted: []string{"80"},
   688  			err:       false,
   689  		},
   690  		{
   691  			name: "port number with local port",
   692  			pod: corev1.Pod{
   693  				Spec: corev1.PodSpec{
   694  					Containers: []corev1.Container{
   695  						{
   696  							Ports: []corev1.ContainerPort{
   697  								{
   698  									Name:          "http",
   699  									ContainerPort: int32(80)},
   700  							},
   701  						},
   702  					},
   703  				},
   704  			},
   705  			ports:     []string{"8000:80"},
   706  			converted: []string{"8000:80"},
   707  			err:       false,
   708  		},
   709  		{
   710  			name: "port number with random local port",
   711  			pod: corev1.Pod{
   712  				Spec: corev1.PodSpec{
   713  					Containers: []corev1.Container{
   714  						{
   715  							Ports: []corev1.ContainerPort{
   716  								{
   717  									Name:          "http",
   718  									ContainerPort: int32(80)},
   719  							},
   720  						},
   721  					},
   722  				},
   723  			},
   724  			ports:     []string{":80"},
   725  			converted: []string{":80"},
   726  			err:       false,
   727  		},
   728  		{
   729  			name: "named port without local port",
   730  			pod: corev1.Pod{
   731  				Spec: corev1.PodSpec{
   732  					Containers: []corev1.Container{
   733  						{
   734  							Ports: []corev1.ContainerPort{
   735  								{
   736  									Name:          "http",
   737  									ContainerPort: int32(80)},
   738  							},
   739  						},
   740  					},
   741  				},
   742  			},
   743  			ports:     []string{"http"},
   744  			converted: []string{"80"},
   745  			err:       false,
   746  		},
   747  		{
   748  			name: "named port with local port",
   749  			pod: corev1.Pod{
   750  				Spec: corev1.PodSpec{
   751  					Containers: []corev1.Container{
   752  						{
   753  							Ports: []corev1.ContainerPort{
   754  								{
   755  									Name:          "http",
   756  									ContainerPort: int32(80)},
   757  							},
   758  						},
   759  					},
   760  				},
   761  			},
   762  			ports:     []string{"8000:http"},
   763  			converted: []string{"8000:80"},
   764  			err:       false,
   765  		},
   766  		{
   767  			name: "named port with random local port",
   768  			pod: corev1.Pod{
   769  				Spec: corev1.PodSpec{
   770  					Containers: []corev1.Container{
   771  						{
   772  							Ports: []corev1.ContainerPort{
   773  								{
   774  									Name:          "http",
   775  									ContainerPort: int32(80)},
   776  							},
   777  						},
   778  					},
   779  				},
   780  			},
   781  			ports:     []string{":http"},
   782  			converted: []string{":80"},
   783  			err:       false,
   784  		},
   785  		{
   786  			name: "named port can not be found",
   787  			pod: corev1.Pod{
   788  				Spec: corev1.PodSpec{
   789  					Containers: []corev1.Container{
   790  						{
   791  							Ports: []corev1.ContainerPort{
   792  								{
   793  									Name:          "https",
   794  									ContainerPort: int32(443)},
   795  							},
   796  						},
   797  					},
   798  				},
   799  			},
   800  			ports: []string{"http"},
   801  			err:   true,
   802  		},
   803  		{
   804  			name: "one of the requested named ports can not be found",
   805  			pod: corev1.Pod{
   806  				Spec: corev1.PodSpec{
   807  					Containers: []corev1.Container{
   808  						{
   809  							Ports: []corev1.ContainerPort{
   810  								{
   811  									Name:          "https",
   812  									ContainerPort: int32(443)},
   813  							},
   814  						},
   815  					},
   816  				},
   817  			},
   818  			ports: []string{"https", "http"},
   819  			err:   true,
   820  		},
   821  	}
   822  
   823  	for _, tc := range cases {
   824  		converted, err := convertPodNamedPortToNumber(tc.ports, tc.pod)
   825  		if err != nil {
   826  			if tc.err {
   827  				continue
   828  			}
   829  
   830  			t.Errorf("%v: unexpected error: %v", tc.name, err)
   831  			continue
   832  		}
   833  
   834  		if tc.err {
   835  			t.Errorf("%v: unexpected success", tc.name)
   836  			continue
   837  		}
   838  
   839  		if !reflect.DeepEqual(converted, tc.converted) {
   840  			t.Errorf("%v: expected %v; got %v", tc.name, tc.converted, converted)
   841  		}
   842  	}
   843  }
   844  
   845  func TestCheckUDPPort(t *testing.T) {
   846  	tests := []struct {
   847  		name        string
   848  		pod         *corev1.Pod
   849  		service     *corev1.Service
   850  		ports       []string
   851  		expectError bool
   852  	}{
   853  		{
   854  			name: "forward to a UDP port in a Pod",
   855  			pod: &corev1.Pod{
   856  				Spec: corev1.PodSpec{
   857  					Containers: []corev1.Container{
   858  						{
   859  							Ports: []corev1.ContainerPort{
   860  								{Protocol: corev1.ProtocolUDP, ContainerPort: 53},
   861  							},
   862  						},
   863  					},
   864  				},
   865  			},
   866  			ports:       []string{"53"},
   867  			expectError: true,
   868  		},
   869  		{
   870  			name: "forward to a named UDP port in a Pod",
   871  			pod: &corev1.Pod{
   872  				Spec: corev1.PodSpec{
   873  					Containers: []corev1.Container{
   874  						{
   875  							Ports: []corev1.ContainerPort{
   876  								{Protocol: corev1.ProtocolUDP, ContainerPort: 53, Name: "dns"},
   877  							},
   878  						},
   879  					},
   880  				},
   881  			},
   882  			ports:       []string{"dns"},
   883  			expectError: true,
   884  		},
   885  		{
   886  			name: "Pod has ports with both TCP and UDP protocol (UDP first)",
   887  			pod: &corev1.Pod{
   888  				Spec: corev1.PodSpec{
   889  					Containers: []corev1.Container{
   890  						{
   891  							Ports: []corev1.ContainerPort{
   892  								{Protocol: corev1.ProtocolUDP, ContainerPort: 53},
   893  								{Protocol: corev1.ProtocolTCP, ContainerPort: 53},
   894  							},
   895  						},
   896  					},
   897  				},
   898  			},
   899  			ports: []string{":53"},
   900  		},
   901  		{
   902  			name: "Pod has ports with both TCP and UDP protocol (TCP first)",
   903  			pod: &corev1.Pod{
   904  				Spec: corev1.PodSpec{
   905  					Containers: []corev1.Container{
   906  						{
   907  							Ports: []corev1.ContainerPort{
   908  								{Protocol: corev1.ProtocolTCP, ContainerPort: 53},
   909  								{Protocol: corev1.ProtocolUDP, ContainerPort: 53},
   910  							},
   911  						},
   912  					},
   913  				},
   914  			},
   915  			ports: []string{":53"},
   916  		},
   917  
   918  		{
   919  			name: "forward to a UDP port in a Service",
   920  			service: &corev1.Service{
   921  				Spec: corev1.ServiceSpec{
   922  					Ports: []corev1.ServicePort{
   923  						{Protocol: corev1.ProtocolUDP, Port: 53},
   924  					},
   925  				},
   926  			},
   927  			ports:       []string{"53"},
   928  			expectError: true,
   929  		},
   930  		{
   931  			name: "forward to a named UDP port in a Service",
   932  			service: &corev1.Service{
   933  				Spec: corev1.ServiceSpec{
   934  					Ports: []corev1.ServicePort{
   935  						{Protocol: corev1.ProtocolUDP, Port: 53, Name: "dns"},
   936  					},
   937  				},
   938  			},
   939  			ports:       []string{"10053:dns"},
   940  			expectError: true,
   941  		},
   942  		{
   943  			name: "Service has ports with both TCP and UDP protocol (UDP first)",
   944  			service: &corev1.Service{
   945  				Spec: corev1.ServiceSpec{
   946  					Ports: []corev1.ServicePort{
   947  						{Protocol: corev1.ProtocolUDP, Port: 53},
   948  						{Protocol: corev1.ProtocolTCP, Port: 53},
   949  					},
   950  				},
   951  			},
   952  			ports: []string{"53"},
   953  		},
   954  		{
   955  			name: "Service has ports with both TCP and UDP protocol (TCP first)",
   956  			service: &corev1.Service{
   957  				Spec: corev1.ServiceSpec{
   958  					Ports: []corev1.ServicePort{
   959  						{Protocol: corev1.ProtocolTCP, Port: 53},
   960  						{Protocol: corev1.ProtocolUDP, Port: 53},
   961  					},
   962  				},
   963  			},
   964  			ports: []string{"53"},
   965  		},
   966  	}
   967  	for _, tc := range tests {
   968  		var err error
   969  		if tc.pod != nil {
   970  			err = checkUDPPortInPod(tc.ports, tc.pod)
   971  		} else if tc.service != nil {
   972  			err = checkUDPPortInService(tc.ports, tc.service)
   973  		}
   974  		if err != nil {
   975  			if tc.expectError {
   976  				continue
   977  			}
   978  			t.Errorf("%v: unexpected error: %v", tc.name, err)
   979  			continue
   980  		}
   981  		if tc.expectError {
   982  			t.Errorf("%v: unexpected success", tc.name)
   983  		}
   984  	}
   985  }
   986  

View as plain text