Allow getting logs directly from deployment, job and statefulset
This commit is contained in:
		| @@ -54,13 +54,19 @@ var ( | |||||||
| 		kubectl logs --tail=20 nginx | 		kubectl logs --tail=20 nginx | ||||||
|  |  | ||||||
| 		# Show all logs from pod nginx written in the last hour | 		# Show all logs from pod nginx written in the last hour | ||||||
| 		kubectl logs --since=1h nginx`) | 		kubectl logs --since=1h nginx | ||||||
|  |  | ||||||
|  | 		# Return snapshot logs from first container of a job named hello | ||||||
|  | 		kubectl logs job/hello | ||||||
|  |  | ||||||
|  | 		# Return snapshot logs from container nginx-1 of a deployment named nginx | ||||||
|  | 		kubectl logs deployment/nginx -c nginx-1`) | ||||||
|  |  | ||||||
| 	selectorTail int64 = 10 | 	selectorTail int64 = 10 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	logsUsageStr = "expected 'logs POD_NAME [CONTAINER_NAME]'.\nPOD_NAME is a required argument for the logs command" | 	logsUsageStr = "expected 'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type LogsOptions struct { | type LogsOptions struct { | ||||||
| @@ -83,9 +89,9 @@ type LogsOptions struct { | |||||||
| func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command { | func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command { | ||||||
| 	o := &LogsOptions{} | 	o := &LogsOptions{} | ||||||
| 	cmd := &cobra.Command{ | 	cmd := &cobra.Command{ | ||||||
| 		Use:     "logs [-f] [-p] POD [-c CONTAINER]", | 		Use:     "logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER]", | ||||||
| 		Short:   i18n.T("Print the logs for a container in a pod"), | 		Short:   i18n.T("Print the logs for a container in a pod"), | ||||||
| 		Long:    "Print the logs for a container in a pod. If the pod has only one container, the container name is optional.", | 		Long:    "Print the logs for a container in a pod or specified resource. If the pod has only one container, the container name is optional.", | ||||||
| 		Example: logs_example, | 		Example: logs_example, | ||||||
| 		PreRun: func(cmd *cobra.Command, args []string) { | 		PreRun: func(cmd *cobra.Command, args []string) { | ||||||
| 			if len(os.Args) > 1 && os.Args[1] == "log" { | 			if len(os.Args) > 1 && os.Args[1] == "log" { | ||||||
| @@ -94,9 +100,7 @@ func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command { | |||||||
| 		}, | 		}, | ||||||
| 		Run: func(cmd *cobra.Command, args []string) { | 		Run: func(cmd *cobra.Command, args []string) { | ||||||
| 			cmdutil.CheckErr(o.Complete(f, out, cmd, args)) | 			cmdutil.CheckErr(o.Complete(f, out, cmd, args)) | ||||||
| 			if err := o.Validate(); err != nil { | 			cmdutil.CheckErr(o.Validate()) | ||||||
| 				cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error())) |  | ||||||
| 			} |  | ||||||
| 			cmdutil.CheckErr(o.RunLogs()) | 			cmdutil.CheckErr(o.RunLogs()) | ||||||
| 		}, | 		}, | ||||||
| 		Aliases: []string{"log"}, | 		Aliases: []string{"log"}, | ||||||
|   | |||||||
| @@ -75,6 +75,7 @@ go_test( | |||||||
|     name = "go_default_test", |     name = "go_default_test", | ||||||
|     srcs = [ |     srcs = [ | ||||||
|         "cached_discovery_test.go", |         "cached_discovery_test.go", | ||||||
|  |         "factory_object_mapping_test.go", | ||||||
|         "factory_test.go", |         "factory_test.go", | ||||||
|         "helpers_test.go", |         "helpers_test.go", | ||||||
|         "shortcut_restmapper_test.go", |         "shortcut_restmapper_test.go", | ||||||
| @@ -90,7 +91,10 @@ go_test( | |||||||
|         "//pkg/api/testing:go_default_library", |         "//pkg/api/testing:go_default_library", | ||||||
|         "//pkg/api/v1:go_default_library", |         "//pkg/api/v1:go_default_library", | ||||||
|         "//pkg/api/validation:go_default_library", |         "//pkg/api/validation:go_default_library", | ||||||
|  |         "//pkg/apis/apps:go_default_library", | ||||||
|  |         "//pkg/apis/batch:go_default_library", | ||||||
|         "//pkg/apis/extensions:go_default_library", |         "//pkg/apis/extensions:go_default_library", | ||||||
|  |         "//pkg/client/clientset_generated/internalclientset:go_default_library", | ||||||
|         "//pkg/client/clientset_generated/internalclientset/fake:go_default_library", |         "//pkg/client/clientset_generated/internalclientset/fake:go_default_library", | ||||||
|         "//pkg/controller:go_default_library", |         "//pkg/controller:go_default_library", | ||||||
|         "//pkg/kubectl:go_default_library", |         "//pkg/kubectl:go_default_library", | ||||||
| @@ -104,6 +108,7 @@ go_test( | |||||||
|         "//vendor:k8s.io/apimachinery/pkg/labels", |         "//vendor:k8s.io/apimachinery/pkg/labels", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime", |         "//vendor:k8s.io/apimachinery/pkg/runtime", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", |         "//vendor:k8s.io/apimachinery/pkg/runtime/schema", | ||||||
|  |         "//vendor:k8s.io/apimachinery/pkg/util/diff", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/util/validation/field", |         "//vendor:k8s.io/apimachinery/pkg/util/validation/field", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/version", |         "//vendor:k8s.io/apimachinery/pkg/version", | ||||||
|         "//vendor:k8s.io/apimachinery/pkg/watch", |         "//vendor:k8s.io/apimachinery/pkg/watch", | ||||||
|   | |||||||
| @@ -213,51 +213,48 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclien | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | 	opts, ok := options.(*api.PodLogOptions) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, errors.New("provided options object is not a PodLogOptions") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var selector labels.Selector | ||||||
|  | 	var namespace string | ||||||
| 	switch t := object.(type) { | 	switch t := object.(type) { | ||||||
| 	case *api.Pod: | 	case *api.Pod: | ||||||
| 		opts, ok := options.(*api.PodLogOptions) |  | ||||||
| 		if !ok { |  | ||||||
| 			return nil, errors.New("provided options object is not a PodLogOptions") |  | ||||||
| 		} |  | ||||||
| 		return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil | 		return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil | ||||||
|  |  | ||||||
| 	case *api.ReplicationController: | 	case *api.ReplicationController: | ||||||
| 		opts, ok := options.(*api.PodLogOptions) | 		namespace = t.Namespace | ||||||
| 		if !ok { | 		selector = labels.SelectorFromSet(t.Spec.Selector) | ||||||
| 			return nil, errors.New("provided options object is not a PodLogOptions") |  | ||||||
| 		} |  | ||||||
| 		selector := labels.SelectorFromSet(t.Spec.Selector) |  | ||||||
| 		sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) } |  | ||||||
| 		pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		if numPods > 1 { |  | ||||||
| 			fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil |  | ||||||
|  |  | ||||||
| 	case *extensions.ReplicaSet: | 	case *extensions.ReplicaSet: | ||||||
| 		opts, ok := options.(*api.PodLogOptions) | 		namespace = t.Namespace | ||||||
| 		if !ok { | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
| 			return nil, errors.New("provided options object is not a PodLogOptions") |  | ||||||
| 		} |  | ||||||
| 		selector, err := metav1.LabelSelectorAsSelector(t.Spec.Selector) |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("invalid label selector: %v", err) | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
| 		} | 		} | ||||||
| 		sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) } |  | ||||||
| 		pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy) | 	case *extensions.Deployment: | ||||||
|  | 		namespace = t.Namespace | ||||||
|  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
| 		} |  | ||||||
| 		if numPods > 1 { |  | ||||||
| 			fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil | 	case *batch.Job: | ||||||
|  | 		namespace = t.Namespace | ||||||
|  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	case *apps.StatefulSet: | ||||||
|  | 		namespace = t.Namespace | ||||||
|  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	default: | 	default: | ||||||
| 		gvks, _, err := api.Scheme.ObjectKinds(object) | 		gvks, _, err := api.Scheme.ObjectKinds(object) | ||||||
| @@ -266,6 +263,16 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclien | |||||||
| 		} | 		} | ||||||
| 		return nil, fmt.Errorf("cannot get the logs from %v", gvks[0]) | 		return nil, fmt.Errorf("cannot get the logs from %v", gvks[0]) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) } | ||||||
|  | 	pod, numPods, err := GetFirstPod(clientset.Core(), namespace, selector, 20*time.Second, sortBy) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if numPods > 1 { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) | ||||||
|  | 	} | ||||||
|  | 	return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *ring1Factory) Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error) { | func (f *ring1Factory) Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error) { | ||||||
| @@ -329,29 +336,35 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod, | |||||||
| 	case *extensions.ReplicaSet: | 	case *extensions.ReplicaSet: | ||||||
| 		namespace = t.Namespace | 		namespace = t.Namespace | ||||||
| 		selector = labels.SelectorFromSet(t.Spec.Selector.MatchLabels) | 		selector = labels.SelectorFromSet(t.Spec.Selector.MatchLabels) | ||||||
|  |  | ||||||
| 	case *api.ReplicationController: | 	case *api.ReplicationController: | ||||||
| 		namespace = t.Namespace | 		namespace = t.Namespace | ||||||
| 		selector = labels.SelectorFromSet(t.Spec.Selector) | 		selector = labels.SelectorFromSet(t.Spec.Selector) | ||||||
|  |  | ||||||
| 	case *apps.StatefulSet: | 	case *apps.StatefulSet: | ||||||
| 		namespace = t.Namespace | 		namespace = t.Namespace | ||||||
| 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("invalid label selector: %v", err) | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case *extensions.Deployment: | 	case *extensions.Deployment: | ||||||
| 		namespace = t.Namespace | 		namespace = t.Namespace | ||||||
| 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("invalid label selector: %v", err) | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case *batch.Job: | 	case *batch.Job: | ||||||
| 		namespace = t.Namespace | 		namespace = t.Namespace | ||||||
| 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("invalid label selector: %v", err) | 			return nil, fmt.Errorf("invalid label selector: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case *api.Pod: | 	case *api.Pod: | ||||||
| 		return t, nil | 		return t, nil | ||||||
|  |  | ||||||
| 	default: | 	default: | ||||||
| 		gvks, _, err := api.Scheme.ObjectKinds(object) | 		gvks, _, err := api.Scheme.ObjectKinds(object) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -359,6 +372,7 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod, | |||||||
| 		} | 		} | ||||||
| 		return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0]) | 		return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0]) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } | 	sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } | ||||||
| 	pod, _, err := GetFirstPod(clientset.Core(), namespace, selector, 1*time.Minute, sortBy) | 	pod, _, err := GetFirstPod(clientset.Core(), namespace, selector, 1*time.Minute, sortBy) | ||||||
| 	return pod, err | 	return pod, err | ||||||
|   | |||||||
							
								
								
									
										192
									
								
								pkg/kubectl/cmd/util/factory_object_mapping_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								pkg/kubectl/cmd/util/factory_object_mapping_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2017 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 util | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/diff" | ||||||
|  | 	testclient "k8s.io/client-go/testing" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/batch" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
|  | 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
|  | 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type fakeClientAccessFactory struct { | ||||||
|  | 	ClientAccessFactory | ||||||
|  |  | ||||||
|  | 	fakeClientset *fake.Clientset | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *fakeClientAccessFactory) ClientSetForVersion(requiredVersion *schema.GroupVersion) (internalclientset.Interface, error) { | ||||||
|  | 	return f.fakeClientset, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newFakeClientAccessFactory(objs []runtime.Object) *fakeClientAccessFactory { | ||||||
|  | 	return &fakeClientAccessFactory{ | ||||||
|  | 		fakeClientset: fake.NewSimpleClientset(objs...), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	podsResource = schema.GroupVersionResource{Resource: "pods"} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestLogsForObject(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name    string | ||||||
|  | 		obj     runtime.Object | ||||||
|  | 		opts    *api.PodLogOptions | ||||||
|  | 		pods    []runtime.Object | ||||||
|  | 		actions []testclient.Action | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "pod logs", | ||||||
|  | 			obj: &api.Pod{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"}, | ||||||
|  | 			}, | ||||||
|  | 			pods: []runtime.Object{testPod()}, | ||||||
|  | 			actions: []testclient.Action{ | ||||||
|  | 				getLogsAction("test", nil), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "replication controller logs", | ||||||
|  | 			obj: &api.ReplicationController{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"}, | ||||||
|  | 				Spec: api.ReplicationControllerSpec{ | ||||||
|  | 					Selector: map[string]string{"foo": "bar"}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			pods: []runtime.Object{testPod()}, | ||||||
|  | 			actions: []testclient.Action{ | ||||||
|  | 				testclient.NewListAction(podsResource, "test", metav1.ListOptions{LabelSelector: "foo=bar"}), | ||||||
|  | 				getLogsAction("test", nil), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "replica set logs", | ||||||
|  | 			obj: &extensions.ReplicaSet{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"}, | ||||||
|  | 				Spec: extensions.ReplicaSetSpec{ | ||||||
|  | 					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			pods: []runtime.Object{testPod()}, | ||||||
|  | 			actions: []testclient.Action{ | ||||||
|  | 				testclient.NewListAction(podsResource, "test", metav1.ListOptions{LabelSelector: "foo=bar"}), | ||||||
|  | 				getLogsAction("test", nil), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "deployment logs", | ||||||
|  | 			obj: &extensions.Deployment{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"}, | ||||||
|  | 				Spec: extensions.DeploymentSpec{ | ||||||
|  | 					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			pods: []runtime.Object{testPod()}, | ||||||
|  | 			actions: []testclient.Action{ | ||||||
|  | 				testclient.NewListAction(podsResource, "test", metav1.ListOptions{LabelSelector: "foo=bar"}), | ||||||
|  | 				getLogsAction("test", nil), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "job logs", | ||||||
|  | 			obj: &batch.Job{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"}, | ||||||
|  | 				Spec: batch.JobSpec{ | ||||||
|  | 					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			pods: []runtime.Object{testPod()}, | ||||||
|  | 			actions: []testclient.Action{ | ||||||
|  | 				testclient.NewListAction(podsResource, "test", metav1.ListOptions{LabelSelector: "foo=bar"}), | ||||||
|  | 				getLogsAction("test", nil), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "stateful set logs", | ||||||
|  | 			obj: &apps.StatefulSet{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"}, | ||||||
|  | 				Spec: apps.StatefulSetSpec{ | ||||||
|  | 					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			pods: []runtime.Object{testPod()}, | ||||||
|  | 			actions: []testclient.Action{ | ||||||
|  | 				testclient.NewListAction(podsResource, "test", metav1.ListOptions{LabelSelector: "foo=bar"}), | ||||||
|  | 				getLogsAction("test", nil), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		caf := newFakeClientAccessFactory(test.pods) | ||||||
|  | 		omf := NewObjectMappingFactory(caf) | ||||||
|  | 		_, err := omf.LogsForObject(test.obj, test.opts) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Errorf("%s: unexpected error: %v", test.name, err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		for i := range test.actions { | ||||||
|  | 			if len(caf.fakeClientset.Actions()) < i { | ||||||
|  | 				t.Errorf("%s: action %d does not exists in actual actions: %#v", | ||||||
|  | 					test.name, i, caf.fakeClientset.Actions()) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			got := caf.fakeClientset.Actions()[i] | ||||||
|  | 			want := test.actions[i] | ||||||
|  | 			if !reflect.DeepEqual(got, want) { | ||||||
|  | 				t.Errorf("%s: unexpected action: %s", test.name, diff.ObjectDiff(got, want)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func testPod() runtime.Object { | ||||||
|  | 	return &api.Pod{ | ||||||
|  | 		ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 			Name:      "foo", | ||||||
|  | 			Namespace: "test", | ||||||
|  | 			Labels:    map[string]string{"foo": "bar"}, | ||||||
|  | 		}, | ||||||
|  | 		Spec: api.PodSpec{ | ||||||
|  | 			RestartPolicy: api.RestartPolicyAlways, | ||||||
|  | 			DNSPolicy:     api.DNSClusterFirst, | ||||||
|  | 			Containers:    []api.Container{{Name: "c1"}}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getLogsAction(namespace string, opts *api.PodLogOptions) testclient.Action { | ||||||
|  | 	action := testclient.GenericActionImpl{} | ||||||
|  | 	action.Verb = "get" | ||||||
|  | 	action.Namespace = namespace | ||||||
|  | 	action.Resource = podsResource | ||||||
|  | 	action.Subresource = "logs" | ||||||
|  | 	action.Value = opts | ||||||
|  | 	return action | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Maciej Szulik
					Maciej Szulik