...

Source file src/k8s.io/kubectl/pkg/polymorphichelpers/logsforobject_test.go

Documentation: k8s.io/kubectl/pkg/polymorphichelpers

     1  /*
     2  Copyright 2017 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 polymorphichelpers
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	appsv1 "k8s.io/api/apps/v1"
    27  	batchv1 "k8s.io/api/batch/v1"
    28  	corev1 "k8s.io/api/core/v1"
    29  	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	fakeexternal "k8s.io/client-go/kubernetes/fake"
    34  	testclient "k8s.io/client-go/testing"
    35  	"k8s.io/kubectl/pkg/cmd/util/podcmd"
    36  )
    37  
    38  var (
    39  	podsResource = schema.GroupVersionResource{Version: "v1", Resource: "pods"}
    40  	podsKind     = schema.GroupVersionKind{Version: "v1", Kind: "Pod"}
    41  )
    42  
    43  func TestLogsForObject(t *testing.T) {
    44  	tests := []struct {
    45  		name          string
    46  		obj           runtime.Object
    47  		opts          *corev1.PodLogOptions
    48  		allContainers bool
    49  		clientsetPods []runtime.Object
    50  		actions       []testclient.Action
    51  
    52  		expectedErr     string
    53  		expectedSources []corev1.ObjectReference
    54  	}{
    55  		{
    56  			name: "pod logs",
    57  			obj:  testPodWithOneContainers(),
    58  			actions: []testclient.Action{
    59  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
    60  			},
    61  			expectedSources: []corev1.ObjectReference{
    62  				{
    63  					Kind:       testPodWithOneContainers().Kind,
    64  					APIVersion: testPodWithOneContainers().APIVersion,
    65  					Name:       testPodWithOneContainers().Name,
    66  					Namespace:  testPodWithOneContainers().Namespace,
    67  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
    68  				},
    69  			},
    70  		},
    71  		{
    72  			name:          "pod logs: all containers",
    73  			obj:           testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers(),
    74  			opts:          &corev1.PodLogOptions{},
    75  			allContainers: true,
    76  			actions: []testclient.Action{
    77  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-and-1-initc1"}),
    78  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-and-1-initc2"}),
    79  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-and-1-c1"}),
    80  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-and-1-c2"}),
    81  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-and-1-e1"}),
    82  			},
    83  			expectedSources: []corev1.ObjectReference{
    84  				{
    85  					Kind:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Kind,
    86  					APIVersion: testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().APIVersion,
    87  					Name:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Name,
    88  					Namespace:  testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Namespace,
    89  					FieldPath:  fmt.Sprintf("spec.initContainers{%s}", testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Spec.InitContainers[0].Name),
    90  				},
    91  				{
    92  					Kind:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Kind,
    93  					APIVersion: testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().APIVersion,
    94  					Name:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Name,
    95  					Namespace:  testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Namespace,
    96  					FieldPath:  fmt.Sprintf("spec.initContainers{%s}", testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Spec.InitContainers[1].Name),
    97  				},
    98  				{
    99  					Kind:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Kind,
   100  					APIVersion: testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().APIVersion,
   101  					Name:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Name,
   102  					Namespace:  testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Namespace,
   103  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Spec.Containers[0].Name),
   104  				},
   105  				{
   106  					Kind:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Kind,
   107  					APIVersion: testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().APIVersion,
   108  					Name:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Name,
   109  					Namespace:  testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Namespace,
   110  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Spec.Containers[1].Name),
   111  				},
   112  				{
   113  					Kind:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Kind,
   114  					APIVersion: testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().APIVersion,
   115  					Name:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Name,
   116  					Namespace:  testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Namespace,
   117  					FieldPath:  fmt.Sprintf("spec.ephemeralContainers{%s}", testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Spec.EphemeralContainers[0].Name),
   118  				},
   119  			},
   120  		},
   121  		{
   122  			name: "pod logs: default to first container",
   123  			obj:  testPodWithTwoContainers(),
   124  			actions: []testclient.Action{
   125  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-c1"}),
   126  			},
   127  			expectedSources: []corev1.ObjectReference{
   128  				{
   129  					Kind:       testPodWithTwoContainers().Kind,
   130  					APIVersion: testPodWithTwoContainers().APIVersion,
   131  					Name:       testPodWithTwoContainers().Name,
   132  					Namespace:  testPodWithTwoContainers().Namespace,
   133  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithTwoContainers().Spec.Containers[0].Name),
   134  				},
   135  			},
   136  		},
   137  		{
   138  			name: "pods list logs",
   139  			obj: &corev1.PodList{
   140  				Items: []corev1.Pod{*testPodWithOneContainers()},
   141  			},
   142  			actions: []testclient.Action{
   143  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   144  			},
   145  			expectedSources: []corev1.ObjectReference{{
   146  				Kind:       testPodWithOneContainers().Kind,
   147  				APIVersion: testPodWithOneContainers().APIVersion,
   148  				Name:       testPodWithOneContainers().Name,
   149  				Namespace:  testPodWithOneContainers().Namespace,
   150  				FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
   151  			}},
   152  		},
   153  		{
   154  			name: "pods list logs: default container should not leak across pods",
   155  			obj: &corev1.PodList{
   156  				Items: []corev1.Pod{
   157  					{
   158  						TypeMeta: metav1.TypeMeta{
   159  							Kind:       "pod",
   160  							APIVersion: "v1",
   161  						},
   162  						ObjectMeta: metav1.ObjectMeta{
   163  							Name:      "foo",
   164  							Namespace: "test",
   165  							Labels:    map[string]string{"test": "logs"},
   166  							Annotations: map[string]string{
   167  								"kubectl.kubernetes.io/default-container": "c1",
   168  							},
   169  						},
   170  						Spec: corev1.PodSpec{
   171  							RestartPolicy: corev1.RestartPolicyAlways,
   172  							DNSPolicy:     corev1.DNSClusterFirst,
   173  							Containers: []corev1.Container{
   174  								{Name: "c1"},
   175  								{Name: "c2"},
   176  							},
   177  						},
   178  					},
   179  					{
   180  						TypeMeta: metav1.TypeMeta{
   181  							Kind:       "pod",
   182  							APIVersion: "v1",
   183  						},
   184  						ObjectMeta: metav1.ObjectMeta{
   185  							Name:      "bar",
   186  							Namespace: "test",
   187  							Labels:    map[string]string{"test": "logs"},
   188  						},
   189  						Spec: corev1.PodSpec{
   190  							RestartPolicy: corev1.RestartPolicyAlways,
   191  							DNSPolicy:     corev1.DNSClusterFirst,
   192  							Containers: []corev1.Container{
   193  								{Name: "c2"},
   194  							},
   195  						},
   196  					},
   197  				},
   198  			},
   199  			actions: []testclient.Action{
   200  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   201  				getLogsAction("test", &corev1.PodLogOptions{Container: "c2"}),
   202  			},
   203  			expectedSources: []corev1.ObjectReference{{
   204  				Kind:       "pod",
   205  				APIVersion: "v1",
   206  				Name:       "foo",
   207  				Namespace:  "test",
   208  				FieldPath:  fmt.Sprintf("spec.containers{%s}", "c1"),
   209  			}, {
   210  				Kind:       "pod",
   211  				APIVersion: "v1",
   212  				Name:       "bar",
   213  				Namespace:  "test",
   214  				FieldPath:  fmt.Sprintf("spec.containers{%s}", "c2"),
   215  			}},
   216  		},
   217  		{
   218  			name: "pods list logs: all containers",
   219  			obj: &corev1.PodList{
   220  				Items: []corev1.Pod{*testPodWithTwoContainersAndTwoInitContainers()},
   221  			},
   222  			opts:          &corev1.PodLogOptions{},
   223  			allContainers: true,
   224  			actions: []testclient.Action{
   225  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-initc1"}),
   226  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-initc2"}),
   227  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-c1"}),
   228  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-c2"}),
   229  			},
   230  			expectedSources: []corev1.ObjectReference{
   231  				{
   232  					Kind:       testPodWithTwoContainersAndTwoInitContainers().Kind,
   233  					APIVersion: testPodWithTwoContainersAndTwoInitContainers().APIVersion,
   234  					Name:       testPodWithTwoContainersAndTwoInitContainers().Name,
   235  					Namespace:  testPodWithTwoContainersAndTwoInitContainers().Namespace,
   236  					FieldPath:  fmt.Sprintf("spec.initContainers{%s}", testPodWithTwoContainersAndTwoInitContainers().Spec.InitContainers[0].Name),
   237  				},
   238  				{
   239  					Kind:       testPodWithTwoContainersAndTwoInitContainers().Kind,
   240  					APIVersion: testPodWithTwoContainersAndTwoInitContainers().APIVersion,
   241  					Name:       testPodWithTwoContainersAndTwoInitContainers().Name,
   242  					Namespace:  testPodWithTwoContainersAndTwoInitContainers().Namespace,
   243  					FieldPath:  fmt.Sprintf("spec.initContainers{%s}", testPodWithTwoContainersAndTwoInitContainers().Spec.InitContainers[1].Name),
   244  				},
   245  				{
   246  					Kind:       testPodWithTwoContainersAndTwoInitContainers().Kind,
   247  					APIVersion: testPodWithTwoContainersAndTwoInitContainers().APIVersion,
   248  					Name:       testPodWithTwoContainersAndTwoInitContainers().Name,
   249  					Namespace:  testPodWithTwoContainersAndTwoInitContainers().Namespace,
   250  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithTwoContainersAndTwoInitContainers().Spec.Containers[0].Name),
   251  				},
   252  				{
   253  					Kind:       testPodWithTwoContainersAndTwoInitContainers().Kind,
   254  					APIVersion: testPodWithTwoContainersAndTwoInitContainers().APIVersion,
   255  					Name:       testPodWithTwoContainersAndTwoInitContainers().Name,
   256  					Namespace:  testPodWithTwoContainersAndTwoInitContainers().Namespace,
   257  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithTwoContainersAndTwoInitContainers().Spec.Containers[1].Name),
   258  				},
   259  			},
   260  		},
   261  		{
   262  			name: "pods list logs: default to first container",
   263  			obj: &corev1.PodList{
   264  				Items: []corev1.Pod{*testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers()},
   265  			},
   266  			actions: []testclient.Action{
   267  				getLogsAction("test", &corev1.PodLogOptions{Container: "foo-2-and-2-and-1-c1"}),
   268  			},
   269  			expectedSources: []corev1.ObjectReference{
   270  				{
   271  					Kind:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Kind,
   272  					APIVersion: testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().APIVersion,
   273  					Name:       testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Name,
   274  					Namespace:  testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Namespace,
   275  					FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers().Spec.Containers[0].Name),
   276  				},
   277  			},
   278  		},
   279  		{
   280  			name: "replication controller logs",
   281  			obj: &corev1.ReplicationController{
   282  				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
   283  				Spec: corev1.ReplicationControllerSpec{
   284  					Selector: map[string]string{"foo": "bar"},
   285  				},
   286  			},
   287  			clientsetPods: []runtime.Object{testPodWithOneContainers()},
   288  			actions: []testclient.Action{
   289  				testclient.NewListAction(podsResource, podsKind, "test", metav1.ListOptions{LabelSelector: "foo=bar"}),
   290  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   291  			},
   292  			expectedSources: []corev1.ObjectReference{{
   293  				Kind:       testPodWithOneContainers().Kind,
   294  				APIVersion: testPodWithOneContainers().APIVersion,
   295  				Name:       testPodWithOneContainers().Name,
   296  				Namespace:  testPodWithOneContainers().Namespace,
   297  				FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
   298  			}},
   299  		},
   300  		{
   301  			name: "replica set logs",
   302  			obj: &extensionsv1beta1.ReplicaSet{
   303  				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
   304  				Spec: extensionsv1beta1.ReplicaSetSpec{
   305  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
   306  				},
   307  			},
   308  			clientsetPods: []runtime.Object{testPodWithOneContainers()},
   309  			actions: []testclient.Action{
   310  				testclient.NewListAction(podsResource, podsKind, "test", metav1.ListOptions{LabelSelector: "foo=bar"}),
   311  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   312  			},
   313  			expectedSources: []corev1.ObjectReference{{
   314  				Kind:       testPodWithOneContainers().Kind,
   315  				APIVersion: testPodWithOneContainers().APIVersion,
   316  				Name:       testPodWithOneContainers().Name,
   317  				Namespace:  testPodWithOneContainers().Namespace,
   318  				FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
   319  			}},
   320  		},
   321  		{
   322  			name: "deployment logs",
   323  			obj: &extensionsv1beta1.Deployment{
   324  				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
   325  				Spec: extensionsv1beta1.DeploymentSpec{
   326  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
   327  				},
   328  			},
   329  			clientsetPods: []runtime.Object{testPodWithOneContainers()},
   330  			actions: []testclient.Action{
   331  				testclient.NewListAction(podsResource, podsKind, "test", metav1.ListOptions{LabelSelector: "foo=bar"}),
   332  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   333  			},
   334  			expectedSources: []corev1.ObjectReference{{
   335  				Kind:       testPodWithOneContainers().Kind,
   336  				APIVersion: testPodWithOneContainers().APIVersion,
   337  				Name:       testPodWithOneContainers().Name,
   338  				Namespace:  testPodWithOneContainers().Namespace,
   339  				FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
   340  			}},
   341  		},
   342  		{
   343  			name: "job logs",
   344  			obj: &batchv1.Job{
   345  				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
   346  				Spec: batchv1.JobSpec{
   347  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
   348  				},
   349  			},
   350  			clientsetPods: []runtime.Object{testPodWithOneContainers()},
   351  			actions: []testclient.Action{
   352  				testclient.NewListAction(podsResource, podsKind, "test", metav1.ListOptions{LabelSelector: "foo=bar"}),
   353  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   354  			},
   355  			expectedSources: []corev1.ObjectReference{{
   356  				Kind:       testPodWithOneContainers().Kind,
   357  				APIVersion: testPodWithOneContainers().APIVersion,
   358  				Name:       testPodWithOneContainers().Name,
   359  				Namespace:  testPodWithOneContainers().Namespace,
   360  				FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
   361  			}},
   362  		},
   363  		{
   364  			name: "stateful set logs",
   365  			obj: &appsv1.StatefulSet{
   366  				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
   367  				Spec: appsv1.StatefulSetSpec{
   368  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
   369  				},
   370  			},
   371  			clientsetPods: []runtime.Object{testPodWithOneContainers()},
   372  			actions: []testclient.Action{
   373  				testclient.NewListAction(podsResource, podsKind, "test", metav1.ListOptions{LabelSelector: "foo=bar"}),
   374  				getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
   375  			},
   376  			expectedSources: []corev1.ObjectReference{{
   377  				Kind:       testPodWithOneContainers().Kind,
   378  				APIVersion: testPodWithOneContainers().APIVersion,
   379  				Name:       testPodWithOneContainers().Name,
   380  				Namespace:  testPodWithOneContainers().Namespace,
   381  				FieldPath:  fmt.Sprintf("spec.containers{%s}", testPodWithOneContainers().Spec.Containers[0].Name),
   382  			}},
   383  		},
   384  	}
   385  
   386  	for _, test := range tests {
   387  		fakeClientset := fakeexternal.NewSimpleClientset(test.clientsetPods...)
   388  		responses, err := logsForObjectWithClient(fakeClientset.CoreV1(), test.obj, test.opts, 20*time.Second, test.allContainers)
   389  		if test.expectedErr == "" && err != nil {
   390  			t.Errorf("%s: unexpected error: %v", test.name, err)
   391  			continue
   392  		}
   393  
   394  		if err != nil && test.expectedErr != err.Error() {
   395  			t.Errorf("%s: expected error: %v, got: %v", test.name, test.expectedErr, err)
   396  			continue
   397  		}
   398  
   399  		if len(test.expectedSources) != len(responses) {
   400  			t.Errorf(
   401  				"%s: the number of expected sources doesn't match the number of responses: %v, got: %v",
   402  				test.name,
   403  				len(test.expectedSources),
   404  				len(responses),
   405  			)
   406  			continue
   407  		}
   408  
   409  		for _, ref := range test.expectedSources {
   410  			if _, ok := responses[ref]; !ok {
   411  				t.Errorf("%s: didn't find expected log source object reference: %#v", test.name, ref)
   412  			}
   413  		}
   414  
   415  		var i int
   416  		for i = range test.actions {
   417  			if len(fakeClientset.Actions()) < i {
   418  				t.Errorf("%s: expected action %d does not exists in actual actions: %#v",
   419  					test.name, i, fakeClientset.Actions())
   420  				continue
   421  			}
   422  			got := fakeClientset.Actions()[i]
   423  			want := test.actions[i]
   424  			if !reflect.DeepEqual(got, want) {
   425  				t.Errorf("%s: unexpected diff for action: %s", test.name, cmp.Diff(got, want))
   426  			}
   427  		}
   428  		for i++; i < len(fakeClientset.Actions()); i++ {
   429  			t.Errorf("%s: actual action %d does not exist in expected: %v", test.name, i, fakeClientset.Actions()[i])
   430  		}
   431  	}
   432  }
   433  
   434  func testPodWithOneContainers() *corev1.Pod {
   435  	return &corev1.Pod{
   436  		TypeMeta: metav1.TypeMeta{
   437  			Kind:       "pod",
   438  			APIVersion: "v1",
   439  		},
   440  		ObjectMeta: metav1.ObjectMeta{
   441  			Name:      "foo",
   442  			Namespace: "test",
   443  			Labels:    map[string]string{"foo": "bar"},
   444  		},
   445  		Spec: corev1.PodSpec{
   446  			RestartPolicy: corev1.RestartPolicyAlways,
   447  			DNSPolicy:     corev1.DNSClusterFirst,
   448  			Containers: []corev1.Container{
   449  				{Name: "c1"},
   450  			},
   451  		},
   452  	}
   453  }
   454  
   455  func testPodWithTwoContainers() *corev1.Pod {
   456  	return &corev1.Pod{
   457  		TypeMeta: metav1.TypeMeta{
   458  			Kind:       "pod",
   459  			APIVersion: "v1",
   460  		},
   461  		ObjectMeta: metav1.ObjectMeta{
   462  			Name:      "foo-two-containers",
   463  			Namespace: "test",
   464  			Labels:    map[string]string{"foo": "bar"},
   465  		},
   466  		Spec: corev1.PodSpec{
   467  			RestartPolicy: corev1.RestartPolicyAlways,
   468  			DNSPolicy:     corev1.DNSClusterFirst,
   469  			Containers: []corev1.Container{
   470  				{Name: "foo-2-c1"},
   471  				{Name: "foo-2-c2"},
   472  			},
   473  		},
   474  	}
   475  }
   476  
   477  func testPodWithTwoContainersAndTwoInitContainers() *corev1.Pod {
   478  	return &corev1.Pod{
   479  		TypeMeta: metav1.TypeMeta{
   480  			Kind:       "pod",
   481  			APIVersion: "v1",
   482  		},
   483  		ObjectMeta: metav1.ObjectMeta{
   484  			Name:      "foo-two-containers-and-two-init-containers",
   485  			Namespace: "test",
   486  		},
   487  		Spec: corev1.PodSpec{
   488  			InitContainers: []corev1.Container{
   489  				{Name: "foo-2-and-2-initc1"},
   490  				{Name: "foo-2-and-2-initc2"},
   491  			},
   492  			Containers: []corev1.Container{
   493  				{Name: "foo-2-and-2-c1"},
   494  				{Name: "foo-2-and-2-c2"},
   495  			},
   496  		},
   497  	}
   498  }
   499  
   500  func TestLogsForObjectWithClient(t *testing.T) {
   501  	cases := []struct {
   502  		name              string
   503  		podFn             func() *corev1.Pod
   504  		podLogOptions     *corev1.PodLogOptions
   505  		expectedFieldPath string
   506  		allContainers     bool
   507  		expectedError     string
   508  	}{
   509  		{
   510  			name:              "two container pod without default container selected should default to the first one",
   511  			podFn:             testPodWithTwoContainers,
   512  			podLogOptions:     &corev1.PodLogOptions{},
   513  			expectedFieldPath: `spec.containers{foo-2-c1}`,
   514  		},
   515  		{
   516  			name: "two container pod with default container selected",
   517  			podFn: func() *corev1.Pod {
   518  				pod := testPodWithTwoContainers()
   519  				pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "foo-2-c1"}
   520  				return pod
   521  			},
   522  			podLogOptions:     &corev1.PodLogOptions{},
   523  			expectedFieldPath: `spec.containers{foo-2-c1}`,
   524  		},
   525  		{
   526  			name: "two container pod with default container selected but also container set explicitly",
   527  			podFn: func() *corev1.Pod {
   528  				pod := testPodWithTwoContainers()
   529  				pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "foo-2-c1"}
   530  				return pod
   531  			},
   532  			podLogOptions: &corev1.PodLogOptions{
   533  				Container: "foo-2-c2",
   534  			},
   535  			expectedFieldPath: `spec.containers{foo-2-c2}`,
   536  		},
   537  		{
   538  			name: "two container pod with non-existing default container selected should default to the first container",
   539  			podFn: func() *corev1.Pod {
   540  				pod := testPodWithTwoContainers()
   541  				pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "non-existing"}
   542  				return pod
   543  			},
   544  			podLogOptions:     &corev1.PodLogOptions{},
   545  			expectedFieldPath: `spec.containers{foo-2-c1}`,
   546  		},
   547  		{
   548  			name: "two container pod with default container set, but allContainers also set",
   549  			podFn: func() *corev1.Pod {
   550  				pod := testPodWithTwoContainers()
   551  				pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "foo-2-c1"}
   552  				return pod
   553  			},
   554  			allContainers:     true,
   555  			podLogOptions:     &corev1.PodLogOptions{},
   556  			expectedFieldPath: `spec.containers{foo-2-c2}`,
   557  		},
   558  	}
   559  
   560  	for _, tc := range cases {
   561  		t.Run(tc.name, func(t *testing.T) {
   562  			pod := tc.podFn()
   563  			fakeClientset := fakeexternal.NewSimpleClientset(pod)
   564  			responses, err := logsForObjectWithClient(fakeClientset.CoreV1(), pod, tc.podLogOptions, 20*time.Second, tc.allContainers)
   565  			if err != nil {
   566  				if len(tc.expectedError) > 0 {
   567  					if err.Error() == tc.expectedError {
   568  						return
   569  					}
   570  				}
   571  				t.Errorf("unexpected error: %v", err)
   572  				return
   573  			}
   574  			if len(tc.expectedError) > 0 {
   575  				t.Errorf("expected error %q, got none", tc.expectedError)
   576  				return
   577  			}
   578  			if !tc.allContainers && len(responses) != 1 {
   579  				t.Errorf("expected one response, got %d", len(responses))
   580  				return
   581  			}
   582  			if tc.allContainers && len(responses) != 2 {
   583  				t.Errorf("expected 2 responses for allContainers, got %d", len(responses))
   584  				return
   585  			}
   586  			// do not check actual responses in this case as we know there are at least two, which means the preselected
   587  			// container was not used (which is desired).
   588  			if tc.allContainers {
   589  				return
   590  			}
   591  			for r := range responses {
   592  				if r.FieldPath != tc.expectedFieldPath {
   593  					t.Errorf("expected %q container to be preselected, got %q", tc.expectedFieldPath, r.FieldPath)
   594  				}
   595  			}
   596  		})
   597  	}
   598  
   599  }
   600  
   601  func testPodWithTwoContainersAndTwoInitAndOneEphemeralContainers() *corev1.Pod {
   602  	return &corev1.Pod{
   603  		TypeMeta: metav1.TypeMeta{
   604  			Kind:       "pod",
   605  			APIVersion: "v1",
   606  		},
   607  		ObjectMeta: metav1.ObjectMeta{
   608  			Name:      "foo-two-containers-and-two-init-containers",
   609  			Namespace: "test",
   610  		},
   611  		Spec: corev1.PodSpec{
   612  			InitContainers: []corev1.Container{
   613  				{Name: "foo-2-and-2-and-1-initc1"},
   614  				{Name: "foo-2-and-2-and-1-initc2"},
   615  			},
   616  			Containers: []corev1.Container{
   617  				{Name: "foo-2-and-2-and-1-c1"},
   618  				{Name: "foo-2-and-2-and-1-c2"},
   619  			},
   620  			EphemeralContainers: []corev1.EphemeralContainer{
   621  				{
   622  					EphemeralContainerCommon: corev1.EphemeralContainerCommon{Name: "foo-2-and-2-and-1-e1"},
   623  				},
   624  			},
   625  		},
   626  	}
   627  }
   628  
   629  func getLogsAction(namespace string, opts *corev1.PodLogOptions) testclient.Action {
   630  	action := testclient.GenericActionImpl{}
   631  	action.Verb = "get"
   632  	action.Namespace = namespace
   633  	action.Resource = podsResource
   634  	action.Subresource = "log"
   635  	action.Value = opts
   636  	return action
   637  }
   638  

View as plain text