From 24b0a43c275ea68663b7980ab2db15fe47f89e64 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Thu, 2 Aug 2018 11:55:56 +0200 Subject: [PATCH 01/12] UPSTREAM: 66398: fix logs command to be generic for all resources again --- .../pkg/kubectl/cmd/clusterinfo_dump.go | 16 ++-- .../k8s.io/kubernetes/pkg/kubectl/cmd/logs.go | 47 +++------- .../kubernetes/pkg/kubectl/cmd/logs_test.go | 4 +- .../k8s.io/kubernetes/pkg/kubectl/cmd/run.go | 16 ++-- .../kubectl/polymorphichelpers/interface.go | 2 +- .../polymorphichelpers/logsforobject.go | 86 +++++++++++++++++-- .../polymorphichelpers/logsforobject_test.go | 2 +- 7 files changed, 108 insertions(+), 65 deletions(-) diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go index 9e55ca45d778..c4200b221acc 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go @@ -236,20 +236,22 @@ func (o *ClusterInfoDumpOptions) Run() error { writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name))) defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name))) - request, err := o.LogsForObject(o.RESTClientGetter, pod, &api.PodLogOptions{Container: container.Name}, timeout) + requests, err := o.LogsForObject(o.RESTClientGetter, pod, &api.PodLogOptions{Container: container.Name}, timeout, false) if err != nil { // Print error and return. writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error()))) return } - data, err := request.DoRaw() - if err != nil { - // Print error and return. - writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error()))) - return + for _, request := range requests { + data, err := request.DoRaw() + if err != nil { + // Print error and return. + writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error()))) + return + } + writer.Write(data) } - writer.Write(data) } for ix := range pods.Items { diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go index 01c62e7dae8f..79acc9af55e4 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" @@ -244,55 +245,27 @@ func (o LogsOptions) Validate() error { // RunLogs retrieves a pod log func (o LogsOptions) RunLogs() error { - switch t := o.Object.(type) { - case *api.PodList: - for _, p := range t.Items { - if err := o.getPodLogs(&p); err != nil { - return err - } - } - return nil - case *api.Pod: - return o.getPodLogs(t) - default: - return o.getLogs(o.Object) - } -} - -// getPodLogs checks whether o.AllContainers is set to true. -// If so, it retrives all containers' log in the pod. -func (o LogsOptions) getPodLogs(pod *api.Pod) error { - if !o.AllContainers { - return o.getLogs(pod) + requests, err := o.LogsForObject(o.RESTClientGetter, o.Object, o.Options, o.GetPodTimeout, o.AllContainers) + if err != nil { + return err } - for _, c := range pod.Spec.InitContainers { - o.Options.(*api.PodLogOptions).Container = c.Name - if err := o.getLogs(pod); err != nil { - return err - } - } - for _, c := range pod.Spec.Containers { - o.Options.(*api.PodLogOptions).Container = c.Name - if err := o.getLogs(pod); err != nil { + for _, request := range requests { + if err := consumeRequest(request, o.Out); err != nil { return err } } + return nil } -func (o LogsOptions) getLogs(obj runtime.Object) error { - req, err := o.LogsForObject(o.RESTClientGetter, obj, o.Options, o.GetPodTimeout) - if err != nil { - return err - } - - readCloser, err := req.Stream() +func consumeRequest(request *rest.Request, out io.Writer) error { + readCloser, err := request.Stream() if err != nil { return err } defer readCloser.Close() - _, err = io.Copy(o.Out, readCloser) + _, err = io.Copy(out, readCloser) return err } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go index 7b82432532e4..03d2dd22a249 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go @@ -244,14 +244,14 @@ type logTestMock struct { client internalclientset.Interface } -func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) { +func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*restclient.Request, error) { switch t := object.(type) { case *api.Pod: opts, ok := options.(*api.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } - return m.client.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil + return []*restclient.Request{m.client.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil default: return nil, fmt.Errorf("cannot get the logs from %T", object) } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go index 07de298372d6..54f3c17e5ccc 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go @@ -18,7 +18,6 @@ package cmd import ( "fmt" - "io" "github.com/docker/distribution/reference" "github.com/spf13/cobra" @@ -540,21 +539,16 @@ func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *api.Pod, return err } - req, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout) + requests, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout, false) if err != nil { return err } - - readCloser, err := req.Stream() - if err != nil { - return err + for _, request := range requests { + if err := consumeRequest(request, opts.Out); err != nil { + return err + } } - defer readCloser.Close() - _, err = io.Copy(opts.Out, readCloser) - if err != nil { - return err - } return nil } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go index 06f40df60bef..7904616a4f74 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go @@ -30,7 +30,7 @@ import ( ) // LogsForObjectFunc is a function type that can tell you how to get logs for a runtime.object -type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) +type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) // LogsForObjectFn gives a way to easily override the function for unit testing if needed. var LogsForObjectFn LogsForObjectFunc = logsForObject diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go index f5a517a1b3da..0cb68526516c 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go @@ -33,7 +33,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) -func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) { +func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) { clientConfig, err := restClientGetter.ToRESTConfig() if err != nil { return nil, err @@ -42,21 +42,94 @@ func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, if err != nil { return nil, err } - return logsForObjectWithClient(clientset, object, options, timeout) + return logsForObjectWithClient(clientset, object, options, timeout, allContainers) } // this is split for easy test-ability -func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) { +func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) { opts, ok := options.(*coreinternal.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } switch t := object.(type) { + case *coreinternal.PodList: + ret := []*rest.Request{} + for i := range t.Items { + currRet, err := logsForObjectWithClient(clientset, &t.Items[i], options, timeout, allContainers) + if err != nil { + return nil, err + } + ret = append(ret, currRet...) + } + return ret, nil + + case *corev1.PodList: + ret := []*rest.Request{} + for i := range t.Items { + currRet, err := logsForObjectWithClient(clientset, &t.Items[i], options, timeout, allContainers) + if err != nil { + return nil, err + } + ret = append(ret, currRet...) + } + return ret, nil + case *coreinternal.Pod: - return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil + // if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false + if !allContainers { + return []*rest.Request{clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil + } + + ret := []*rest.Request{} + for _, c := range t.Spec.InitContainers { + currOpts := opts.DeepCopy() + currOpts.Container = c.Name + currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false) + if err != nil { + return nil, err + } + ret = append(ret, currRet...) + } + for _, c := range t.Spec.Containers { + currOpts := opts.DeepCopy() + currOpts.Container = c.Name + currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false) + if err != nil { + return nil, err + } + ret = append(ret, currRet...) + } + + return ret, nil + case *corev1.Pod: - return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil + // if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false + if !allContainers { + return []*rest.Request{clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil + } + + ret := []*rest.Request{} + for _, c := range t.Spec.InitContainers { + currOpts := opts.DeepCopy() + currOpts.Container = c.Name + currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false) + if err != nil { + return nil, err + } + ret = append(ret, currRet...) + } + for _, c := range t.Spec.Containers { + currOpts := opts.DeepCopy() + currOpts.Container = c.Name + currRet, err := logsForObjectWithClient(clientset, t, options, timeout, false) + if err != nil { + return nil, err + } + ret = append(ret, currRet...) + } + + return ret, nil } namespace, selector, err := SelectorsForObject(object) @@ -71,5 +144,6 @@ func logsForObjectWithClient(clientset internalclientset.Interface, object, opti 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 + + return logsForObjectWithClient(clientset, pod, options, timeout, allContainers) } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go index 6ed87d910b09..3f87f710b53d 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go @@ -130,7 +130,7 @@ func TestLogsForObject(t *testing.T) { for _, test := range tests { fakeClientset := fake.NewSimpleClientset(test.pods...) - _, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second) + _, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second, false) if err != nil { t.Errorf("%s: unexpected error: %v", test.name, err) continue From c7e00d1755ffab40b241de9f8c89c6508e92ac79 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Wed, 25 Jul 2018 18:38:00 -0400 Subject: [PATCH 02/12] UPSTREAM: 66519: switch attach to use external objs --- .../k8s.io/kubernetes/pkg/kubectl/cmd/BUILD | 3 +- .../kubernetes/pkg/kubectl/cmd/attach.go | 266 ++++++++--------- .../kubernetes/pkg/kubectl/cmd/attach_test.go | 275 ++++++++++-------- .../k8s.io/kubernetes/pkg/kubectl/cmd/logs.go | 166 +++++++---- .../kubernetes/pkg/kubectl/cmd/logs_test.go | 272 ++++++++++------- .../kubernetes/pkg/kubectl/cmd/portforward.go | 9 +- .../k8s.io/kubernetes/pkg/kubectl/cmd/run.go | 46 +-- .../kubernetes/pkg/kubectl/conditions.go | 21 ++ .../pkg/kubectl/polymorphichelpers/BUILD | 5 +- .../attachablepodforobject.go | 17 +- .../pkg/kubectl/polymorphichelpers/helpers.go | 15 +- .../polymorphichelpers/helpers_test.go | 94 +++--- .../kubectl/polymorphichelpers/interface.go | 3 +- .../polymorphichelpers/logsforobject.go | 17 +- .../polymorphichelpers/logsforobject_test.go | 25 +- .../kubernetes/test/e2e/kubectl/kubectl.go | 2 +- 16 files changed, 697 insertions(+), 539 deletions(-) diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD index 82074478ae18..1d9757461066 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD @@ -59,7 +59,7 @@ go_library( "//pkg/api/legacyscheme:go_default_library", "//pkg/apis/certificates:go_default_library", "//pkg/apis/core:go_default_library", - "//pkg/apis/core/validation:go_default_library", + "//pkg/apis/core/v1:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", @@ -198,7 +198,6 @@ go_test( "//pkg/apis/batch:go_default_library", "//pkg/apis/core:go_default_library", "//pkg/apis/extensions:go_default_library", - "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/kubectl/cmd/create:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go index 88ff4f7d4158..153a1406bebc 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go @@ -17,7 +17,6 @@ limitations under the License. package cmd import ( - "errors" "fmt" "io" "net/url" @@ -26,17 +25,17 @@ import ( "github.com/golang/glog" "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - utilerrors "k8s.io/apimachinery/pkg/util/errors" + apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" "k8s.io/kubernetes/pkg/api/legacyscheme" - api "k8s.io/kubernetes/pkg/apis/core" - coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" + "k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/util/i18n" ) @@ -62,14 +61,40 @@ const ( defaultPodLogsTimeout = 20 * time.Second ) -func NewCmdAttach(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := &AttachOptions{ +// AttachOptions declare the arguments accepted by the Exec command +type AttachOptions struct { + StreamOptions + + // whether to disable the use of standard error when streaming output from tty + DisableStderr bool + + CommandName string + SuggestedCmdUsage string + + Pod *apiv1.Pod + + AttachFunc func(*AttachOptions, *apiv1.Container, bool, remotecommand.TerminalSizeQueue) func() error + Resources []string + Builder func() *resource.Builder + AttachablePodFn polymorphichelpers.AttachableLogsForObjectFunc + restClientGetter genericclioptions.RESTClientGetter + + Attach RemoteAttach + GetPodTimeout time.Duration + Config *restclient.Config +} + +func NewAttachOptions(streams genericclioptions.IOStreams) *AttachOptions { + return &AttachOptions{ StreamOptions: StreamOptions{ IOStreams: streams, }, - Attach: &DefaultRemoteAttach{}, } +} + +func NewCmdAttach(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := NewAttachOptions(streams) cmd := &cobra.Command{ Use: "attach (POD | TYPE/NAME) -c CONTAINER", DisableFlagsInUseLine: true, @@ -94,6 +119,29 @@ type RemoteAttach interface { Attach(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error } +func defaultAttachFunc(o *AttachOptions, containerToAttach *apiv1.Container, raw bool, sizeQueue remotecommand.TerminalSizeQueue) func() error { + return func() error { + restClient, err := restclient.RESTClientFor(o.Config) + if err != nil { + return err + } + req := restClient.Post(). + Resource("pods"). + Name(o.Pod.Name). + Namespace(o.Pod.Namespace). + SubResource("attach") + req.VersionedParams(&apiv1.PodAttachOptions{ + Container: containerToAttach.Name, + Stdin: o.Stdin, + Stdout: o.Out != nil, + Stderr: !o.DisableStderr, + TTY: raw, + }, legacyscheme.ParameterCodec) + + return o.Attach.Attach("POST", req.URL(), o.Config, o.In, o.Out, o.ErrOut, raw, sizeQueue) + } +} + // DefaultRemoteAttach is the standard implementation of attaching type DefaultRemoteAttach struct{} @@ -111,63 +159,24 @@ func (*DefaultRemoteAttach) Attach(method string, url *url.URL, config *restclie }) } -// AttachOptions declare the arguments accepted by the Exec command -type AttachOptions struct { - StreamOptions - - CommandName string - SuggestedCmdUsage string - - Pod *api.Pod - - Attach RemoteAttach - PodClient coreclient.PodsGetter - GetPodTimeout time.Duration - Config *restclient.Config -} - // Complete verifies command line arguments and loads data from the command environment -func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []string) error { - if len(argsIn) == 0 { - return cmdutil.UsageErrorf(cmd, "at least 1 argument is required for attach") - } - if len(argsIn) > 2 { - return cmdutil.UsageErrorf(cmd, "expected POD, TYPE/NAME, or TYPE NAME, (at most 2 arguments) saw %d: %v", len(argsIn), argsIn) - } - - namespace, _, err := f.ToRawKubeConfigLoader().Namespace() +func (o *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { + var err error + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - p.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd) - if err != nil { - return cmdutil.UsageErrorf(cmd, err.Error()) - } - - builder := f.NewBuilder(). - WithScheme(legacyscheme.Scheme). - NamespaceParam(namespace).DefaultNamespace() - - switch len(argsIn) { - case 1: - builder.ResourceNames("pods", argsIn[0]) - case 2: - builder.ResourceNames(argsIn[0], argsIn[1]) - } - - obj, err := builder.Do().Object() - if err != nil { - return err - } + o.AttachablePodFn = polymorphichelpers.AttachablePodForObjectFn - attachablePod, err := polymorphichelpers.AttachablePodForObjectFn(f, obj, p.GetPodTimeout) + o.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd) if err != nil { - return err + return cmdutil.UsageErrorf(cmd, err.Error()) } - p.PodName = attachablePod.Name - p.Namespace = namespace + o.Builder = f.NewBuilder + o.Resources = args + o.restClientGetter = f fullCmdName := "" cmdParent := cmd.Parent() @@ -175,82 +184,87 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [ fullCmdName = cmdParent.CommandPath() } if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") { - p.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe pod/%s -n %s' to see all of the containers in this pod.", fullCmdName, p.PodName, p.Namespace) + o.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe pod/%s -n %s' to see all of the containers in this pod.", fullCmdName, o.PodName, o.Namespace) } config, err := f.ToRESTConfig() if err != nil { return err } - p.Config = config + o.Config = config - clientset, err := f.ClientSet() - if err != nil { - return err - } - - p.PodClient = clientset.Core() + o.AttachFunc = defaultAttachFunc - if p.CommandName == "" { - p.CommandName = cmd.CommandPath() + if o.CommandName == "" { + o.CommandName = cmd.CommandPath() } return nil } // Validate checks that the provided attach options are specified. -func (p *AttachOptions) Validate() error { - allErrs := []error{} - if len(p.PodName) == 0 { - allErrs = append(allErrs, errors.New("pod name must be specified")) +func (o *AttachOptions) Validate() error { + if len(o.Resources) == 0 { + return fmt.Errorf("at least 1 argument is required for attach") } - if p.Out == nil || p.ErrOut == nil { - allErrs = append(allErrs, errors.New("both output and error output must be provided")) + if len(o.Resources) > 2 { + return fmt.Errorf("expected POD, TYPE/NAME, or TYPE NAME, (at most 2 arguments) saw %d: %v", len(o.Resources), o.Resources) } - if p.Attach == nil || p.PodClient == nil || p.Config == nil { - allErrs = append(allErrs, errors.New("client, client config, and attach must be provided")) + if o.GetPodTimeout <= 0 { + return fmt.Errorf("--pod-running-timeout must be higher than zero") } - return utilerrors.NewAggregate(allErrs) + + return nil } // Run executes a validated remote execution against a pod. -func (p *AttachOptions) Run() error { - if p.Pod == nil { - pod, err := p.PodClient.Pods(p.Namespace).Get(p.PodName, metav1.GetOptions{}) +func (o *AttachOptions) Run() error { + if o.Pod == nil { + b := o.Builder(). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). + NamespaceParam(o.Namespace).DefaultNamespace() + + switch len(o.Resources) { + case 1: + b.ResourceNames("pods", o.Resources[0]) + case 2: + b.ResourceNames(o.Resources[0], o.Resources[1]) + } + + obj, err := b.Do().Object() if err != nil { return err } - if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed { - return fmt.Errorf("cannot attach a container in a completed pod; current phase is %s", pod.Status.Phase) + o.Pod, err = o.findAttachablePod(obj) + if err != nil { + return err } - p.Pod = pod + if o.Pod.Status.Phase == apiv1.PodSucceeded || o.Pod.Status.Phase == apiv1.PodFailed { + return fmt.Errorf("cannot attach a container in a completed pod; current phase is %s", o.Pod.Status.Phase) + } // TODO: convert this to a clean "wait" behavior } - pod := p.Pod // check for TTY - containerToAttach, err := p.containerToAttachTo(pod) + containerToAttach, err := o.containerToAttachTo(o.Pod) if err != nil { return fmt.Errorf("cannot attach to the container: %v", err) } - if p.TTY && !containerToAttach.TTY { - p.TTY = false - if p.ErrOut != nil { - fmt.Fprintf(p.ErrOut, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name) + if o.TTY && !containerToAttach.TTY { + o.TTY = false + if o.ErrOut != nil { + fmt.Fprintf(o.ErrOut, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name) } - } else if !p.TTY && containerToAttach.TTY { + } else if !o.TTY && containerToAttach.TTY { // the container was launched with a TTY, so we have to force a TTY here, otherwise you'll get // an error "Unrecognized input header" - p.TTY = true + o.TTY = true } // ensure we can recover the terminal while attached - t := p.setupTTY() - - // save p.Err so we can print the command prompt message below - stderr := p.ErrOut + t := o.setupTTY() var sizeQueue remotecommand.TerminalSizeQueue if t.Raw { @@ -265,66 +279,52 @@ func (p *AttachOptions) Run() error { sizeQueue = t.MonitorSize(&sizePlusOne, size) } - // unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is - // true - p.ErrOut = nil - } - - fn := func() error { - restClient, err := restclient.RESTClientFor(p.Config) - if err != nil { - return err - } - // TODO: consider abstracting into a client invocation or client helper - req := restClient.Post(). - Resource("pods"). - Name(pod.Name). - Namespace(pod.Namespace). - SubResource("attach") - req.VersionedParams(&api.PodAttachOptions{ - Container: containerToAttach.Name, - Stdin: p.Stdin, - Stdout: p.Out != nil, - Stderr: p.ErrOut != nil, - TTY: t.Raw, - }, legacyscheme.ParameterCodec) - - return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.ErrOut, t.Raw, sizeQueue) + o.DisableStderr = true } - if !p.Quiet && stderr != nil { - fmt.Fprintln(stderr, "If you don't see a command prompt, try pressing enter.") + if !o.Quiet { + fmt.Fprintln(o.ErrOut, "If you don't see a command prompt, try pressing enter.") } - if err := t.Safe(fn); err != nil { + if err := t.Safe(o.AttachFunc(o, containerToAttach, t.Raw, sizeQueue)); err != nil { return err } - if p.Stdin && t.Raw && pod.Spec.RestartPolicy == api.RestartPolicyAlways { - fmt.Fprintf(p.Out, "Session ended, resume using '%s %s -c %s -i -t' command when the pod is running\n", p.CommandName, pod.Name, containerToAttach.Name) + if o.Stdin && t.Raw && o.Pod.Spec.RestartPolicy == apiv1.RestartPolicyAlways { + fmt.Fprintf(o.Out, "Session ended, resume using '%s %s -c %s -i -t' command when the pod is running\n", o.CommandName, o.Pod.Name, containerToAttach.Name) } return nil } +func (o *AttachOptions) findAttachablePod(obj runtime.Object) (*apiv1.Pod, error) { + attachablePod, err := o.AttachablePodFn(o.restClientGetter, obj, o.GetPodTimeout) + if err != nil { + return nil, err + } + + o.StreamOptions.PodName = attachablePod.Name + return attachablePod, nil +} + // containerToAttach returns a reference to the container to attach to, given // by name or the first container if name is empty. -func (p *AttachOptions) containerToAttachTo(pod *api.Pod) (*api.Container, error) { - if len(p.ContainerName) > 0 { +func (o *AttachOptions) containerToAttachTo(pod *apiv1.Pod) (*apiv1.Container, error) { + if len(o.ContainerName) > 0 { for i := range pod.Spec.Containers { - if pod.Spec.Containers[i].Name == p.ContainerName { + if pod.Spec.Containers[i].Name == o.ContainerName { return &pod.Spec.Containers[i], nil } } for i := range pod.Spec.InitContainers { - if pod.Spec.InitContainers[i].Name == p.ContainerName { + if pod.Spec.InitContainers[i].Name == o.ContainerName { return &pod.Spec.InitContainers[i], nil } } - return nil, fmt.Errorf("container not found (%s)", p.ContainerName) + return nil, fmt.Errorf("container not found (%s)", o.ContainerName) } - if len(p.SuggestedCmdUsage) > 0 { - fmt.Fprintf(p.ErrOut, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name) - fmt.Fprintf(p.ErrOut, "%s\n", p.SuggestedCmdUsage) + if len(o.SuggestedCmdUsage) > 0 { + fmt.Fprintf(o.ErrOut, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name) + fmt.Fprintf(o.ErrOut, "%s\n", o.SuggestedCmdUsage) } glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name) @@ -332,8 +332,8 @@ func (p *AttachOptions) containerToAttachTo(pod *api.Pod) (*api.Container, error } // GetContainerName returns the name of the container to attach to, with a fallback. -func (p *AttachOptions) GetContainerName(pod *api.Pod) (string, error) { - c, err := p.containerToAttachTo(pod) +func (o *AttachOptions) GetContainerName(pod *apiv1.Pod) (string, error) { + c, err := o.containerToAttachTo(pod) if err != nil { return "", err } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go index 007df0dccdf7..3b8236c2a1ae 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go @@ -25,8 +25,7 @@ import ( "testing" "time" - "github.com/spf13/cobra" - + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -34,10 +33,9 @@ import ( "k8s.io/client-go/rest/fake" "k8s.io/client-go/tools/remotecommand" "k8s.io/kubernetes/pkg/api/legacyscheme" - api "k8s.io/kubernetes/pkg/apis/core" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" "k8s.io/kubernetes/pkg/kubectl/scheme" ) @@ -53,130 +51,136 @@ func (f *fakeRemoteAttach) Attach(method string, url *url.URL, config *restclien return f.err } +func fakeAttachablePodFn(pod *corev1.Pod) polymorphichelpers.AttachableLogsForObjectFunc { + return func(getter genericclioptions.RESTClientGetter, obj runtime.Object, timeout time.Duration) (*corev1.Pod, error) { + return pod, nil + } +} + func TestPodAndContainerAttach(t *testing.T) { tests := []struct { - args []string - p *AttachOptions - name string - expectError bool - expectedPod string - expectedContainer string - timeout time.Duration - obj runtime.Object + name string + args []string + options *AttachOptions + expectError string + expectedPodName string + expectedContainerName string + obj *corev1.Pod }{ { - p: &AttachOptions{}, - expectError: true, name: "empty", - timeout: 1, + options: &AttachOptions{GetPodTimeout: 1}, + expectError: "at least 1 argument is required", }, { - p: &AttachOptions{}, - args: []string{"one", "two", "three"}, - expectError: true, name: "too many args", - timeout: 2, - }, - { - p: &AttachOptions{}, - args: []string{"foo"}, - expectedPod: "foo", - name: "no container, no flags", - obj: attachPod(), - timeout: defaultPodLogsTimeout, + options: &AttachOptions{GetPodTimeout: 2}, + args: []string{"one", "two", "three"}, + expectError: "at most 2 arguments", }, { - p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}}, - args: []string{"foo"}, - expectedPod: "foo", - expectedContainer: "bar", - name: "container in flag", - obj: attachPod(), - timeout: 10000000, + name: "no container, no flags", + options: &AttachOptions{GetPodTimeout: defaultPodLogsTimeout}, + args: []string{"foo"}, + expectedPodName: "foo", + expectedContainerName: "bar", + obj: attachPod(), }, { - p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "initfoo"}}, - args: []string{"foo"}, - expectedPod: "foo", - expectedContainer: "initfoo", - name: "init container in flag", - obj: attachPod(), - timeout: 30, + name: "container in flag", + options: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}, GetPodTimeout: 10000000}, + args: []string{"foo"}, + expectedPodName: "foo", + expectedContainerName: "bar", + obj: attachPod(), }, { - p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}}, - args: []string{"foo", "-c", "wrong"}, - expectError: true, - name: "non-existing container in flag", - obj: attachPod(), - timeout: 10, + name: "init container in flag", + options: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "initfoo"}, GetPodTimeout: 30}, + args: []string{"foo"}, + expectedPodName: "foo", + expectedContainerName: "initfoo", + obj: attachPod(), }, { - p: &AttachOptions{}, - args: []string{"pods", "foo"}, - expectedPod: "foo", - name: "no container, no flags, pods and name", - obj: attachPod(), - timeout: 10000, + name: "non-existing container", + options: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "wrong"}, GetPodTimeout: 10}, + args: []string{"foo"}, + expectedPodName: "foo", + expectError: "container not found", + obj: attachPod(), }, { - p: &AttachOptions{}, - args: []string{"pod/foo"}, - expectedPod: "foo", - name: "no container, no flags, pod/name", - obj: attachPod(), - timeout: 1, + name: "no container, no flags, pods and name", + options: &AttachOptions{GetPodTimeout: 10000}, + args: []string{"pods", "foo"}, + expectedPodName: "foo", + expectedContainerName: "bar", + obj: attachPod(), }, { - p: &AttachOptions{}, - args: []string{"pod/foo"}, - expectedPod: "foo", - name: "invalid get pod timeout value", + name: "invalid get pod timeout value", + options: &AttachOptions{GetPodTimeout: 0}, + args: []string{"pod/foo"}, + expectedPodName: "foo", + expectedContainerName: "bar", obj: attachPod(), - expectError: true, - timeout: 0, + expectError: "must be higher than zero", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory().WithNamespace("test") - defer tf.Cleanup() - - codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - ns := legacyscheme.Codecs + // setup opts to fetch our test pod + test.options.AttachablePodFn = fakeAttachablePodFn(test.obj) + test.options.Resources = test.args - tf.Client = &fake.RESTClient{ - GroupVersion: schema.GroupVersion{Group: "", Version: "v1"}, - NegotiatedSerializer: ns, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if test.obj != nil { - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, test.obj)}, nil - } - return nil, nil - }), + if err := test.options.Validate(); err != nil { + if !strings.Contains(err.Error(), test.expectError) { + t.Errorf("unexpected error: expected %q, got %q", test.expectError, err) + } + return } - tf.ClientConfigVal = defaultClientConfig() - cmd := &cobra.Command{} - options := test.p - cmdutil.AddPodRunningTimeoutFlag(cmd, test.timeout) - - err := options.Complete(tf, cmd, test.args) - if test.expectError && err == nil { - t.Errorf("%s: unexpected non-error", test.name) + pod, err := test.options.findAttachablePod(&corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "test"}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foobar", + }, + }, + }, + }) + if err != nil { + if !strings.Contains(err.Error(), test.expectError) { + t.Errorf("unexpected error: expected %q, got %q", err, test.expectError) + } + return } - if !test.expectError && err != nil { - t.Errorf("%s: unexpected error: %v", test.name, err) + + if pod.Name != test.expectedPodName { + t.Errorf("unexpected pod name: expected %q, got %q", test.expectedContainerName, pod.Name) } + + container, err := test.options.containerToAttachTo(attachPod()) if err != nil { + if !strings.Contains(err.Error(), test.expectError) { + t.Errorf("unexpected error: expected %q, got %q", err, test.expectError) + } return } - if options.PodName != test.expectedPod { - t.Errorf("%s: expected: %s, got: %s", test.name, test.expectedPod, options.PodName) + + if container.Name != test.expectedContainerName { + t.Errorf("unexpected container name: expected %q, got %q", test.expectedContainerName, container.Name) + } + + if test.options.PodName != test.expectedPodName { + t.Errorf("%s: expected: %s, got: %s", test.name, test.expectedPodName, test.options.PodName) } - if options.ContainerName != test.expectedContainer { - t.Errorf("%s: expected: %s, got: %s", test.name, test.expectedContainer, options.ContainerName) + + if len(test.expectError) > 0 { + t.Fatalf("expected error %q, but saw none", test.expectError) } }) } @@ -186,7 +190,7 @@ func TestAttach(t *testing.T) { version := "v1" tests := []struct { name, version, podPath, fetchPodPath, attachPath, container string - pod *api.Pod + pod *corev1.Pod remoteAttachErr bool exepctedErr string }{ @@ -247,11 +251,12 @@ func TestAttach(t *testing.T) { }), } tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}} + remoteAttach := &fakeRemoteAttach{} if test.remoteAttachErr { remoteAttach.err = fmt.Errorf("attach error") } - params := &AttachOptions{ + options := &AttachOptions{ StreamOptions: StreamOptions{ ContainerName: test.container, IOStreams: genericclioptions.NewTestIOStreamsDiscard(), @@ -259,12 +264,24 @@ func TestAttach(t *testing.T) { Attach: remoteAttach, GetPodTimeout: 1000, } - cmd := &cobra.Command{} - cmdutil.AddPodRunningTimeoutFlag(cmd, 1000) - if err := params.Complete(tf, cmd, []string{"foo"}); err != nil { - t.Fatal(err) + + options.restClientGetter = tf + options.Namespace = "test" + options.Resources = []string{"foo"} + options.Builder = tf.NewBuilder + options.AttachablePodFn = fakeAttachablePodFn(test.pod) + options.AttachFunc = func(opts *AttachOptions, containerToAttach *corev1.Container, raw bool, sizeQueue remotecommand.TerminalSizeQueue) func() error { + return func() error { + u, err := url.Parse(fmt.Sprintf("%s?container=%s", test.attachPath, containerToAttach.Name)) + if err != nil { + return err + } + + return options.Attach.Attach("POST", u, nil, nil, nil, nil, raw, sizeQueue) + } } - err := params.Run() + + err := options.Run() if test.exepctedErr != "" && err.Error() != test.exepctedErr { t.Errorf("%s: Unexpected exec error: %v", test.name, err) return @@ -294,7 +311,7 @@ func TestAttachWarnings(t *testing.T) { version := "v1" tests := []struct { name, container, version, podPath, fetchPodPath, expectedErr string - pod *api.Pod + pod *corev1.Pod stdin, tty bool }{ { @@ -313,6 +330,8 @@ func TestAttachWarnings(t *testing.T) { tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() + streams, _, _, bufErr := genericclioptions.NewTestIOStreams() + codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) ns := legacyscheme.Codecs @@ -328,30 +347,42 @@ func TestAttachWarnings(t *testing.T) { body := objBody(codec, test.pod) return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil default: - t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req) + t.Errorf("%s: unexpected request: %s %#v\n%#v", p, req.Method, req.URL, req) return nil, fmt.Errorf("unexpected request") } }), } tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}} - streams, _, _, bufErr := genericclioptions.NewTestIOStreams() - ex := &fakeRemoteAttach{} - params := &AttachOptions{ + + options := &AttachOptions{ StreamOptions: StreamOptions{ - ContainerName: test.container, - IOStreams: streams, Stdin: test.stdin, TTY: test.tty, + ContainerName: test.container, + IOStreams: streams, }, - Attach: ex, + + Attach: &fakeRemoteAttach{}, GetPodTimeout: 1000, } - cmd := &cobra.Command{} - cmdutil.AddPodRunningTimeoutFlag(cmd, 1000) - if err := params.Complete(tf, cmd, []string{"foo"}); err != nil { - t.Fatal(err) + + options.restClientGetter = tf + options.Namespace = "test" + options.Resources = []string{"foo"} + options.Builder = tf.NewBuilder + options.AttachablePodFn = fakeAttachablePodFn(test.pod) + options.AttachFunc = func(opts *AttachOptions, containerToAttach *corev1.Container, raw bool, sizeQueue remotecommand.TerminalSizeQueue) func() error { + return func() error { + u, err := url.Parse("http://foo.bar") + if err != nil { + return err + } + + return options.Attach.Attach("POST", u, nil, nil, nil, nil, raw, sizeQueue) + } } - if err := params.Run(); err != nil { + + if err := options.Run(); err != nil { t.Fatal(err) } @@ -367,25 +398,25 @@ func TestAttachWarnings(t *testing.T) { } } -func attachPod() *api.Pod { - return &api.Pod{ +func attachPod() *corev1.Pod { + return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{ + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyAlways, + DNSPolicy: corev1.DNSClusterFirst, + Containers: []corev1.Container{ { Name: "bar", }, }, - InitContainers: []api.Container{ + InitContainers: []corev1.Container{ { Name: "initfoo", }, }, }, - Status: api.PodStatus{ - Phase: api.PodRunning, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, }, } } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go index 79acc9af55e4..d8b8191b0aec 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go @@ -25,11 +25,10 @@ import ( "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" @@ -80,6 +79,22 @@ type LogsOptions struct { ResourceArg string AllContainers bool Options runtime.Object + Resources []string + + ConsumeRequestFn func(*rest.Request, io.Writer) error + // PodLogOptions + SinceTime string + SinceSeconds time.Duration + Follow bool + Previous bool + Timestamps bool + LimitBytes int64 + Tail int64 + Container string + // whether or not a container name was given via --container + ContainerNameSpecified bool + Interactive bool + Selector string Object runtime.Object GetPodTimeout time.Duration @@ -93,6 +108,7 @@ func NewLogsOptions(streams genericclioptions.IOStreams, allContainers bool) *Lo return &LogsOptions{ IOStreams: streams, AllContainers: allContainers, + Tail: -1, } } @@ -119,40 +135,74 @@ func NewCmdLogs(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C Aliases: []string{"log"}, } cmd.Flags().BoolVar(&o.AllContainers, "all-containers", o.AllContainers, "Get all containers's logs in the pod(s).") - cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.") - cmd.Flags().Bool("timestamps", false, "Include timestamps on each line in the log output") - cmd.Flags().Int64("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.") - cmd.Flags().BoolP("previous", "p", false, "If true, print the logs for the previous instance of the container in a pod if it exists.") - cmd.Flags().Int64("tail", -1, "Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.") - cmd.Flags().String("since-time", "", i18n.T("Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")) - cmd.Flags().Duration("since", 0, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.") - cmd.Flags().StringP("container", "c", "", "Print the logs of this container") - cmd.Flags().Bool("interactive", false, "If true, prompt the user for input when required.") + cmd.Flags().BoolVarP(&o.Follow, "follow", "f", o.Follow, "Specify if the logs should be streamed.") + cmd.Flags().BoolVar(&o.Timestamps, "timestamps", o.Timestamps, "Include timestamps on each line in the log output") + cmd.Flags().Int64Var(&o.LimitBytes, "limit-bytes", o.LimitBytes, "Maximum bytes of logs to return. Defaults to no limit.") + cmd.Flags().BoolVarP(&o.Previous, "previous", "p", o.Previous, "If true, print the logs for the previous instance of the container in a pod if it exists.") + cmd.Flags().Int64Var(&o.Tail, "tail", o.Tail, "Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.") + cmd.Flags().StringVar(&o.SinceTime, "since-time", o.SinceTime, i18n.T("Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")) + cmd.Flags().DurationVar(&o.SinceSeconds, "since", o.SinceSeconds, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.") + cmd.Flags().StringVarP(&o.Container, "container", "c", o.Container, "Print the logs of this container") + cmd.Flags().BoolVar(&o.Interactive, "interactive", o.Interactive, "If true, prompt the user for input when required.") cmd.Flags().MarkDeprecated("interactive", "This flag is no longer respected and there is no replacement.") cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") return cmd } +func (o *LogsOptions) ToLogOptions() (*corev1.PodLogOptions, error) { + logOptions := &corev1.PodLogOptions{ + Container: o.Container, + Follow: o.Follow, + Previous: o.Previous, + Timestamps: o.Timestamps, + } + + if len(o.SinceTime) > 0 { + t, err := util.ParseRFC3339(o.SinceTime, metav1.Now) + if err != nil { + return nil, err + } + + logOptions.SinceTime = &t + } + + if o.LimitBytes != 0 { + logOptions.LimitBytes = &o.LimitBytes + } + + if o.SinceSeconds != 0 { + // round up to the nearest second + sec := int64(o.SinceSeconds.Round(time.Second).Seconds()) + logOptions.SinceSeconds = &sec + } + + if len(o.Selector) > 0 && o.Tail != -1 { + logOptions.TailLines = &selectorTail + } else if o.Tail != -1 { + logOptions.TailLines = &o.Tail + } + + return logOptions, nil +} + func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { - containerName := cmdutil.GetFlagString(cmd, "container") - selector := cmdutil.GetFlagString(cmd, "selector") + o.ContainerNameSpecified = cmd.Flag("container").Changed + o.Resources = args + switch len(args) { case 0: - if len(selector) == 0 { + if len(o.Selector) == 0 { return cmdutil.UsageErrorf(cmd, "%s", logsUsageStr) } case 1: o.ResourceArg = args[0] - if len(selector) != 0 { + if len(o.Selector) != 0 { return cmdutil.UsageErrorf(cmd, "only a selector (-l) or a POD name is allowed") } case 2: - if cmd.Flag("container").Changed { - return cmdutil.UsageErrorf(cmd, "only one of -c or an inline [CONTAINER] arg is allowed") - } o.ResourceArg = args[0] - containerName = args[1] + o.Container = args[1] default: return cmdutil.UsageErrorf(cmd, "%s", logsUsageStr) } @@ -162,48 +212,21 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str return err } - logOptions := &api.PodLogOptions{ - Container: containerName, - Follow: cmdutil.GetFlagBool(cmd, "follow"), - Previous: cmdutil.GetFlagBool(cmd, "previous"), - Timestamps: cmdutil.GetFlagBool(cmd, "timestamps"), - } - if sinceTime := cmdutil.GetFlagString(cmd, "since-time"); len(sinceTime) > 0 { - t, err := util.ParseRFC3339(sinceTime, metav1.Now) - if err != nil { - return err - } - logOptions.SinceTime = &t - } - if limit := cmdutil.GetFlagInt64(cmd, "limit-bytes"); limit != 0 { - logOptions.LimitBytes = &limit - } - tail := cmdutil.GetFlagInt64(cmd, "tail") - if tail != -1 { - logOptions.TailLines = &tail - } - if sinceSeconds := cmdutil.GetFlagDuration(cmd, "since"); sinceSeconds != 0 { - // round up to the nearest second - sec := int64(sinceSeconds.Round(time.Second).Seconds()) - logOptions.SinceSeconds = &sec - } + o.ConsumeRequestFn = consumeRequest + o.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd) if err != nil { return err } - o.Options = logOptions - o.RESTClientGetter = f - o.LogsForObject = polymorphichelpers.LogsForObjectFn - if len(selector) != 0 { - if logOptions.Follow { - return cmdutil.UsageErrorf(cmd, "only one of follow (-f) or selector (-l) is allowed") - } - if logOptions.TailLines == nil && tail != -1 { - logOptions.TailLines = &selectorTail - } + o.Options, err = o.ToLogOptions() + if err != nil { + return err } + o.RESTClientGetter = f + o.LogsForObject = polymorphichelpers.LogsForObjectFn + if o.Object == nil { builder := f.NewBuilder(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). @@ -212,14 +235,14 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str if o.ResourceArg != "" { builder.ResourceNames("pods", o.ResourceArg) } - if selector != "" { - builder.ResourceTypes("pods").LabelSelectorParam(selector) + if o.Selector != "" { + builder.ResourceTypes("pods").LabelSelectorParam(o.Selector) } infos, err := builder.Do().Infos() if err != nil { return err } - if selector == "" && len(infos) != 1 { + if o.Selector == "" && len(infos) != 1 { return errors.New("expected a resource") } o.Object = infos[0].Object @@ -229,15 +252,36 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str } func (o LogsOptions) Validate() error { - logsOptions, ok := o.Options.(*api.PodLogOptions) + if o.Follow && len(o.Selector) > 0 { + return fmt.Errorf("only one of follow (-f) or selector (-l) is allowed") + } + + if len(o.SinceTime) > 0 && o.SinceSeconds != 0 { + return fmt.Errorf("at most one of `sinceTime` or `sinceSeconds` may be specified") + } + + logsOptions, ok := o.Options.(*corev1.PodLogOptions) if !ok { return errors.New("unexpected logs options object") } if o.AllContainers && len(logsOptions.Container) > 0 { return fmt.Errorf("--all-containers=true should not be specified with container name %s", logsOptions.Container) } - if errs := validation.ValidatePodLogOptions(logsOptions); len(errs) > 0 { - return errs.ToAggregate() + + if o.ContainerNameSpecified && len(o.Resources) == 2 { + return fmt.Errorf("only one of -c or an inline [CONTAINER] arg is allowed") + } + + if o.LimitBytes < 0 { + return fmt.Errorf("--limit-bytes must be greater than 0") + } + + if logsOptions.SinceSeconds != nil && *logsOptions.SinceSeconds < int64(0) { + return fmt.Errorf("--since must be greater than 0") + } + + if logsOptions.TailLines != nil && *logsOptions.TailLines < 0 { + return fmt.Errorf("TailLines must be greater than or equal to 0") } return nil @@ -251,7 +295,7 @@ func (o LogsOptions) RunLogs() error { } for _, request := range requests { - if err := consumeRequest(request, o.Out); err != nil { + if err := o.ConsumeRequestFn(request, o.Out); err != nil { return err } } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go index 03d2dd22a249..934fa5ee8e04 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go @@ -17,29 +17,20 @@ limitations under the License. package cmd import ( - "bytes" "errors" - "io/ioutil" - "net/http" + "fmt" + "io" "strings" "testing" "time" - "github.com/spf13/cobra" - - "fmt" - + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" restclient "k8s.io/client-go/rest" - "k8s.io/client-go/rest/fake" - "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" - "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" - "k8s.io/kubernetes/pkg/kubectl/scheme" ) func TestLog(t *testing.T) { @@ -48,11 +39,8 @@ func TestLog(t *testing.T) { pod *api.Pod }{ { - name: "v1 - pod log", - version: "v1", - podPath: "/namespaces/test/pods/foo", - logPath: "/api/v1/namespaces/test/pods/foo/log", - pod: testPod(), + name: "v1 - pod log", + pod: testPod(), }, } for _, test := range tests { @@ -61,41 +49,19 @@ func TestLog(t *testing.T) { tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - ns := legacyscheme.Codecs - - tf.Client = &fake.RESTClient{ - NegotiatedSerializer: ns, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil - case p == test.logPath && m == "GET": - body := ioutil.NopCloser(bytes.NewBufferString(logContent)) - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil - default: - t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) - return nil, nil - } - }), - } - tf.ClientConfigVal = defaultClientConfig() - oldLogFn := polymorphichelpers.LogsForObjectFn - defer func() { - polymorphichelpers.LogsForObjectFn = oldLogFn - }() - clientset, err := tf.ClientSet() - if err != nil { - t.Fatal(err) - } - polymorphichelpers.LogsForObjectFn = logTestMock{client: clientset}.logsForObject - streams, _, buf, _ := genericclioptions.NewTestIOStreams() - cmd := NewCmdLogs(tf, streams) - cmd.Flags().Set("namespace", "test") - cmd.Run(cmd, []string{"foo"}) + mock := &logTestMock{ + logsContent: logContent, + } + + opts := NewLogsOptions(streams, false) + opts.Namespace = "test" + opts.Object = test.pod + opts.Options = &corev1.PodLogOptions{} + opts.LogsForObject = mock.mockLogsForObject + opts.ConsumeRequestFn = mock.mockConsumeRequest + opts.RunLogs() if buf.String() != logContent { t.Errorf("%s: did not get expected log content. Got: %s", test.name, buf.String()) @@ -119,66 +85,152 @@ func testPod() *api.Pod { } } -func TestValidateLogFlags(t *testing.T) { +func TestValidateLogOptions(t *testing.T) { f := cmdtesting.NewTestFactory() defer f.Cleanup() f.WithNamespace("") tests := []struct { name string - flags map[string]string args []string + opts func(genericclioptions.IOStreams) *LogsOptions expected string }{ { - name: "since & since-time", - flags: map[string]string{"since": "1h", "since-time": "2006-01-02T15:04:05Z"}, + name: "since & since-time", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.SinceSeconds = time.Hour + o.SinceTime = "2006-01-02T15:04:05Z" + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "at most one of `sinceTime` or `sinceSeconds` may be specified", }, { - name: "negative since-time", - flags: map[string]string{"since": "-1s"}, + name: "negative since-time", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.SinceSeconds = -1 * time.Second + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "must be greater than 0", }, { - name: "negative limit-bytes", - flags: map[string]string{"limit-bytes": "-100"}, + name: "negative limit-bytes", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.LimitBytes = -100 + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "must be greater than 0", }, { - name: "negative tail", - flags: map[string]string{"tail": "-100"}, + name: "negative tail", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Tail = -100 + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"foo"}, expected: "must be greater than or equal to 0", }, { - name: "container name combined with --all-containers", - flags: map[string]string{"all-containers": "true"}, + name: "container name combined with --all-containers", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, true) + o.Container = "my-container" + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, args: []string{"my-pod", "my-container"}, expected: "--all-containers=true should not be specified with container", }, + { + name: "container name combined with second argument", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Container = "my-container" + o.ContainerNameSpecified = true + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, + args: []string{"my-pod", "my-container"}, + expected: "only one of -c or an inline", + }, + { + name: "follow and selector conflict", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Selector = "foo" + o.Follow = true + + var err error + o.Options, err = o.ToLogOptions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + return o + }, + expected: "only one of follow (-f) or selector (-l) is allowed", + }, } for _, test := range tests { streams := genericclioptions.NewTestIOStreamsDiscard() - cmd := NewCmdLogs(f, streams) - out := "" - for flag, value := range test.flags { - cmd.Flags().Set(flag, value) - } - // checkErr breaks tests in case of errors, plus we just - // need to check errors returned by the command validation - o := NewLogsOptions(streams, test.flags["all-containers"] == "true") - cmd.Run = func(cmd *cobra.Command, args []string) { - o.Complete(f, cmd, args) - out = o.Validate().Error() + + o := test.opts(streams) + o.Resources = test.args + + err := o.Validate() + if err == nil { + t.Fatalf("expected error %q, got none", test.expected) } - cmd.Run(cmd, test.args) - if !strings.Contains(out, test.expected) { - t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, out) + if !strings.Contains(err.Error(), test.expected) { + t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, err.Error()) } } } @@ -190,49 +242,49 @@ func TestLogComplete(t *testing.T) { tests := []struct { name string args []string - flags map[string]string + opts func(genericclioptions.IOStreams) *LogsOptions expected string }{ { - name: "No args case", - flags: map[string]string{"selector": ""}, + name: "No args case", + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + return NewLogsOptions(streams, false) + }, expected: "'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command", }, { - name: "One args case", - args: []string{"foo"}, - flags: map[string]string{"selector": "foo"}, + name: "One args case", + args: []string{"foo"}, + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Selector = "foo" + return o + }, expected: "only a selector (-l) or a POD name is allowed", }, { - name: "Two args case", - args: []string{"foo", "foo1"}, - flags: map[string]string{"container": "foo1"}, - expected: "only one of -c or an inline [CONTAINER] arg is allowed", - }, - { - name: "More than two args case", - args: []string{"foo", "foo1", "foo2"}, - flags: map[string]string{"tail": "1"}, + name: "More than two args case", + args: []string{"foo", "foo1", "foo2"}, + opts: func(streams genericclioptions.IOStreams) *LogsOptions { + o := NewLogsOptions(streams, false) + o.Tail = 1 + return o + }, expected: "'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command", }, - { - name: "follow and selecter conflict", - flags: map[string]string{"selector": "foo", "follow": "true"}, - expected: "only one of follow (-f) or selector (-l) is allowed", - }, } for _, test := range tests { cmd := NewCmdLogs(f, genericclioptions.NewTestIOStreamsDiscard()) - var err error out := "" - for flag, value := range test.flags { - cmd.Flags().Set(flag, value) - } + // checkErr breaks tests in case of errors, plus we just // need to check errors returned by the command validation - o := NewLogsOptions(genericclioptions.NewTestIOStreamsDiscard(), false) - err = o.Complete(f, cmd, test.args) + o := test.opts(genericclioptions.NewTestIOStreamsDiscard()) + err := o.Complete(f, cmd, test.args) + if err == nil { + t.Fatalf("expected error %q, got none", test.expected) + } + out = err.Error() if !strings.Contains(out, test.expected) { t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, out) @@ -241,17 +293,23 @@ func TestLogComplete(t *testing.T) { } type logTestMock struct { - client internalclientset.Interface + logsContent string } -func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*restclient.Request, error) { - switch t := object.(type) { +func (l *logTestMock) mockConsumeRequest(req *restclient.Request, out io.Writer) error { + fmt.Fprintf(out, l.logsContent) + return nil +} + +func (l *logTestMock) mockLogsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*restclient.Request, error) { + switch object.(type) { case *api.Pod: - opts, ok := options.(*api.PodLogOptions) + _, ok := options.(*corev1.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } - return []*restclient.Request{m.client.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil + + return []*restclient.Request{{}}, nil default: return nil, fmt.Errorf("cannot get the logs from %T", object) } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go index 3baa3bee0b1a..1664316337df 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go @@ -34,6 +34,7 @@ import ( "k8s.io/client-go/transport/spdy" "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" + apiv1 "k8s.io/kubernetes/pkg/apis/core/v1" coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -207,7 +208,13 @@ func (o *PortForwardOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg // handle service port mapping to target port if needed switch t := obj.(type) { case *api.Service: - o.Ports, err = translateServicePortToTargetPort(args[1:], *t, *forwardablePod) + // TODO(juanvallejo): remove this once we convert this command to work with externals + internalPod := &api.Pod{} + if err := apiv1.Convert_v1_Pod_To_core_Pod(forwardablePod, internalPod, nil); err != nil { + return err + } + + o.Ports, err = translateServicePortToTargetPort(args[1:], *t, *internalPod) if err != nil { return err } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go index 54f3c17e5ccc..5652ac9e88b0 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/run.go @@ -22,18 +22,20 @@ import ( "github.com/docker/distribution/reference" "github.com/spf13/cobra" - "k8s.io/client-go/dynamic" - "github.com/golang/glog" + + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + coreclientv1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" - coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -394,27 +396,27 @@ func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e return err } opts.Config = config + opts.AttachFunc = defaultAttachFunc - clientset, err := f.ClientSet() + clientset, err := kubernetes.NewForConfig(config) if err != nil { return err } - opts.PodClient = clientset.Core() attachablePod, err := polymorphichelpers.AttachablePodForObjectFn(f, runObject.Object, opts.GetPodTimeout) if err != nil { return err } - err = handleAttachPod(f, clientset.Core(), attachablePod.Namespace, attachablePod.Name, opts) + err = handleAttachPod(f, clientset.CoreV1(), attachablePod.Namespace, attachablePod.Name, opts) if err != nil { return err } - var pod *api.Pod + var pod *corev1.Pod leaveStdinOpen := o.LeaveStdinOpen waitForExitCode := !leaveStdinOpen && restartPolicy == api.RestartPolicyNever if waitForExitCode { - pod, err = waitForPod(clientset.Core(), attachablePod.Namespace, attachablePod.Name, kubectl.PodCompleted) + pod, err = waitForPod(clientset.CoreV1(), attachablePod.Namespace, attachablePod.Name, kubectl.PodCompleted) if err != nil { return err } @@ -426,9 +428,9 @@ func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e } switch pod.Status.Phase { - case api.PodSucceeded: + case corev1.PodSucceeded: return nil - case api.PodFailed: + case corev1.PodFailed: unknownRcErr := fmt.Errorf("pod %s/%s failed with unknown exit code", pod.Namespace, pod.Name) if len(pod.Status.ContainerStatuses) == 0 || pod.Status.ContainerStatuses[0].State.Terminated == nil { return unknownRcErr @@ -483,20 +485,20 @@ func (o *RunOptions) removeCreatedObjects(f cmdutil.Factory, createdObjects []*R } // waitForPod watches the given pod until the exitCondition is true -func waitForPod(podClient coreclient.PodsGetter, ns, name string, exitCondition watch.ConditionFunc) (*api.Pod, error) { +func waitForPod(podClient coreclientv1.PodsGetter, ns, name string, exitCondition watch.ConditionFunc) (*corev1.Pod, error) { w, err := podClient.Pods(ns).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: name})) if err != nil { return nil, err } intr := interrupt.New(nil, w.Stop) - var result *api.Pod + var result *corev1.Pod err = intr.Run(func() error { ev, err := watch.Until(0, w, func(ev watch.Event) (bool, error) { return exitCondition(ev) }) if ev != nil { - result = ev.Object.(*api.Pod) + result = ev.Object.(*corev1.Pod) } return err }) @@ -509,37 +511,39 @@ func waitForPod(podClient coreclient.PodsGetter, ns, name string, exitCondition return result, err } -func handleAttachPod(f cmdutil.Factory, podClient coreclient.PodsGetter, ns, name string, opts *AttachOptions) error { +func handleAttachPod(f cmdutil.Factory, podClient coreclientv1.PodsGetter, ns, name string, opts *AttachOptions) error { pod, err := waitForPod(podClient, ns, name, kubectl.PodRunningAndReady) if err != nil && err != kubectl.ErrPodCompleted { return err } - if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed { + if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed { return logOpts(f, pod, opts) } - opts.PodClient = podClient + opts.Pod = pod opts.PodName = name opts.Namespace = ns - // TODO: opts.Run sets opts.Err to nil, we need to find a better way - stderr := opts.ErrOut + if opts.AttachFunc == nil { + opts.AttachFunc = defaultAttachFunc + } + if err := opts.Run(); err != nil { - fmt.Fprintf(stderr, "Error attaching, falling back to logs: %v\n", err) + fmt.Fprintf(opts.ErrOut, "Error attaching, falling back to logs: %v\n", err) return logOpts(f, pod, opts) } return nil } // logOpts logs output from opts to the pods log. -func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *api.Pod, opts *AttachOptions) error { +func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *corev1.Pod, opts *AttachOptions) error { ctrName, err := opts.GetContainerName(pod) if err != nil { return err } - requests, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout, false) + requests, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &corev1.PodLogOptions{Container: ctrName}, opts.GetPodTimeout, false) if err != nil { return err } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/conditions.go b/vendor/k8s.io/kubernetes/pkg/kubectl/conditions.go index 771ccbc4fb8d..d28b74ed53f3 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/conditions.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/conditions.go @@ -19,12 +19,14 @@ package kubectl import ( "fmt" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" "k8s.io/kubernetes/pkg/api/pod" + podv1 "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/apis/apps" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/extensions" @@ -140,6 +142,13 @@ func PodRunning(event watch.Event) (bool, error) { case api.PodFailed, api.PodSucceeded: return false, ErrPodCompleted } + case *corev1.Pod: + switch t.Status.Phase { + case corev1.PodRunning: + return true, nil + case corev1.PodFailed, corev1.PodSucceeded: + return false, ErrPodCompleted + } } return false, nil } @@ -157,6 +166,11 @@ func PodCompleted(event watch.Event) (bool, error) { case api.PodFailed, api.PodSucceeded: return true, nil } + case *corev1.Pod: + switch t.Status.Phase { + case corev1.PodFailed, corev1.PodSucceeded: + return true, nil + } } return false, nil } @@ -177,6 +191,13 @@ func PodRunningAndReady(event watch.Event) (bool, error) { case api.PodRunning: return pod.IsPodReady(t), nil } + case *corev1.Pod: + switch t.Status.Phase { + case corev1.PodFailed, corev1.PodSucceeded: + return false, ErrPodCompleted + case corev1.PodRunning: + return podv1.IsPodReady(t), nil + } } return false, nil } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/BUILD b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/BUILD index 08132fceba2c..3489772ac4d1 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/BUILD +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/BUILD @@ -28,8 +28,6 @@ go_library( "//pkg/apis/core:go_default_library", "//pkg/apis/core/v1:go_default_library", "//pkg/apis/extensions:go_default_library", - "//pkg/client/clientset_generated/internalclientset:go_default_library", - "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", "//pkg/controller:go_default_library", "//pkg/kubectl:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library", @@ -49,6 +47,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", ], ) @@ -67,7 +66,6 @@ go_test( "//pkg/apis/batch:go_default_library", "//pkg/apis/core:go_default_library", "//pkg/apis/extensions:go_default_library", - "//pkg/client/clientset_generated/internalclientset/fake:go_default_library", "//pkg/controller:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", @@ -78,6 +76,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", ], ) diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/attachablepodforobject.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/attachablepodforobject.go index 5e8b7f1850ed..0e1cdbdc48dc 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/attachablepodforobject.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/attachablepodforobject.go @@ -24,30 +24,29 @@ import ( "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" api "k8s.io/kubernetes/pkg/apis/core" apiv1 "k8s.io/kubernetes/pkg/apis/core/v1" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) // attachablePodForObject returns the pod to which to attach given an object. -func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*api.Pod, error) { +func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*corev1.Pod, error) { switch t := object.(type) { case *api.Pod: - return t, nil + externalPod := &corev1.Pod{} + err := apiv1.Convert_core_Pod_To_v1_Pod(t, externalPod, nil) + return externalPod, err case *corev1.Pod: - internalPod := &api.Pod{} - err := apiv1.Convert_v1_Pod_To_core_Pod(t, internalPod, nil) - return internalPod, err - + return t, nil } clientConfig, err := restClientGetter.ToRESTConfig() if err != nil { return nil, err } - clientset, err := internalclientset.NewForConfig(clientConfig) + clientset, err := corev1client.NewForConfig(clientConfig) if err != nil { return nil, err } @@ -57,6 +56,6 @@ func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter, return nil, fmt.Errorf("cannot attach to %T: %v", object, err) } sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } - pod, _, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy) + pod, _, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy) return pod, err } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers.go index 5d9f0c3336f8..3e1ba59ccebb 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers.go @@ -32,17 +32,16 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" + coreclient "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/batch" api "k8s.io/kubernetes/pkg/apis/core" - apiv1 "k8s.io/kubernetes/pkg/apis/core/v1" "k8s.io/kubernetes/pkg/apis/extensions" - coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" ) // GetFirstPod returns a pod matching the namespace and label selector // and the number of all pods that match the label selector. -func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string, timeout time.Duration, sortBy func([]*v1.Pod) sort.Interface) (*api.Pod, int, error) { +func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string, timeout time.Duration, sortBy func([]*v1.Pod) sort.Interface) (*v1.Pod, int, error) { options := metav1.ListOptions{LabelSelector: selector} podList, err := client.Pods(namespace).List(options) @@ -52,15 +51,11 @@ func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string pods := []*v1.Pod{} for i := range podList.Items { pod := podList.Items[i] - externalPod := &v1.Pod{} - apiv1.Convert_core_Pod_To_v1_Pod(&pod, externalPod, nil) - pods = append(pods, externalPod) + pods = append(pods, &pod) } if len(pods) > 0 { sort.Sort(sortBy(pods)) - internalPod := &api.Pod{} - apiv1.Convert_v1_Pod_To_core_Pod(pods[0], internalPod, nil) - return internalPod, len(podList.Items), nil + return pods[0], len(podList.Items), nil } // Watch until we observe a pod @@ -78,7 +73,7 @@ func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string if err != nil { return nil, 0, err } - pod, ok := event.Object.(*api.Pod) + pod, ok := event.Object.(*v1.Pod) if !ok { return nil, 0, fmt.Errorf("%#v is not a pod event", event) } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers_test.go index 98a06d5f4671..91e582f8aa6c 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/helpers_test.go @@ -22,15 +22,13 @@ import ( "testing" "time" - "k8s.io/api/core/v1" - + corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/watch" + fakeexternal "k8s.io/client-go/kubernetes/fake" testcore "k8s.io/client-go/testing" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/controller" ) @@ -39,30 +37,30 @@ func TestGetFirstPod(t *testing.T) { tests := []struct { name string - podList *api.PodList + podList *corev1.PodList watching []watch.Event - sortBy func([]*v1.Pod) sort.Interface + sortBy func([]*corev1.Pod) sort.Interface - expected *api.Pod + expected *corev1.Pod expectedNum int expectedErr bool }{ { name: "kubectl logs - two ready pods", podList: newPodList(2, -1, -1, labelSet), - sortBy: func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }, - expected: &api.Pod{ + sortBy: func(pods []*corev1.Pod) sort.Interface { return controller.ByLogging(pods) }, + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: metav1.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: corev1.ConditionTrue, + Type: corev1.PodReady, }, }, }, @@ -72,22 +70,22 @@ func TestGetFirstPod(t *testing.T) { { name: "kubectl logs - one unhealthy, one healthy", podList: newPodList(2, -1, 1, labelSet), - sortBy: func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }, - expected: &api.Pod{ + sortBy: func(pods []*corev1.Pod) sort.Interface { return controller.ByLogging(pods) }, + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-2", Namespace: metav1.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 1, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: corev1.ConditionTrue, + Type: corev1.PodReady, }, }, - ContainerStatuses: []api.ContainerStatus{{RestartCount: 5}}, + ContainerStatuses: []corev1.ContainerStatus{{RestartCount: 5}}, }, }, expectedNum: 2, @@ -95,19 +93,19 @@ func TestGetFirstPod(t *testing.T) { { name: "kubectl attach - two ready pods", podList: newPodList(2, -1, -1, labelSet), - sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }, - expected: &api.Pod{ + sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }, + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: metav1.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: corev1.ConditionTrue, + Type: corev1.PodReady, }, }, }, @@ -120,37 +118,37 @@ func TestGetFirstPod(t *testing.T) { watching: []watch.Event{ { Type: watch.Modified, - Object: &api.Pod{ + Object: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: metav1.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: corev1.ConditionTrue, + Type: corev1.PodReady, }, }, }, }, }, }, - sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }, - expected: &api.Pod{ + sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }, + expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: metav1.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: corev1.ConditionTrue, + Type: corev1.PodReady, }, }, }, @@ -161,7 +159,7 @@ func TestGetFirstPod(t *testing.T) { for i := range tests { test := tests[i] - fake := fake.NewSimpleClientset(test.podList) + fake := fakeexternal.NewSimpleClientset(test.podList) if len(test.watching) > 0 { watcher := watch.NewFake() for _, event := range test.watching { @@ -196,21 +194,21 @@ func TestGetFirstPod(t *testing.T) { } } -func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *api.PodList { - pods := []api.Pod{} +func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *corev1.PodList { + pods := []corev1.Pod{} for i := 0; i < count; i++ { - newPod := api.Pod{ + newPod := corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("pod-%d", i+1), Namespace: metav1.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, i, 0, time.UTC), Labels: labels, }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: corev1.ConditionTrue, + Type: corev1.PodReady, }, }, }, @@ -218,12 +216,12 @@ func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *ap pods = append(pods, newPod) } if isUnready > -1 && isUnready < count { - pods[isUnready].Status.Conditions[0].Status = api.ConditionFalse + pods[isUnready].Status.Conditions[0].Status = corev1.ConditionFalse } if isUnhealthy > -1 && isUnhealthy < count { - pods[isUnhealthy].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 5}} + pods[isUnhealthy].Status.ContainerStatuses = []corev1.ContainerStatus{{RestartCount: 5}} } - return &api.PodList{ + return &corev1.PodList{ Items: pods, } } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go index 7904616a4f74..0cb2e1725ad1 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/interface.go @@ -24,7 +24,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/rest" - api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) @@ -36,7 +35,7 @@ type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, var LogsForObjectFn LogsForObjectFunc = logsForObject // AttachableLogsForObjectFunc is a function type that can tell you how to get the pod for which to attach a given object -type AttachableLogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*api.Pod, error) +type AttachableLogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*v1.Pod, error) // AttachablePodForObjectFn gives a way to easily override the function for unit testing if needed. var AttachablePodForObjectFn AttachableLogsForObjectFunc = attachablePodForObject diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go index 0cb68526516c..4b543711d274 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject.go @@ -26,9 +26,9 @@ import ( "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" coreinternal "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) @@ -38,16 +38,18 @@ func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, if err != nil { return nil, err } - clientset, err := internalclientset.NewForConfig(clientConfig) + + clientset, err := corev1client.NewForConfig(clientConfig) if err != nil { return nil, err } return logsForObjectWithClient(clientset, object, options, timeout, allContainers) } +// TODO: remove internal clientset once all callers use external versions // this is split for easy test-ability -func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) { - opts, ok := options.(*coreinternal.PodLogOptions) +func logsForObjectWithClient(clientset corev1client.CoreV1Interface, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) { + opts, ok := options.(*corev1.PodLogOptions) if !ok { return nil, errors.New("provided options object is not a PodLogOptions") } @@ -78,7 +80,7 @@ func logsForObjectWithClient(clientset internalclientset.Interface, object, opti case *coreinternal.Pod: // if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false if !allContainers { - return []*rest.Request{clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil + return []*rest.Request{clientset.Pods(t.Namespace).GetLogs(t.Name, opts)}, nil } ret := []*rest.Request{} @@ -106,7 +108,7 @@ func logsForObjectWithClient(clientset internalclientset.Interface, object, opti case *corev1.Pod: // if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false if !allContainers { - return []*rest.Request{clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts)}, nil + return []*rest.Request{clientset.Pods(t.Namespace).GetLogs(t.Name, opts)}, nil } ret := []*rest.Request{} @@ -136,8 +138,9 @@ func logsForObjectWithClient(clientset internalclientset.Interface, object, opti if err != nil { return nil, fmt.Errorf("cannot get the logs from %T: %v", object, err) } + sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) } - pod, numPods, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy) + pod, numPods, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy) if err != nil { return nil, err } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go index 3f87f710b53d..09274ef9f969 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/polymorphichelpers/logsforobject_test.go @@ -21,28 +21,29 @@ import ( "testing" "time" + corev1 "k8s.io/api/core/v1" 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" + fakeexternal "k8s.io/client-go/kubernetes/fake" testclient "k8s.io/client-go/testing" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/batch" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" ) var ( - podsResource = schema.GroupVersionResource{Resource: "pods"} - podsKind = schema.GroupVersionKind{Kind: "Pod"} + podsResource = schema.GroupVersionResource{Version: "v1", Resource: "pods"} + podsKind = schema.GroupVersionKind{Version: "v1", Kind: "Pod"} ) func TestLogsForObject(t *testing.T) { tests := []struct { name string obj runtime.Object - opts *api.PodLogOptions + opts *corev1.PodLogOptions pods []runtime.Object actions []testclient.Action }{ @@ -129,8 +130,8 @@ func TestLogsForObject(t *testing.T) { } for _, test := range tests { - fakeClientset := fake.NewSimpleClientset(test.pods...) - _, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second, false) + fakeClientset := fakeexternal.NewSimpleClientset(test.pods...) + _, err := logsForObjectWithClient(fakeClientset.CoreV1(), test.obj, test.opts, 20*time.Second, false) if err != nil { t.Errorf("%s: unexpected error: %v", test.name, err) continue @@ -151,21 +152,21 @@ func TestLogsForObject(t *testing.T) { } func testPod() runtime.Object { - return &api.Pod{ + return &corev1.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"}}, + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyAlways, + DNSPolicy: corev1.DNSClusterFirst, + Containers: []corev1.Container{{Name: "c1"}}, }, } } -func getLogsAction(namespace string, opts *api.PodLogOptions) testclient.Action { +func getLogsAction(namespace string, opts *corev1.PodLogOptions) testclient.Action { action := testclient.GenericActionImpl{} action.Verb = "get" action.Namespace = namespace diff --git a/vendor/k8s.io/kubernetes/test/e2e/kubectl/kubectl.go b/vendor/k8s.io/kubernetes/test/e2e/kubectl/kubectl.go index 3132d2c11a96..b2fb92639fdc 100644 --- a/vendor/k8s.io/kubernetes/test/e2e/kubectl/kubectl.go +++ b/vendor/k8s.io/kubernetes/test/e2e/kubectl/kubectl.go @@ -529,7 +529,7 @@ var _ = SIGDescribe("Kubectl client", func() { ExecOrDie() Expect(runOutput).ToNot(ContainSubstring("stdin closed")) g := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } - runTestPod, _, err := polymorphichelpers.GetFirstPod(f.InternalClientset.Core(), ns, "run=run-test-3", 1*time.Minute, g) + runTestPod, _, err := polymorphichelpers.GetFirstPod(f.ClientSet.CoreV1(), ns, "run=run-test-3", 1*time.Minute, g) if err != nil { os.Exit(1) } From 426c23150f84f3d86719882ed81910846e445284 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Mon, 23 Jul 2018 15:50:36 -0400 Subject: [PATCH 03/12] update extract command --- pkg/oc/cli/extract/extract.go | 50 +++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/pkg/oc/cli/extract/extract.go b/pkg/oc/cli/extract/extract.go index 5123c01bb1ab..20d1ff4e204d 100644 --- a/pkg/oc/cli/extract/extract.go +++ b/pkg/oc/cli/extract/extract.go @@ -10,15 +10,14 @@ import ( "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" - kapi "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" - - "github.com/openshift/origin/pkg/oc/util/ocscheme" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) var ( @@ -55,7 +54,11 @@ type ExtractOptions struct { TargetDirectory string Overwrite bool - VisitorFn func(resource.VisitorFunc) error + Namespace string + ExplicitNamespace bool + Resources []string + Builder func() *resource.Builder + ExtractFileContentsFn func(runtime.Object) (map[string][]byte, bool, error) genericclioptions.IOStreams @@ -99,20 +102,15 @@ func NewCmdExtract(fullName string, f kcmdutil.Factory, streams genericclioption func (o *ExtractOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args []string) error { o.ExtractFileContentsFn = extractFileContents - cmdNamespace, explicit, err := f.ToRawKubeConfigLoader().Namespace() + var err error + o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - b := f.NewBuilder(). - WithScheme(ocscheme.ReadingInternalScheme). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(explicit, &resource.FilenameOptions{Recursive: false, Filenames: o.Filenames}). - ResourceNames("", args...). - ContinueOnError(). - Flatten() + o.Resources = args + o.Builder = f.NewBuilder - o.VisitorFn = b.Do().Visit return nil } @@ -131,9 +129,21 @@ func name(info *resource.Info) string { } func (o *ExtractOptions) Run() error { + r := o.Builder(). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). + NamespaceParam(o.Namespace).DefaultNamespace(). + FilenameParam(o.ExplicitNamespace, &resource.FilenameOptions{Recursive: false, Filenames: o.Filenames}). + ResourceNames("", o.Resources...). + ContinueOnError(). + Flatten().Do() + + if err := r.Err(); err != nil { + return err + } + count := 0 contains := sets.NewString(o.OnlyKeys...) - err := o.VisitorFn(func(info *resource.Info, err error) error { + err := r.Visit(func(info *resource.Info, err error) error { if err != nil { return fmt.Errorf("%s: %v", name(info), err) } @@ -158,7 +168,7 @@ func (o *ExtractOptions) Run() error { } default: target := filepath.Join(o.TargetDirectory, k) - if err := writeToDisk(target, v, o.Overwrite, o.Out); err != nil { + if err := o.writeToDisk(target, v); err != nil { if os.IsExist(err) { err = fmt.Errorf("file exists, pass --confirm to overwrite") } @@ -181,8 +191,8 @@ func (o *ExtractOptions) Run() error { return nil } -func writeToDisk(path string, data []byte, overwrite bool, out io.Writer) error { - if overwrite { +func (o *ExtractOptions) writeToDisk(path string, data []byte) error { + if o.Overwrite { if err := ioutil.WriteFile(path, data, 0600); err != nil { return err } @@ -199,7 +209,7 @@ func writeToDisk(path string, data []byte, overwrite bool, out io.Writer) error return err } } - fmt.Fprintf(out, "%s\n", path) + fmt.Fprintf(o.Out, "%s\n", path) return nil } @@ -207,9 +217,9 @@ func writeToDisk(path string, data []byte, overwrite bool, out io.Writer) error // operation, or an error. func extractFileContents(obj runtime.Object) (map[string][]byte, bool, error) { switch t := obj.(type) { - case *kapi.Secret: + case *corev1.Secret: return t.Data, true, nil - case *kapi.ConfigMap: + case *corev1.ConfigMap: out := make(map[string][]byte) for k, v := range t.Data { out[k] = []byte(v) From 7fa513e08a13d7f93bf3edc14af4302bceb639c5 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Mon, 23 Jul 2018 16:02:11 -0400 Subject: [PATCH 04/12] update rsync --- pkg/oc/cli/rsync/pathspec.go | 5 ++--- pkg/oc/cli/rsync/rsync.go | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/oc/cli/rsync/pathspec.go b/pkg/oc/cli/rsync/pathspec.go index dfef2ed983d4..3ff19a911209 100644 --- a/pkg/oc/cli/rsync/pathspec.go +++ b/pkg/oc/cli/rsync/pathspec.go @@ -9,8 +9,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" kvalidation "k8s.io/kubernetes/pkg/apis/core/validation" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - - "github.com/openshift/origin/pkg/oc/util/ocscheme" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) // pathSpec represents a path (remote or local) given as a source or destination @@ -96,7 +95,7 @@ func resolveResourceKindPath(f kcmdutil.Factory, path, namespace string) (string } r := f.NewBuilder(). - WithScheme(ocscheme.ReadingInternalScheme). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(namespace). SingleResourceType(). ResourceNames("pods", podName). diff --git a/pkg/oc/cli/rsync/rsync.go b/pkg/oc/cli/rsync/rsync.go index 1024f92ca3a6..ce006a3dd460 100644 --- a/pkg/oc/cli/rsync/rsync.go +++ b/pkg/oc/cli/rsync/rsync.go @@ -10,6 +10,7 @@ import ( "github.com/fsnotify/fsnotify" "github.com/golang/glog" "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" From 779a3be45f4cc6a2b3fc793062e1ab12a6775c62 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Mon, 23 Jul 2018 16:51:40 -0400 Subject: [PATCH 05/12] update debug cmd to use externals --- pkg/image/util/helpers.go | 203 +++++++++++------- pkg/oc/cli/debug/debug.go | 199 +++++++++-------- pkg/oc/cli/rsh/rsh.go | 69 +++++- .../attachablepodforobject.go | 9 +- 4 files changed, 292 insertions(+), 188 deletions(-) diff --git a/pkg/image/util/helpers.go b/pkg/image/util/helpers.go index ebe0e030f3cf..d7e19418651a 100644 --- a/pkg/image/util/helpers.go +++ b/pkg/image/util/helpers.go @@ -11,23 +11,20 @@ import ( "github.com/golang/glog" godigest "github.com/opencontainers/go-digest" + imagev1 "github.com/openshift/api/image/v1" imageapi "github.com/openshift/origin/pkg/image/apis/image" "github.com/openshift/origin/pkg/image/apis/image/docker10" ) -func fillImageLayers(image *imageapi.Image, manifest docker10.DockerImageManifest) error { - if len(image.DockerImageLayers) != 0 { - // DockerImageLayers is already filled by the registry. - return nil - } - +func getImageLayers(manifest docker10.DockerImageManifest) ([]imageapi.ImageLayer, error) { + var imageLayers []imageapi.ImageLayer switch manifest.SchemaVersion { case 1: if len(manifest.History) != len(manifest.FSLayers) { - return fmt.Errorf("the image %s (%s) has mismatched history and fslayer cardinality (%d != %d)", image.Name, image.DockerImageReference, len(manifest.History), len(manifest.FSLayers)) + return nil, fmt.Errorf("mismatched history and fslayer cardinality (%d != %d)", len(manifest.History), len(manifest.FSLayers)) } - image.DockerImageLayers = make([]imageapi.ImageLayer, len(manifest.FSLayers)) + imageLayers = make([]imageapi.ImageLayer, len(manifest.FSLayers)) for i, obj := range manifest.History { layer := manifest.FSLayers[i] @@ -42,29 +39,80 @@ func fillImageLayers(image *imageapi.Image, manifest docker10.DockerImageManifes // in order from the oldest to the youngest. revidx := (len(manifest.History) - 1) - i // n-1, n-2, ..., 1, 0 - image.DockerImageLayers[revidx].Name = layer.DockerBlobSum - image.DockerImageLayers[revidx].LayerSize = size.Size - image.DockerImageLayers[revidx].MediaType = schema1.MediaTypeManifestLayer + imageLayers[revidx].Name = layer.DockerBlobSum + imageLayers[revidx].LayerSize = size.Size + imageLayers[revidx].MediaType = schema1.MediaTypeManifestLayer } case 2: // The layer list is ordered starting from the base image (opposite order of schema1). // So, we do not need to change the order of layers. - image.DockerImageLayers = make([]imageapi.ImageLayer, len(manifest.Layers)) + imageLayers = make([]imageapi.ImageLayer, len(manifest.Layers)) for i, layer := range manifest.Layers { - image.DockerImageLayers[i].Name = layer.Digest - image.DockerImageLayers[i].LayerSize = layer.Size - image.DockerImageLayers[i].MediaType = layer.MediaType + imageLayers[i].Name = layer.Digest + imageLayers[i].LayerSize = layer.Size + imageLayers[i].MediaType = layer.MediaType } default: - return fmt.Errorf("unrecognized Docker image manifest schema %d for %q (%s)", manifest.SchemaVersion, image.Name, image.DockerImageReference) + return nil, fmt.Errorf("unrecognized Docker image manifest schema %d", manifest.SchemaVersion) } - if image.Annotations == nil { - image.Annotations = map[string]string{} + return imageLayers, nil +} + +// reorderImageLayers mutates the given image. It reorders the layers in ascending order. +// Ascending order matches the order of layers in schema 2. Schema 1 has reversed (descending) order of layers. +func reorderImageLayers(imageLayers []imageapi.ImageLayer, layersOrder, imageManifestMediaType string) bool { + if imageLayers == nil || len(imageLayers) == 0 { + return false } - image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending - return nil + if layersOrder == "" { + switch imageManifestMediaType { + case schema1.MediaTypeManifest, schema1.MediaTypeSignedManifest: + layersOrder = imageapi.DockerImageLayersOrderAscending + case schema2.MediaTypeManifest: + layersOrder = imageapi.DockerImageLayersOrderDescending + default: + return false + } + } + + if layersOrder == imageapi.DockerImageLayersOrderDescending { + // reverse order of the layers (lowest = 0, highest = i) + for i, j := 0, len(imageLayers)-1; i < j; i, j = i+1, j-1 { + imageLayers[i], imageLayers[j] = imageLayers[j], imageLayers[i] + } + } + + return true +} + +func convertImageLayers(imageLayers []imagev1.ImageLayer) []imageapi.ImageLayer { + if imageLayers == nil { + return nil + } + + result := make([]imageapi.ImageLayer, len(imageLayers)) + for i := range imageLayers { + result[i].MediaType = imageLayers[i].MediaType + result[i].Name = imageLayers[i].Name + result[i].LayerSize = imageLayers[i].LayerSize + } + return result +} + +func GetImageMetadata(image *imagev1.Image) (imageapi.DockerImage, error) { + if len(image.DockerImageManifest) == 0 { + return imageapi.DockerImage{}, nil + } + + imageLayers := convertImageLayers(image.DockerImageLayers) + reorderImageLayers(imageLayers, image.Annotations[imageapi.DockerImageLayersOrderAnnotation], image.DockerImageManifestMediaType) + + _, imageMetadata, _, _, err := getImageMetadata(image.Name, image.DockerImageReference, + image.DockerImageManifest, image.DockerImageConfig, imageLayers) + return imageMetadata, err + } // ImageWithMetadata mutates the given image. It parses raw DockerImageManifest data stored in the image and @@ -74,110 +122,109 @@ func ImageWithMetadata(image *imageapi.Image) error { return nil } - ReorderImageLayers(image) + if ok := reorderImageLayers(image.DockerImageLayers, + image.Annotations[imageapi.DockerImageLayersOrderAnnotation], image.DockerImageManifestMediaType); ok { + if image.Annotations == nil { + image.Annotations = map[string]string{} + } + image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending + } if len(image.DockerImageLayers) > 0 && image.DockerImageMetadata.Size > 0 && len(image.DockerImageManifestMediaType) > 0 { glog.V(5).Infof("Image metadata already filled for %s", image.Name) return nil } + imageManifestMediaType, imageMetadata, imageLayers, orderAscending, err := getImageMetadata(image.Name, image.DockerImageReference, + image.DockerImageManifest, image.DockerImageConfig, image.DockerImageLayers) + if err != nil { + return err + } + image.DockerImageManifestMediaType = imageManifestMediaType + image.DockerImageMetadata = imageMetadata + image.DockerImageLayers = imageLayers + if orderAscending { + if image.Annotations == nil { + image.Annotations = map[string]string{} + } + image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending + } + return nil +} + +func getImageMetadata(imageName, imageReference, imageManifest, imageConfig string, + imageLayers []imageapi.ImageLayer) (string, imageapi.DockerImage, []imageapi.ImageLayer, bool, error) { manifest := docker10.DockerImageManifest{} - if err := json.Unmarshal([]byte(image.DockerImageManifest), &manifest); err != nil { - return err + if err := json.Unmarshal([]byte(imageManifest), &manifest); err != nil { + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err } - err := fillImageLayers(image, manifest) - if err != nil { - return err + var err error + var orderAscending bool + if len(imageLayers) == 0 { + imageLayers, err = getImageLayers(manifest) + if err != nil { + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("the image %s (%s) failed reading layers: %v", imageName, imageReference, err) + } + orderAscending = true } + var imageManifestMediaType string + var imageMetadata imageapi.DockerImage switch manifest.SchemaVersion { case 1: - image.DockerImageManifestMediaType = schema1.MediaTypeManifest + imageManifestMediaType = schema1.MediaTypeManifest if len(manifest.History) == 0 { // It should never have an empty history, but just in case. - return fmt.Errorf("the image %s (%s) has a schema 1 manifest, but it doesn't have history", image.Name, image.DockerImageReference) + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("the image %s (%s) has a schema 1 manifest, but it doesn't have history", imageName, imageReference) } v1Metadata := docker10.DockerV1CompatibilityImage{} if err := json.Unmarshal([]byte(manifest.History[0].DockerV1Compatibility), &v1Metadata); err != nil { - return err + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err } - if err := imageapi.Convert_compatibility_to_api_DockerImage(&v1Metadata, &image.DockerImageMetadata); err != nil { - return err + if err := imageapi.Convert_compatibility_to_api_DockerImage(&v1Metadata, &imageMetadata); err != nil { + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err } case 2: - image.DockerImageManifestMediaType = schema2.MediaTypeManifest + imageManifestMediaType = schema2.MediaTypeManifest - if len(image.DockerImageConfig) == 0 { - return fmt.Errorf("dockerImageConfig must not be empty for manifest schema 2") + if len(imageConfig) == 0 { + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("dockerImageConfig must not be empty for manifest schema 2") } config := docker10.DockerImageConfig{} - if err := json.Unmarshal([]byte(image.DockerImageConfig), &config); err != nil { - return fmt.Errorf("failed to parse dockerImageConfig: %v", err) + if err := json.Unmarshal([]byte(imageConfig), &config); err != nil { + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("failed to parse dockerImageConfig: %v", err) } - if err := imageapi.Convert_imageconfig_to_api_DockerImage(&config, &image.DockerImageMetadata); err != nil { - return err + if err := imageapi.Convert_imageconfig_to_api_DockerImage(&config, &imageMetadata); err != nil { + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, err } - image.DockerImageMetadata.ID = manifest.Config.Digest + imageMetadata.ID = manifest.Config.Digest default: - return fmt.Errorf("unrecognized Docker image manifest schema %d for %q (%s)", manifest.SchemaVersion, image.Name, image.DockerImageReference) + return "", imageapi.DockerImage{}, []imageapi.ImageLayer{}, false, fmt.Errorf("unrecognized Docker image manifest schema %d for %q (%s)", manifest.SchemaVersion, imageName, imageReference) } layerSet := sets.NewString() if manifest.SchemaVersion == 2 { layerSet.Insert(manifest.Config.Digest) - image.DockerImageMetadata.Size = int64(len(image.DockerImageConfig)) + imageMetadata.Size = int64(len(imageConfig)) } else { - image.DockerImageMetadata.Size = 0 + imageMetadata.Size = 0 } - for _, layer := range image.DockerImageLayers { + for _, layer := range imageLayers { if layerSet.Has(layer.Name) { continue } layerSet.Insert(layer.Name) - image.DockerImageMetadata.Size += layer.LayerSize - } - - return nil -} - -// ReorderImageLayers mutates the given image. It reorders the layers in ascending order. -// Ascending order matches the order of layers in schema 2. Schema 1 has reversed (descending) order of layers. -func ReorderImageLayers(image *imageapi.Image) { - if len(image.DockerImageLayers) == 0 { - return - } - - layersOrder, ok := image.Annotations[imageapi.DockerImageLayersOrderAnnotation] - if !ok { - switch image.DockerImageManifestMediaType { - case schema1.MediaTypeManifest, schema1.MediaTypeSignedManifest: - layersOrder = imageapi.DockerImageLayersOrderAscending - case schema2.MediaTypeManifest: - layersOrder = imageapi.DockerImageLayersOrderDescending - default: - return - } - } - - if layersOrder == imageapi.DockerImageLayersOrderDescending { - // reverse order of the layers (lowest = 0, highest = i) - for i, j := 0, len(image.DockerImageLayers)-1; i < j; i, j = i+1, j-1 { - image.DockerImageLayers[i], image.DockerImageLayers[j] = image.DockerImageLayers[j], image.DockerImageLayers[i] - } - } - - if image.Annotations == nil { - image.Annotations = map[string]string{} + imageMetadata.Size += layer.LayerSize } - image.Annotations[imageapi.DockerImageLayersOrderAnnotation] = imageapi.DockerImageLayersOrderAscending + return imageManifestMediaType, imageMetadata, imageLayers, orderAscending, nil } // ManifestMatchesImage returns true if the provided manifest matches the name of the image. diff --git a/pkg/oc/cli/debug/debug.go b/pkg/oc/cli/debug/debug.go index b4d05707ef00..0066beeb0a67 100644 --- a/pkg/oc/cli/debug/debug.go +++ b/pkg/oc/cli/debug/debug.go @@ -1,7 +1,6 @@ package debug import ( - "errors" "fmt" "os" "reflect" @@ -26,7 +25,7 @@ import ( corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/api/legacyscheme" kapi "k8s.io/kubernetes/pkg/apis/core" - coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + kapiv1 "k8s.io/kubernetes/pkg/apis/core/v1" "k8s.io/kubernetes/pkg/kubectl" kcmd "k8s.io/kubernetes/pkg/kubectl/cmd" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" @@ -41,59 +40,16 @@ import ( appsv1 "github.com/openshift/api/apps/v1" imagev1 "github.com/openshift/api/image/v1" - appsclient "github.com/openshift/client-go/apps/clientset/versioned/typed/apps/v1" + appsv1client "github.com/openshift/client-go/apps/clientset/versioned/typed/apps/v1" + imagev1client "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" appsapi "github.com/openshift/origin/pkg/apps/apis/apps" appsutil "github.com/openshift/origin/pkg/apps/util" imageapi "github.com/openshift/origin/pkg/image/apis/image" - imageclientinternal "github.com/openshift/origin/pkg/image/generated/internalclientset" - imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset/typed/image/internalversion" + imageutil "github.com/openshift/origin/pkg/image/util" generateapp "github.com/openshift/origin/pkg/oc/lib/newapp/app" utilenv "github.com/openshift/origin/pkg/oc/util/env" ) -type DebugOptions struct { - PrintFlags *genericclioptions.PrintFlags - - Attach kcmd.AttachOptions - - CoreClient coreclient.CoreInterface - CoreV1Client corev1client.CoreV1Interface - AppsClient appsclient.DeploymentConfigsGetter - ImageClient imageclient.ImageInterface - - Printer printers.ResourcePrinter - LogsForObject polymorphichelpers.LogsForObjectFunc - - NoStdin bool - ForceTTY bool - DisableTTY bool - Timeout time.Duration - - Command []string - Annotations map[string]string - AsRoot bool - AsNonRoot bool - AsUser int64 - KeepLabels bool // TODO: evaluate selecting the right labels automatically - KeepAnnotations bool - KeepLiveness bool - KeepReadiness bool - KeepInitContainers bool - OneContainer bool - NodeName string - AddEnv []kapi.EnvVar - RemoveEnv []string - Resources []string - Builder func() *resource.Builder - Namespace string - ExplicitNamespace bool - DryRun bool - FullCmdName string - - resource.FilenameOptions - genericclioptions.IOStreams -} - const ( debugPodLabelName = "debug.openshift.io/name" @@ -138,6 +94,48 @@ var ( %[1]s dc/test -o yaml`) ) +type DebugOptions struct { + PrintFlags *genericclioptions.PrintFlags + + Attach kcmd.AttachOptions + + CoreClient corev1client.CoreV1Interface + AppsClient appsv1client.AppsV1Interface + ImageClient imagev1client.ImageV1Interface + + Printer printers.ResourcePrinter + LogsForObject polymorphichelpers.LogsForObjectFunc + + NoStdin bool + ForceTTY bool + DisableTTY bool + Timeout time.Duration + + Command []string + Annotations map[string]string + AsRoot bool + AsNonRoot bool + AsUser int64 + KeepLabels bool // TODO: evaluate selecting the right labels automatically + KeepAnnotations bool + KeepLiveness bool + KeepReadiness bool + KeepInitContainers bool + OneContainer bool + NodeName string + AddEnv []corev1.EnvVar + RemoveEnv []string + Resources []string + Builder func() *resource.Builder + Namespace string + ExplicitNamespace bool + DryRun bool + FullCmdName string + + resource.FilenameOptions + genericclioptions.IOStreams +} + func NewDebugOptions(streams genericclioptions.IOStreams) *DebugOptions { return &DebugOptions{ PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme), @@ -255,11 +253,23 @@ func (o *DebugOptions) Complete(cmd *cobra.Command, f kcmdutil.Factory, args []s o.Builder = f.NewBuilder - o.AddEnv, o.RemoveEnv, err = utilenv.ParseEnv(envArgs, nil) + internalAddEnv, removeEnv, err := utilenv.ParseEnv(envArgs, nil) if err != nil { return err } + // convert EnvVars that we should add, from internal to external + for _, internal := range internalAddEnv { + external := corev1.EnvVar{} + if err := kapiv1.Convert_core_EnvVar_To_v1_EnvVar(&internal, &external, nil); err != nil { + return err + } + + o.AddEnv = append(o.AddEnv, external) + } + + o.RemoveEnv = removeEnv + cmdParent := cmd.Parent() if cmdParent != nil && len(cmdParent.CommandPath()) > 0 && kcmdutil.IsSiblingCommandExists(cmd, "describe") { o.FullCmdName = cmdParent.CommandPath() @@ -289,29 +299,20 @@ func (o *DebugOptions) Complete(cmd *cobra.Command, f kcmdutil.Factory, args []s } o.Attach.Config = config - kc, err := f.ClientSet() + o.CoreClient, err = corev1client.NewForConfig(config) if err != nil { return err } - o.Attach.PodClient = kc.Core() - o.CoreClient = kc.Core() - o.CoreV1Client, err = corev1client.NewForConfig(config) + o.AppsClient, err = appsv1client.NewForConfig(config) if err != nil { return err } - appsClient, err := appsclient.NewForConfig(config) + o.ImageClient, err = imagev1client.NewForConfig(config) if err != nil { return err } - o.AppsClient = appsClient - - imageClient, err := imageclientinternal.NewForConfig(config) - if err != nil { - return err - } - o.ImageClient = imageClient.Image() return nil } @@ -350,11 +351,11 @@ func (o *DebugOptions) RunDebug() error { if err != nil { glog.V(4).Infof("Unable to get exact template, but continuing with fallback: %v", err) } - template := &kapi.PodTemplateSpec{} + template := &corev1.PodTemplateSpec{} if err := legacyscheme.Scheme.Convert(templateV1, template, nil); err != nil { return err } - pod := &kapi.Pod{ + pod := &corev1.Pod{ ObjectMeta: template.ObjectMeta, Spec: template.Spec, } @@ -396,7 +397,7 @@ func (o *DebugOptions) RunDebug() error { } if o.Printer != nil { - return o.Printer.PrintObj(kcmdutil.AsDefaultVersionedOrOriginal(pod, nil), o.Out) + return o.Printer.PrintObj(pod, o.Out) } glog.V(5).Infof("Creating pod: %#v", pod) @@ -415,7 +416,7 @@ func (o *DebugOptions) RunDebug() error { stderr = os.Stderr } fmt.Fprintf(stderr, "\nRemoving debug pod ...\n") - if err := o.Attach.PodClient.Pods(pod.Namespace).Delete(pod.Name, metav1.NewDeleteOptions(0)); err != nil { + if err := o.CoreClient.Pods(pod.Namespace).Delete(pod.Name, metav1.NewDeleteOptions(0)); err != nil { if !kapierrors.IsNotFound(err) { fmt.Fprintf(stderr, "error: unable to delete the debug pod %q: %v\n", pod.Name, err) } @@ -425,7 +426,7 @@ func (o *DebugOptions) RunDebug() error { glog.V(5).Infof("Created attach arguments: %#v", o.Attach) return o.Attach.InterruptParent.Run(func() error { - w, err := o.Attach.PodClient.Pods(pod.Namespace).Watch(metav1.SingleObject(pod.ObjectMeta)) + w, err := o.CoreClient.Pods(pod.Namespace).Watch(metav1.SingleObject(pod.ObjectMeta)) if err != nil { return err } @@ -470,7 +471,7 @@ func (o *DebugOptions) RunDebug() error { // (via the "openshift.io/deployment-config.name" annotation), then tries to // find the ImageStream from which the DeploymentConfig is deploying, then tries // to find a match for the Container's image in the ImageStream's Images. -func (o *DebugOptions) getContainerImageViaDeploymentConfig(pod *kapi.Pod, container *kapi.Container) (*imageapi.Image, error) { +func (o *DebugOptions) getContainerImageViaDeploymentConfig(pod *corev1.Pod, container *corev1.Container) (*imagev1.Image, error) { ref, err := imageapi.ParseDockerImageReference(container.Image) if err != nil { return nil, err @@ -522,15 +523,15 @@ func (o *DebugOptions) getContainerImageViaDeploymentConfig(pod *kapi.Pod, conta // set to false. The request will not succeed if the backing repository // requires Insecure to be set to true, which cannot be hard-coded for security // reasons. -func (o *DebugOptions) getContainerImageViaImageStreamImport(container *kapi.Container) (*imageapi.Image, error) { - isi := &imageapi.ImageStreamImport{ +func (o *DebugOptions) getContainerImageViaImageStreamImport(container *corev1.Container) (*imagev1.Image, error) { + isi := &imagev1.ImageStreamImport{ ObjectMeta: metav1.ObjectMeta{ Name: "oc-debug", }, - Spec: imageapi.ImageStreamImportSpec{ - Images: []imageapi.ImageImportSpec{ + Spec: imagev1.ImageStreamImportSpec{ + Images: []imagev1.ImageImportSpec{ { - From: kapi.ObjectReference{ + From: corev1.ObjectReference{ Kind: "DockerImage", Name: container.Image, }, @@ -551,26 +552,32 @@ func (o *DebugOptions) getContainerImageViaImageStreamImport(container *kapi.Con return nil, nil } -func (o *DebugOptions) getContainerImageCommand(pod *kapi.Pod, container *kapi.Container) ([]string, error) { +func (o *DebugOptions) getContainerImageCommand(pod *corev1.Pod, container *corev1.Container) ([]string, error) { if len(container.Command) > 0 { return container.Command, nil } + image, err := o.getContainerImageViaDeploymentConfig(pod, container) + if err != nil { + image, err = o.getContainerImageViaImageStreamImport(container) + if err != nil { + return nil, err + } + } - image, _ := o.getContainerImageViaDeploymentConfig(pod, container) if image == nil { - image, _ = o.getContainerImageViaImageStreamImport(container) + return nil, fmt.Errorf("error: no usable image found") } - if image == nil || image.DockerImageMetadata.Config == nil { - return nil, errors.New("error: no usable image found") + dockerImage, err := imageutil.GetImageMetadata(image) + if err != nil { + return nil, err } - config := image.DockerImageMetadata.Config - return append(config.Entrypoint, config.Cmd...), nil + return append(dockerImage.Config.Entrypoint, dockerImage.Config.Cmd...), nil } // transformPodForDebug alters the input pod to be debuggable -func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kapi.Pod, []string) { +func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*corev1.Pod, []string) { pod := o.Attach.Pod if !o.KeepInitContainers { @@ -582,7 +589,9 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kap // identify the command to be run originalCommand, _ := o.getContainerImageCommand(pod, container) - if len(originalCommand) > 0 { + //originalCommand := []string{""} + if len(container.Command) > 0 { + originalCommand = container.Command originalCommand = append(originalCommand, container.Args...) } @@ -599,7 +608,7 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kap container.LivenessProbe = nil } - var newEnv []kapi.EnvVar + var newEnv []corev1.EnvVar if len(o.RemoveEnv) > 0 { for i := range container.Env { skip := false @@ -623,7 +632,7 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kap container.Env = newEnv if container.SecurityContext == nil { - container.SecurityContext = &kapi.SecurityContext{} + container.SecurityContext = &corev1.SecurityContext{} } switch { case o.AsNonRoot: @@ -639,7 +648,7 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kap } if o.OneContainer { - pod.Spec.Containers = []kapi.Container{*container} + pod.Spec.Containers = []corev1.Container{*container} } // reset the pod @@ -660,9 +669,9 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kap pod.Spec.NodeName = o.NodeName pod.ResourceVersion = "" - pod.Spec.RestartPolicy = kapi.RestartPolicyNever + pod.Spec.RestartPolicy = corev1.RestartPolicyNever - pod.Status = kapi.PodStatus{} + pod.Status = corev1.PodStatus{} pod.UID = "" pod.CreationTimestamp = metav1.Time{} pod.SelfLink = "" @@ -675,17 +684,17 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*kap // createPod creates the debug pod, and will attempt to delete an existing debug // pod with the same name, but will return an error in any other case. -func (o *DebugOptions) createPod(pod *kapi.Pod) (*kapi.Pod, error) { +func (o *DebugOptions) createPod(pod *corev1.Pod) (*corev1.Pod, error) { namespace, name := pod.Namespace, pod.Name // create the pod - created, err := o.Attach.PodClient.Pods(namespace).Create(pod) + created, err := o.CoreClient.Pods(namespace).Create(pod) if err == nil || !kapierrors.IsAlreadyExists(err) { return created, err } // only continue if the pod has the right annotations - existing, err := o.Attach.PodClient.Pods(namespace).Get(name, metav1.GetOptions{}) + existing, err := o.CoreClient.Pods(namespace).Get(name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -694,13 +703,13 @@ func (o *DebugOptions) createPod(pod *kapi.Pod) (*kapi.Pod, error) { } // delete the existing pod - if err := o.Attach.PodClient.Pods(namespace).Delete(name, metav1.NewDeleteOptions(0)); err != nil && !kapierrors.IsNotFound(err) { + if err := o.CoreClient.Pods(namespace).Delete(name, metav1.NewDeleteOptions(0)); err != nil && !kapierrors.IsNotFound(err) { return nil, fmt.Errorf("unable to delete existing debug pod %q: %v", name, err) } - return o.Attach.PodClient.Pods(namespace).Create(pod) + return o.CoreClient.Pods(namespace).Create(pod) } -func containerForName(pod *kapi.Pod, name string) *kapi.Container { +func containerForName(pod *corev1.Pod, name string) *corev1.Container { for i, c := range pod.Spec.Containers { if c.Name == name { return &pod.Spec.Containers[i] @@ -714,7 +723,7 @@ func containerForName(pod *kapi.Pod, name string) *kapi.Container { return nil } -func containerNames(pod *kapi.Pod) []string { +func containerNames(pod *corev1.Pod) []string { var names []string for _, c := range pod.Spec.Containers { names = append(names, c.Name) @@ -750,14 +759,14 @@ func (o *DebugOptions) approximatePodTemplateForObject(object runtime.Object) (* fallback := t.Spec.Template latestDeploymentName := appsutil.LatestDeploymentNameForConfig(t) - deployment, err := o.CoreV1Client.ReplicationControllers(t.Namespace).Get(latestDeploymentName, metav1.GetOptions{}) + deployment, err := o.CoreClient.ReplicationControllers(t.Namespace).Get(latestDeploymentName, metav1.GetOptions{}) if err != nil { return fallback, err } fallback = deployment.Spec.Template - pods, err := o.CoreV1Client.Pods(deployment.Namespace).List(metav1.ListOptions{LabelSelector: labels.SelectorFromSet(deployment.Spec.Selector).String()}) + pods, err := o.CoreClient.Pods(deployment.Namespace).List(metav1.ListOptions{LabelSelector: labels.SelectorFromSet(deployment.Spec.Selector).String()}) if err != nil { return fallback, err } diff --git a/pkg/oc/cli/rsh/rsh.go b/pkg/oc/cli/rsh/rsh.go index 86aae2135be0..6eb7ec7b6015 100644 --- a/pkg/oc/cli/rsh/rsh.go +++ b/pkg/oc/cli/rsh/rsh.go @@ -6,9 +6,11 @@ import ( "time" "github.com/spf13/cobra" + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/batch" kapi "k8s.io/kubernetes/pkg/apis/core" @@ -210,16 +212,20 @@ func podForResource(f kcmdutil.Factory, resource string, timeout time.Duration) case kapi.Resource("pods"): return name, nil case kapi.Resource("replicationcontrollers"): - kc, err := f.ClientSet() + config, err := f.ToRESTConfig() + if err != nil { + return "", err + } + kc, err := corev1client.NewForConfig(config) if err != nil { return "", err } - rc, err := kc.Core().ReplicationControllers(namespace).Get(name, metav1.GetOptions{}) + rc, err := kc.ReplicationControllers(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err } selector := labels.SelectorFromSet(rc.Spec.Selector) - pod, _, err := polymorphichelpers.GetFirstPod(kc.Core(), namespace, selector.String(), timeout, sortBy) + pod, _, err := polymorphichelpers.GetFirstPod(kc, namespace, selector.String(), timeout, sortBy) if err != nil { return "", err } @@ -247,7 +253,16 @@ func podForResource(f kcmdutil.Factory, resource string, timeout time.Duration) if err != nil { return "", err } - pod, _, err := polymorphichelpers.GetFirstPod(kc.Core(), namespace, selector.String(), timeout, sortBy) + config, err := f.ToRESTConfig() + if err != nil { + return "", err + } + coreclient, err := corev1client.NewForConfig(config) + if err != nil { + return "", err + } + + pod, _, err := polymorphichelpers.GetFirstPod(coreclient, namespace, selector.String(), timeout, sortBy) if err != nil { return "", err } @@ -265,7 +280,15 @@ func podForResource(f kcmdutil.Factory, resource string, timeout time.Duration) if err != nil { return "", err } - pod, _, err := polymorphichelpers.GetFirstPod(kc.Core(), namespace, selector.String(), timeout, sortBy) + config, err := f.ToRESTConfig() + if err != nil { + return "", err + } + coreclient, err := corev1client.NewForConfig(config) + if err != nil { + return "", err + } + pod, _, err := polymorphichelpers.GetFirstPod(coreclient, namespace, selector.String(), timeout, sortBy) if err != nil { return "", err } @@ -283,7 +306,15 @@ func podForResource(f kcmdutil.Factory, resource string, timeout time.Duration) if err != nil { return "", err } - pod, _, err := polymorphichelpers.GetFirstPod(kc.Core(), namespace, selector.String(), timeout, sortBy) + config, err := f.ToRESTConfig() + if err != nil { + return "", err + } + coreclient, err := corev1client.NewForConfig(config) + if err != nil { + return "", err + } + pod, _, err := polymorphichelpers.GetFirstPod(coreclient, namespace, selector.String(), timeout, sortBy) if err != nil { return "", err } @@ -301,7 +332,15 @@ func podForResource(f kcmdutil.Factory, resource string, timeout time.Duration) if err != nil { return "", err } - pod, _, err := polymorphichelpers.GetFirstPod(kc.Core(), namespace, selector.String(), timeout, sortBy) + config, err := f.ToRESTConfig() + if err != nil { + return "", err + } + coreclient, err := corev1client.NewForConfig(config) + if err != nil { + return "", err + } + pod, _, err := polymorphichelpers.GetFirstPod(coreclient, namespace, selector.String(), timeout, sortBy) if err != nil { return "", err } @@ -315,18 +354,28 @@ func podForResource(f kcmdutil.Factory, resource string, timeout time.Duration) if err != nil { return "", err } - return podNameForJob(job, kc, timeout, sortBy) + config, err := f.ToRESTConfig() + if err != nil { + return "", err + } + coreclient, err := corev1client.NewForConfig(config) + if err != nil { + return "", err + } + + return podNameForJob(job, coreclient, timeout, sortBy) default: return "", fmt.Errorf("remote shell for %s is not supported", resourceType) } } -func podNameForJob(job *batch.Job, kc kclientset.Interface, timeout time.Duration, sortBy func(pods []*v1.Pod) sort.Interface) (string, error) { +func podNameForJob(job *batch.Job, kc corev1client.CoreV1Interface, timeout time.Duration, sortBy func(pods []*v1.Pod) sort.Interface) (string, error) { selector, err := metav1.LabelSelectorAsSelector(job.Spec.Selector) if err != nil { return "", err } - pod, _, err := polymorphichelpers.GetFirstPod(kc.Core(), job.Namespace, selector.String(), timeout, sortBy) + + pod, _, err := polymorphichelpers.GetFirstPod(kc, job.Namespace, selector.String(), timeout, sortBy) if err != nil { return "", err } diff --git a/pkg/oc/originpolymorphichelpers/attachablepodforobject.go b/pkg/oc/originpolymorphichelpers/attachablepodforobject.go index 9dba29d3a5ea..525af779bb8d 100644 --- a/pkg/oc/originpolymorphichelpers/attachablepodforobject.go +++ b/pkg/oc/originpolymorphichelpers/attachablepodforobject.go @@ -7,8 +7,7 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - api "k8s.io/kubernetes/pkg/apis/core" - kinternalclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" @@ -17,14 +16,14 @@ import ( ) func NewAttachablePodForObjectFn(delegate polymorphichelpers.AttachableLogsForObjectFunc) polymorphichelpers.AttachableLogsForObjectFunc { - return func(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*api.Pod, error) { + return func(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*v1.Pod, error) { switch t := object.(type) { case *appsapi.DeploymentConfig: config, err := restClientGetter.ToRESTConfig() if err != nil { return nil, err } - coreClient, err := kinternalclient.NewForConfig(config) + coreClient, err := kubernetes.NewForConfig(config) if err != nil { return nil, err } @@ -33,7 +32,7 @@ func NewAttachablePodForObjectFn(delegate polymorphichelpers.AttachableLogsForOb f := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } - pod, _, err := polymorphichelpers.GetFirstPod(coreClient.Core(), t.Namespace, selector.String(), 1*time.Minute, f) + pod, _, err := polymorphichelpers.GetFirstPod(coreClient.CoreV1(), t.Namespace, selector.String(), 1*time.Minute, f) return pod, err default: From 3cce5c18ea7703f38f39c2d3cc7e87e4c34f5d86 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Mon, 23 Jul 2018 17:43:38 -0400 Subject: [PATCH 06/12] update secret cmd to externals --- pkg/oc/cli/secrets/basicauth.go | 112 +++++------ pkg/oc/cli/secrets/basicauth_test.go | 81 ++++---- pkg/oc/cli/secrets/dockercfg.go | 99 +++++----- pkg/oc/cli/secrets/known_secret_types.go | 8 +- pkg/oc/cli/secrets/link_secret_to_obj.go | 18 +- pkg/oc/cli/secrets/new.go | 131 ++++++------- pkg/oc/cli/secrets/new_test.go | 184 +++++++++++++----- pkg/oc/cli/secrets/options.go | 51 ++--- pkg/oc/cli/secrets/sshauth.go | 114 +++++------ pkg/oc/cli/secrets/sshauth_test.go | 47 +++-- .../cli/secrets/unlink_secret_from_object.go | 51 ++++- 11 files changed, 498 insertions(+), 398 deletions(-) diff --git a/pkg/oc/cli/secrets/basicauth.go b/pkg/oc/cli/secrets/basicauth.go index 9f994371ae2e..d944ab209b64 100644 --- a/pkg/oc/cli/secrets/basicauth.go +++ b/pkg/oc/cli/secrets/basicauth.go @@ -3,19 +3,20 @@ package secrets import ( "errors" "fmt" - "io" "io/ioutil" "github.com/spf13/cobra" - api "k8s.io/kubernetes/pkg/apis/core" - kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + corev1 "k8s.io/api/core/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" kterm "k8s.io/kubernetes/pkg/kubectl/util/term" "github.com/openshift/origin/pkg/cmd/util/term" + "github.com/openshift/origin/pkg/oc/util/ocscheme" ) // CreateBasicAuthSecretRecommendedCommandName represents name of subcommand for `oc secrets` command @@ -44,6 +45,10 @@ var ( // CreateBasicAuthSecretOptions holds the credential needed to authenticate against SCM servers. type CreateBasicAuthSecretOptions struct { + PrintFlags *genericclioptions.PrintFlags + + Printer printers.ResourcePrinter + SecretName string Username string Password string @@ -52,18 +57,21 @@ type CreateBasicAuthSecretOptions struct { PromptForPassword bool - Reader io.Reader - Out io.Writer + SecretsInterface corev1client.SecretInterface - SecretsInterface kcoreclient.SecretInterface + genericclioptions.IOStreams +} + +func NewCreateBasicAuthSecretOptions(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + return &CreateBasicAuthSecretOptions{ + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + IOStreams: streams, + } } // NewCmdCreateBasicAuthSecret implements the OpenShift cli secrets new-basicauth subcommand func NewCmdCreateBasicAuthSecret(name, fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams, newSecretFullName, ocEditFullName string) *cobra.Command { - o := &CreateBasicAuthSecretOptions{ - Out: streams.Out, - Reader: streams.In, - } + o := NewCreateBasicAuthSecretOptions(streams) cmd := &cobra.Command{ Use: fmt.Sprintf("%s SECRET --username=USERNAME --password=PASSWORD [--ca-cert=FILENAME] [--gitconfig=FILENAME]", name), @@ -73,25 +81,9 @@ func NewCmdCreateBasicAuthSecret(name, fullName string, f kcmdutil.Factory, stre Deprecated: "use oc create secret", Hidden: true, Run: func(c *cobra.Command, args []string) { - if err := o.Complete(f, args); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if err := o.Validate(); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if len(kcmdutil.GetFlagString(c, "output")) != 0 { - secret, err := o.NewBasicAuthSecret() - kcmdutil.CheckErr(err) - - kcmdutil.CheckErr(kcmdutil.PrintObject(c, secret, streams.Out)) - return - } - - if err := o.CreateBasicAuthSecret(); err != nil { - kcmdutil.CheckErr(err) - } + kcmdutil.CheckErr(o.Complete(f, args)) + kcmdutil.CheckErr(o.Validate()) + kcmdutil.CheckErr(o.Run()) }, } @@ -103,13 +95,12 @@ func NewCmdCreateBasicAuthSecret(name, fullName string, f kcmdutil.Factory, stre cmd.MarkFlagFilename("gitconfig") cmd.Flags().BoolVarP(&o.PromptForPassword, "prompt", "", false, "If true, prompt for password or token") - kcmdutil.AddPrinterFlags(cmd) - + o.PrintFlags.AddFlags(cmd) return cmd } // CreateBasicAuthSecret saves created Secret structure and prints the secret name to the output on success. -func (o *CreateBasicAuthSecretOptions) CreateBasicAuthSecret() error { +func (o *CreateBasicAuthSecretOptions) Run() error { secret, err := o.NewBasicAuthSecret() if err != nil { return err @@ -119,16 +110,15 @@ func (o *CreateBasicAuthSecretOptions) CreateBasicAuthSecret() error { return err } - fmt.Fprintf(o.GetOut(), "secret/%s\n", secret.Name) - return nil + return o.Printer.PrintObj(secret, o.Out) } // NewBasicAuthSecret builds up the Secret structure containing secret name, type and data structure // containing desired credentials. -func (o *CreateBasicAuthSecretOptions) NewBasicAuthSecret() (*api.Secret, error) { - secret := &api.Secret{} +func (o *CreateBasicAuthSecretOptions) NewBasicAuthSecret() (*corev1.Secret, error) { + secret := &corev1.Secret{} secret.Name = o.SecretName - secret.Type = api.SecretTypeBasicAuth + secret.Type = corev1.SecretTypeBasicAuth secret.Data = map[string][]byte{} if len(o.Username) != 0 { @@ -164,32 +154,38 @@ func (o *CreateBasicAuthSecretOptions) Complete(f kcmdutil.Factory, args []strin if len(args) != 1 { return errors.New("must have exactly one argument: secret name") } + o.SecretName = args[0] if o.PromptForPassword { - if len(o.Password) != 0 { - return errors.New("must provide either --prompt or --password flag") - } - if !kterm.IsTerminal(o.Reader) { + if !kterm.IsTerminal(o.In) { return errors.New("provided reader is not a terminal") } - o.Password = term.PromptForPasswordString(o.Reader, o.Out, "Password: ") + o.Password = term.PromptForPasswordString(o.In, o.Out, "Password: ") if len(o.Password) == 0 { return errors.New("password must be provided") } } - if f != nil { - client, err := f.ClientSet() - if err != nil { - return err - } - namespace, _, err := f.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - o.SecretsInterface = client.Core().Secrets(namespace) + config, err := f.ToRESTConfig() + if err != nil { + return err + } + + clientset, err := corev1client.NewForConfig(config) + if err != nil { + return err + } + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return err + } + o.SecretsInterface = clientset.Secrets(namespace) + + o.Printer, err = o.PrintFlags.ToPrinter() + if err != nil { + return err } return nil @@ -205,15 +201,9 @@ func (o CreateBasicAuthSecretOptions) Validate() error { return errors.New("must provide basic authentication credentials") } - return nil -} - -// GetOut check if the CreateBasicAuthSecretOptions Out Writer is set. Returns it if the Writer -// is present, if not returns Writer on which all Write calls succeed without doing anything. -func (o CreateBasicAuthSecretOptions) GetOut() io.Writer { - if o.Out == nil { - return ioutil.Discard + if o.PromptForPassword && len(o.Password) > 0 { + return errors.New("must provide either --prompt or --password flag") } - return o.Out + return nil } diff --git a/pkg/oc/cli/secrets/basicauth_test.go b/pkg/oc/cli/secrets/basicauth_test.go index 427a4f875a1c..a35d1ac94c95 100644 --- a/pkg/oc/cli/secrets/basicauth_test.go +++ b/pkg/oc/cli/secrets/basicauth_test.go @@ -3,79 +3,87 @@ package secrets import ( "testing" - api "k8s.io/kubernetes/pkg/apis/core" + corev1 "k8s.io/api/core/v1" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) func TestValidateBasicAuth(t *testing.T) { tests := []struct { testName string - args []string - params CreateBasicAuthSecretOptions + options func(genericclioptions.IOStreams) *CreateBasicAuthSecretOptions expErr bool }{ { testName: "validArgs", - args: []string{"testSecret"}, - params: CreateBasicAuthSecretOptions{ - Username: "testUser", - Password: "testPassword", + options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + o := NewCreateBasicAuthSecretOptions(streams) + o.Username = "testUser" + o.Password = "testPassword" + o.SecretName = "testSecret" + return o }, expErr: false, }, { testName: "validArgsWithCertificate", - args: []string{"testSecret"}, - params: CreateBasicAuthSecretOptions{ - Username: "testUser", - Password: "testPassword", - CertificatePath: "./bsFixtures/valid/ca.crt", + options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + o := NewCreateBasicAuthSecretOptions(streams) + o.Username = "testUser" + o.Password = "testPassword" + o.SecretName = "testSecret" + o.CertificatePath = "./bsFixtures/valid/ca.crt" + return o }, expErr: false, }, { testName: "validArgsWithGitconfig", - args: []string{"testSecret"}, - params: CreateBasicAuthSecretOptions{ - Username: "testUser", - Password: "testPassword", - GitConfigPath: "./bsFixtures/leadingdot/.gitconfig", + options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + o := NewCreateBasicAuthSecretOptions(streams) + o.Username = "testUser" + o.Password = "testPassword" + o.SecretName = "testSecret" + o.GitConfigPath = "./bsFixtures/leadingdot/.gitconfig" + return o }, expErr: false, }, { testName: "noName", - args: []string{}, - params: CreateBasicAuthSecretOptions{ - Username: "testUser", - Password: "testPassword", + options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + o := NewCreateBasicAuthSecretOptions(streams) + o.Username = "testUser" + o.Password = "testPassword" + return o }, expErr: true, //"Must have exactly one argument: secret name" }, { testName: "noParams", - args: []string{"testSecret"}, - params: CreateBasicAuthSecretOptions{}, - expErr: true, //"Must provide basic authentication credentials" + options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + o := NewCreateBasicAuthSecretOptions(streams) + o.SecretName = "testSecret" + return o + }, + expErr: true, //"Must provide basic authentication credentials" }, { testName: "passwordAndPrompt", - args: []string{"testSecret"}, - params: CreateBasicAuthSecretOptions{ - Username: "testUser", - Password: "testPassword", - PromptForPassword: true, + options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { + o := NewCreateBasicAuthSecretOptions(streams) + o.Username = "testUser" + o.Password = "testPassword" + o.SecretName = "testSecret" + o.PromptForPassword = true + return o }, expErr: true, //"Must provide either --prompt or --password flag" }, } for _, test := range tests { - options := test.params - err := options.Complete(nil, test.args) - if err == nil { - err = options.Validate() - } - + options := test.options(genericclioptions.NewTestIOStreamsDiscard()) + err := options.Validate() if test.expErr { if err == nil { t.Errorf("%s: unexpected error: %v", test.testName, err) @@ -85,13 +93,14 @@ func TestValidateBasicAuth(t *testing.T) { if err != nil { t.Errorf("%s: unexpected error: %v", test.testName, err) + continue } secret, err := options.NewBasicAuthSecret() if err != nil { t.Errorf("%s: unexpected error: %v", test.testName, err) } - if secret.Type != api.SecretTypeBasicAuth { + if secret.Type != corev1.SecretTypeBasicAuth { t.Errorf("%s: unexpected secret.Type: %v", test.testName, secret.Type) } } diff --git a/pkg/oc/cli/secrets/dockercfg.go b/pkg/oc/cli/secrets/dockercfg.go index dc5fb1912b31..3392434cb2ba 100644 --- a/pkg/oc/cli/secrets/dockercfg.go +++ b/pkg/oc/cli/secrets/dockercfg.go @@ -4,18 +4,18 @@ import ( "encoding/json" "errors" "fmt" - "io" - "io/ioutil" "strings" - api "k8s.io/kubernetes/pkg/apis/core" - kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + "github.com/spf13/cobra" + + "github.com/openshift/origin/pkg/oc/util/ocscheme" + corev1 "k8s.io/api/core/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/credentialprovider" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" - - "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" ) const CreateDockerConfigSecretRecommendedName = "new-dockercfg" @@ -50,20 +50,31 @@ var ( ) type CreateDockerConfigOptions struct { + PrintFlags *genericclioptions.PrintFlags + + Printer printers.ResourcePrinter + SecretName string RegistryLocation string Username string Password string EmailAddress string - SecretsInterface kcoreclient.SecretInterface + SecretsInterface corev1client.SecretInterface - Out io.Writer + genericclioptions.IOStreams +} + +func NewCreateDockerConfigOptions(streams genericclioptions.IOStreams) *CreateDockerConfigOptions { + return &CreateDockerConfigOptions{ + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + IOStreams: streams, + } } // NewCmdCreateDockerConfigSecret creates a command object for making a dockercfg secret func NewCmdCreateDockerConfigSecret(name, fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams, newSecretFullName, ocEditFullName string) *cobra.Command { - o := &CreateDockerConfigOptions{Out: streams.Out} + o := NewCreateDockerConfigOptions(streams) cmd := &cobra.Command{ Use: fmt.Sprintf("%s SECRET --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL", name), @@ -73,25 +84,9 @@ func NewCmdCreateDockerConfigSecret(name, fullName string, f kcmdutil.Factory, s Deprecated: "use oc create secret", Hidden: true, Run: func(c *cobra.Command, args []string) { - if err := o.Complete(f, args); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if err := o.Validate(); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if len(kcmdutil.GetFlagString(c, "output")) != 0 { - secret, err := o.NewDockerSecret() - kcmdutil.CheckErr(err) - - kcmdutil.CheckErr(kcmdutil.PrintObject(c, secret, streams.Out)) - return - } - - if err := o.CreateDockerSecret(); err != nil { - kcmdutil.CheckErr(err) - } + kcmdutil.CheckErr(o.Complete(f, args)) + kcmdutil.CheckErr(o.Validate()) + kcmdutil.CheckErr(o.Run()) }, } @@ -100,27 +95,31 @@ func NewCmdCreateDockerConfigSecret(name, fullName string, f kcmdutil.Factory, s cmd.Flags().StringVar(&o.Password, "docker-password", "", "Password for Docker registry authentication") cmd.Flags().StringVar(&o.EmailAddress, "docker-email", "", "Email for Docker registry") cmd.Flags().StringVar(&o.RegistryLocation, "docker-server", "https://index.docker.io/v1/", "Server location for Docker registry") - kcmdutil.AddPrinterFlags(cmd) + o.PrintFlags.AddFlags(cmd) return cmd } -func (o CreateDockerConfigOptions) CreateDockerSecret() error { +func (o CreateDockerConfigOptions) Run() error { secret, err := o.NewDockerSecret() if err != nil { return err } - if _, err := o.SecretsInterface.Create(secret); err != nil { - return err - } + // TODO: sweep codebase removing implied --dry-run behavior when -o is specified + if o.PrintFlags.OutputFormat != nil && len(*o.PrintFlags.OutputFormat) == 0 { + persistedSecret, err := o.SecretsInterface.Create(secret) + if err != nil { + return err + } - fmt.Fprintf(o.GetOut(), "secret/%s\n", secret.Name) + return o.Printer.PrintObj(persistedSecret, o.Out) + } - return nil + return o.Printer.PrintObj(secret, o.Out) } -func (o CreateDockerConfigOptions) NewDockerSecret() (*api.Secret, error) { +func (o CreateDockerConfigOptions) NewDockerSecret() (*corev1.Secret, error) { dockercfgAuth := credentialprovider.DockerConfigEntry{ Username: o.Username, Password: o.Password, @@ -136,11 +135,11 @@ func (o CreateDockerConfigOptions) NewDockerSecret() (*api.Secret, error) { return nil, err } - secret := &api.Secret{} + secret := &corev1.Secret{} secret.Name = o.SecretName - secret.Type = api.SecretTypeDockerConfigJson + secret.Type = corev1.SecretTypeDockerConfigJson secret.Data = map[string][]byte{} - secret.Data[api.DockerConfigJsonKey] = dockercfgContent + secret.Data[corev1.DockerConfigJsonKey] = dockercfgContent return secret, nil } @@ -151,7 +150,12 @@ func (o *CreateDockerConfigOptions) Complete(f kcmdutil.Factory, args []string) } o.SecretName = args[0] - client, err := f.ClientSet() + config, err := f.ToRESTConfig() + if err != nil { + return err + } + + client, err := corev1client.NewForConfig(config) if err != nil { return err } @@ -160,7 +164,12 @@ func (o *CreateDockerConfigOptions) Complete(f kcmdutil.Factory, args []string) return err } - o.SecretsInterface = client.Core().Secrets(namespace) + o.SecretsInterface = client.Secrets(namespace) + + o.Printer, err = o.PrintFlags.ToPrinter() + if err != nil { + return err + } return nil } @@ -191,11 +200,3 @@ func (o CreateDockerConfigOptions) Validate() error { return nil } - -func (o CreateDockerConfigOptions) GetOut() io.Writer { - if o.Out == nil { - return ioutil.Discard - } - - return o.Out -} diff --git a/pkg/oc/cli/secrets/known_secret_types.go b/pkg/oc/cli/secrets/known_secret_types.go index d2eea3bbb5b4..f9746237fc76 100644 --- a/pkg/oc/cli/secrets/known_secret_types.go +++ b/pkg/oc/cli/secrets/known_secret_types.go @@ -3,12 +3,12 @@ package secrets import ( "reflect" + coreapiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" - kapi "k8s.io/kubernetes/pkg/apis/core" ) type KnownSecretType struct { - Type kapi.SecretType + Type coreapiv1.SecretType RequiredContents sets.String } @@ -22,7 +22,7 @@ func (ks KnownSecretType) Matches(secretContent map[string][]byte) bool { var ( KnownSecretTypes = []KnownSecretType{ - {kapi.SecretTypeDockercfg, sets.NewString(kapi.DockerConfigKey)}, - {kapi.SecretTypeDockerConfigJson, sets.NewString(kapi.DockerConfigJsonKey)}, + {coreapiv1.SecretTypeDockercfg, sets.NewString(coreapiv1.DockerConfigKey)}, + {coreapiv1.SecretTypeDockerConfigJson, sets.NewString(coreapiv1.DockerConfigJsonKey)}, } ) diff --git a/pkg/oc/cli/secrets/link_secret_to_obj.go b/pkg/oc/cli/secrets/link_secret_to_obj.go index 2cf4f1ac1537..e40caca75c0d 100644 --- a/pkg/oc/cli/secrets/link_secret_to_obj.go +++ b/pkg/oc/cli/secrets/link_secret_to_obj.go @@ -6,7 +6,7 @@ import ( "os" "strings" - kapi "k8s.io/kubernetes/pkg/apis/core" + coreapiv1 "k8s.io/api/core/v1" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" @@ -43,9 +43,15 @@ type LinkSecretOptions struct { typeFlags []string } +func NewLinkSecretOptions(streams genericclioptions.IOStreams) *LinkSecretOptions { + return &LinkSecretOptions{ + SecretOptions: SecretOptions{}, + } +} + // NewCmdLinkSecret creates a command object for linking a secret reference to a service account func NewCmdLinkSecret(name, fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := &LinkSecretOptions{SecretOptions{Out: streams.Out}, false, false, nil} + o := NewLinkSecretOptions(streams) cmd := &cobra.Command{ Use: fmt.Sprintf("%s serviceaccounts-name secret-name [another-secret-name]...", name), @@ -131,7 +137,7 @@ func (o LinkSecretOptions) LinkSecrets() error { // TODO: when Secrets in kapi.ServiceAccount get changed to MountSecrets and represented by LocalObjectReferences, this can be // refactored to reuse the addition code better // linkSecretsToServiceAccount links secrets to the service account, either as pull secrets, mount secrets, or both. -func (o LinkSecretOptions) linkSecretsToServiceAccount(serviceaccount *kapi.ServiceAccount) error { +func (o LinkSecretOptions) linkSecretsToServiceAccount(serviceaccount *coreapiv1.ServiceAccount) error { updated := false newSecrets, hasNotFound, err := o.GetSecrets(false) if err != nil { @@ -143,7 +149,7 @@ func (o LinkSecretOptions) linkSecretsToServiceAccount(serviceaccount *kapi.Serv currentSecrets := o.GetMountSecretNames(serviceaccount) secretsToLink := newSecretNames.Difference(currentSecrets) for _, secretName := range secretsToLink.List() { - serviceaccount.Secrets = append(serviceaccount.Secrets, kapi.ObjectReference{Name: secretName}) + serviceaccount.Secrets = append(serviceaccount.Secrets, coreapiv1.ObjectReference{Name: secretName}) updated = true } } @@ -151,12 +157,12 @@ func (o LinkSecretOptions) linkSecretsToServiceAccount(serviceaccount *kapi.Serv currentSecrets := o.GetPullSecretNames(serviceaccount) secretsToLink := newSecretNames.Difference(currentSecrets) for _, secretName := range secretsToLink.List() { - serviceaccount.ImagePullSecrets = append(serviceaccount.ImagePullSecrets, kapi.LocalObjectReference{Name: secretName}) + serviceaccount.ImagePullSecrets = append(serviceaccount.ImagePullSecrets, coreapiv1.LocalObjectReference{Name: secretName}) updated = true } } if updated { - _, err = o.KubeCoreClient.ServiceAccounts(o.Namespace).Update(serviceaccount) + _, err = o.KubeClient.ServiceAccounts(o.Namespace).Update(serviceaccount) return err } diff --git a/pkg/oc/cli/secrets/new.go b/pkg/oc/cli/secrets/new.go index 584720c37705..781204d2f496 100644 --- a/pkg/oc/cli/secrets/new.go +++ b/pkg/oc/cli/secrets/new.go @@ -3,21 +3,23 @@ package secrets import ( "errors" "fmt" - "io" "io/ioutil" "os" "path" "strings" + "github.com/spf13/cobra" + + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kvalidation "k8s.io/apimachinery/pkg/util/validation" - kapi "k8s.io/kubernetes/pkg/apis/core" - kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" - "github.com/spf13/cobra" + "github.com/openshift/origin/pkg/oc/util/ocscheme" ) const NewSecretRecommendedCommandName = "new" @@ -48,6 +50,10 @@ var ( ) type CreateSecretOptions struct { + PrintFlags *genericclioptions.PrintFlags + + Printer printers.ResourcePrinter + // Name of the resulting secret Name string @@ -58,22 +64,25 @@ type CreateSecretOptions struct { // Directory sources are listed and any direct file children included (but subfolders are not traversed) Sources []string - SecretsInterface kcoreclient.SecretInterface - - // Writer to write warnings to - Stderr io.Writer - - Out io.Writer + SecretsInterface corev1client.SecretInterface // Controls whether to output warnings Quiet bool AllowUnknownTypes bool + + genericclioptions.IOStreams +} + +func NewCreateSecretOptions(streams genericclioptions.IOStreams) *CreateSecretOptions { + return &CreateSecretOptions{ + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + IOStreams: streams, + } } func NewCmdCreateSecret(name, fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - options := NewCreateSecretOptions() - options.Out = streams.Out + o := NewCreateSecretOptions(streams) cmd := &cobra.Command{ Use: fmt.Sprintf("%s NAME [KEY=]SOURCE ...", name), @@ -83,42 +92,20 @@ func NewCmdCreateSecret(name, fullName string, f kcmdutil.Factory, streams gener Deprecated: "use oc create secret", Hidden: true, Run: func(c *cobra.Command, args []string) { - if err := options.Complete(args, f); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if err := options.Validate(); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if len(kcmdutil.GetFlagString(c, "output")) != 0 { - secret, err := options.BundleSecret() - kcmdutil.CheckErr(err) - - kcmdutil.CheckErr(kcmdutil.PrintObject(c, secret, streams.Out)) - return - } - - _, err := options.CreateSecret() - kcmdutil.CheckErr(err) + kcmdutil.CheckErr(o.Complete(args, f)) + kcmdutil.CheckErr(o.Validate()) + kcmdutil.CheckErr(o.Run()) }, } - cmd.Flags().BoolVarP(&options.Quiet, "quiet", "q", options.Quiet, "If true, suppress warnings") - cmd.Flags().BoolVar(&options.AllowUnknownTypes, "confirm", options.AllowUnknownTypes, "If true, allow unknown secret types.") - cmd.Flags().StringVar(&options.SecretTypeName, "type", "", "The type of secret") - kcmdutil.AddPrinterFlags(cmd) + cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "If true, suppress warnings") + cmd.Flags().BoolVar(&o.AllowUnknownTypes, "confirm", o.AllowUnknownTypes, "If true, allow unknown secret types.") + cmd.Flags().StringVar(&o.SecretTypeName, "type", "", "The type of secret") + o.PrintFlags.AddFlags(cmd) return cmd } -func NewCreateSecretOptions() *CreateSecretOptions { - return &CreateSecretOptions{ - Stderr: os.Stderr, - Sources: []string{}, - } -} - func (o *CreateSecretOptions) Complete(args []string, f kcmdutil.Factory) error { // Fill name from args[0] if len(args) > 0 { @@ -130,20 +117,23 @@ func (o *CreateSecretOptions) Complete(args []string, f kcmdutil.Factory) error o.Sources = append(o.Sources, args[1:]...) } - if f != nil { - clientConfig, err := f.ToRESTConfig() - if err != nil { - return err - } - kubeClient, err := kcoreclient.NewForConfig(clientConfig) - if err != nil { - return err - } - namespace, _, err := f.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - o.SecretsInterface = kubeClient.Secrets(namespace) + clientConfig, err := f.ToRESTConfig() + if err != nil { + return err + } + kubeClient, err := corev1client.NewForConfig(clientConfig) + if err != nil { + return err + } + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return err + } + o.SecretsInterface = kubeClient.Secrets(namespace) + + o.Printer, err = o.PrintFlags.ToPrinter() + if err != nil { + return err } return nil @@ -159,7 +149,7 @@ func (o *CreateSecretOptions) Validate() error { if !o.AllowUnknownTypes { switch o.SecretTypeName { - case string(kapi.SecretTypeOpaque), "": + case string(corev1.SecretTypeOpaque), "": // this is ok default: found := false @@ -178,21 +168,26 @@ func (o *CreateSecretOptions) Validate() error { return nil } -func (o *CreateSecretOptions) CreateSecret() (*kapi.Secret, error) { +func (o *CreateSecretOptions) Run() error { secret, err := o.BundleSecret() if err != nil { - return nil, err + return err } - persistedSecret, err := o.SecretsInterface.Create(secret) - if err == nil { - fmt.Fprintf(o.Out, "secret/%s\n", persistedSecret.Name) + // TODO: sweep codebase removing implied --dry-run behavior when -o is specified + if o.PrintFlags.OutputFormat != nil && len(*o.PrintFlags.OutputFormat) == 0 { + persistedSecret, err := o.SecretsInterface.Create(secret) + if err != nil { + return err + } + + return o.Printer.PrintObj(persistedSecret, o.Out) } - return persistedSecret, err + return o.Printer.PrintObj(secret, o.Out) } -func (o *CreateSecretOptions) BundleSecret() (*kapi.Secret, error) { +func (o *CreateSecretOptions) BundleSecret() (*corev1.Secret, error) { secretData := make(map[string][]byte) for _, source := range o.Sources { @@ -223,8 +218,8 @@ func (o *CreateSecretOptions) BundleSecret() (*kapi.Secret, error) { for _, item := range fileList { itemPath := path.Join(filePath, item.Name()) if !item.Mode().IsRegular() { - if o.Stderr != nil && o.Quiet != true { - fmt.Fprintf(o.Stderr, "Skipping resource %s\n", itemPath) + if o.ErrOut != nil && o.Quiet != true { + fmt.Fprintf(o.ErrOut, "Skipping resource %s\n", itemPath) } } else { keyName = item.Name() @@ -247,9 +242,9 @@ func (o *CreateSecretOptions) BundleSecret() (*kapi.Secret, error) { } // if the secret type isn't specified, attempt to auto-detect likely hit - secretType := kapi.SecretType(o.SecretTypeName) + secretType := corev1.SecretType(o.SecretTypeName) if len(o.SecretTypeName) == 0 { - secretType = kapi.SecretTypeOpaque + secretType = corev1.SecretTypeOpaque for _, knownSecretType := range KnownSecretTypes { if knownSecretType.Matches(secretData) { @@ -258,7 +253,7 @@ func (o *CreateSecretOptions) BundleSecret() (*kapi.Secret, error) { } } - secret := &kapi.Secret{ + secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: o.Name}, Type: secretType, Data: secretData, diff --git a/pkg/oc/cli/secrets/new_test.go b/pkg/oc/cli/secrets/new_test.go index 2c0e05eccd99..3bc9de65fdca 100644 --- a/pkg/oc/cli/secrets/new_test.go +++ b/pkg/oc/cli/secrets/new_test.go @@ -1,39 +1,51 @@ package secrets import ( - "bytes" - "io/ioutil" "os" "testing" + corev1 "k8s.io/api/core/v1" kapi "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) func TestValidate(t *testing.T) { tests := []struct { testName string - args []string + options func(genericclioptions.IOStreams) *CreateSecretOptions expErr bool }{ { testName: "validArgs", - args: []string{"testSecret", "./bsFixtures/www.google.com"}, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/www.google.com"} + return o + }, }, { testName: "noName", - args: []string{"./bsFixtures/www.google.com"}, - expErr: true, //"Secret name is required" + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Sources = []string{"./bsFixtures/www.google.com"} + return o + }, + expErr: true, //"Secret name is required" }, { testName: "noFilesPassed", - args: []string{"testSecret"}, - expErr: true, //"At least one source file or directory must be specified" + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + return o + }, + expErr: true, //"At least one source file or directory must be specified" }, } for _, test := range tests { - options := NewCreateSecretOptions() - options.Complete(test.args, nil) + options := test.options(genericclioptions.NewTestIOStreamsDiscard()) err := options.Validate() if err != nil && !test.expErr { t.Errorf("%s: unexpected error: %v", test.testName, err) @@ -47,91 +59,163 @@ func TestCreateSecret(t *testing.T) { tests := []struct { testName string - args []string + options func(genericclioptions.IOStreams) *CreateSecretOptions expErr bool - quiet bool errStreamContent string }{ { testName: "validSources", - args: []string{"testSecret", "./bsFixtures/www.google.com", "./bsFixtures/dirNoSubdir"}, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/www.google.com", "./bsFixtures/dirNoSubdir"} + return o + }, }, { testName: "allowsMixedCaseAndDash", - args: []string{"testSecret", "./bsFixtures/invalid/invalid-DNS"}, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/invalid/invalid-DNS"} + return o + }, }, { testName: "failsWithUnderscore", - args: []string{"testSecret", "./bsFixtures/invalid/not\\valid"}, - expErr: true, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/invalid/not\\valid"} + return o + }, + expErr: true, }, { testName: "leadingDotsAllowed", - args: []string{"testSecret", "./bsFixtures/leadingdot/.dockercfg"}, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/leadingdot/.dockercfg"} + return o + }, }, { testName: "filesSameName", - args: []string{"testSecret", "./bsFixtures/www.google.com", "./bsFixtures/multiple/www.google.com"}, - expErr: true, // "Multiple files with the same name (www.google.com) cannot be included a secret" + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/www.google.com", "./bsFixtures/multiple/www.google.com"} + return o + }, + expErr: true, // "Multiple files with the same name (www.google.com) cannot be included a secret" }, { testName: "testQuietTrue", - args: []string{"testSecret", "./bsFixtures/dir"}, - quiet: true, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/dir"} + o.Quiet = true + return o + }, }, { - testName: "testQuietFalse", - args: []string{"testSecret", "./bsFixtures/dir"}, + testName: "testQuietFalse", + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/dir"} + return o + }, errStreamContent: "Skipping resource bsFixtures/dir/symbolic\n", }, { testName: "testNamedKeys", - args: []string{"testSecret", ".googlename=./bsFixtures/www.google.com"}, - expErr: false, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{".googlename=./bsFixtures/www.google.com"} + return o + }, + expErr: false, }, { testName: "testNamedDir", - args: []string{"testSecret", ".somename=./bsFixtures/dirNoSubdir"}, - expErr: true, // "Cannot give a key name for a directory path." + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{".somename=./bsFixtures/dirNoSubdir"} + return o + }, + expErr: true, // "Cannot give a key name for a directory path." }, { testName: "testUnnamedDir", - args: []string{"testSecret", "./bsFixtures/dirContainsMany"}, - expErr: false, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"./bsFixtures/dirContainsMany"} + return o + }, + expErr: false, }, { testName: "testMalformedName", - args: []string{"testSecret", ".google=name=./bsFixtures/www.google.com"}, - expErr: true, // "Key names or file paths cannot contain '='." + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{".google=name=./bsFixtures/www.google.com"} + return o + }, + expErr: true, // "Key names or file paths cannot contain '='." }, { testName: "testMissingName", - args: []string{"testSecret", "=./bsFixtures/www.google.com"}, - expErr: true, // "Key name for file path ./bsFixtures/www.google.com missing." + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{"=./bsFixtures/www.google.com"} + return o + }, + expErr: true, // "Key name for file path ./bsFixtures/www.google.com missing." }, { testName: "testMissingPath", - args: []string{"testSecret", ".somename="}, - expErr: true, // "File path for key name some-name missing." + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{".somename="} + return o + }, + expErr: true, // "File path for key name some-name missing." }, { testName: "testNamesAvoidCollision", - args: []string{"testSecret", ".googlename=./bsFixtures/www.google.com", ".othergooglename=./bsFixtures/multiple/www.google.com"}, - expErr: false, + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{".googlename=./bsFixtures/www.google.com", ".othergooglename=./bsFixtures/multiple/www.google.com"} + return o + }, + expErr: false, }, { testName: "testNameCollision", - args: []string{"testSecret", ".googlename=./bsFixtures/www.google.com", ".googlename=./bsFixtures/multiple/www.google.com"}, - expErr: true, // "Cannot add key google-name from path ./bsFixtures/multiple/www.google.com, another key by that name already exists." + options: func(streams genericclioptions.IOStreams) *CreateSecretOptions { + o := NewCreateSecretOptions(streams) + o.Name = "testSecret" + o.Sources = []string{".googlename=./bsFixtures/www.google.com", ".googlename=./bsFixtures/multiple/www.google.com"} + return o + }, + expErr: true, // "Cannot add key google-name from path ./bsFixtures/multiple/www.google.com, another key by that name already exists." }, } for _, test := range tests { - errStream := &bytes.Buffer{} - options := NewCreateSecretOptions() - options.Stderr = errStream - options.Complete(test.args, nil) - options.Quiet = test.quiet + streams, _, _, errStream := genericclioptions.NewTestIOStreams() + + options := test.options(streams) err := options.Validate() if err != nil { @@ -155,29 +239,29 @@ func TestSecretTypeSpecified(t *testing.T) { Name: "any", SecretTypeName: string(kapi.SecretTypeDockercfg), Sources: []string{"./bsFixtures/www.google.com"}, - Stderr: ioutil.Discard, + IOStreams: genericclioptions.NewTestIOStreamsDiscard(), } secret, err := options.BundleSecret() if err != nil { t.Errorf("unexpected error: %v", err) } - if secret.Type != kapi.SecretTypeDockercfg { + if secret.Type != corev1.SecretTypeDockercfg { t.Errorf("expected %v, got %v", kapi.SecretTypeDockercfg, secret.Type) } } func TestSecretTypeDiscovered(t *testing.T) { options := CreateSecretOptions{ - Name: "any", - Sources: []string{"./bsFixtures/leadingdot/.dockercfg"}, - Stderr: ioutil.Discard, + Name: "any", + Sources: []string{"./bsFixtures/leadingdot/.dockercfg"}, + IOStreams: genericclioptions.NewTestIOStreamsDiscard(), } secret, err := options.BundleSecret() if err != nil { t.Errorf("unexpected error: %v", err) } - if secret.Type != kapi.SecretTypeDockercfg { + if secret.Type != corev1.SecretTypeDockercfg { t.Errorf("expected %v, got %v", kapi.SecretTypeDockercfg, secret.Type) } } diff --git a/pkg/oc/cli/secrets/options.go b/pkg/oc/cli/secrets/options.go index ba77190b529d..3651fc1015d9 100644 --- a/pkg/oc/cli/secrets/options.go +++ b/pkg/oc/cli/secrets/options.go @@ -3,18 +3,17 @@ package secrets import ( "errors" "fmt" - "io" - "io/ioutil" "os" "strings" + corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" - kapi "k8s.io/kubernetes/pkg/apis/core" - kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" + "k8s.io/kubernetes/pkg/kubectl/scheme" "github.com/openshift/origin/pkg/oc/util/ocscheme" ) @@ -28,10 +27,8 @@ type SecretOptions struct { Namespace string - BuilderFunc func() *resource.Builder - KubeCoreClient kcoreclient.CoreInterface - - Out io.Writer + BuilderFunc func() *resource.Builder + KubeClient corev1client.CoreV1Interface } // Complete Parses the command line arguments and populates SecretOptions @@ -49,11 +46,10 @@ func (o *SecretOptions) Complete(f kcmdutil.Factory, args []string) error { if err != nil { return err } - kubeClient, err := kcoreclient.NewForConfig(clientConfig) + o.KubeClient, err = corev1client.NewForConfig(clientConfig) if err != nil { return err } - o.KubeCoreClient = kubeClient o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err @@ -70,8 +66,8 @@ func (o SecretOptions) Validate() error { if len(o.SecretNames) == 0 { return errors.New("secret name must be present") } - if o.KubeCoreClient == nil { - return errors.New("KubeCoreClient must be present") + if o.KubeClient == nil { + return errors.New("KubeClient must be present") } // if any secret names are of the form /, @@ -88,9 +84,9 @@ func (o SecretOptions) Validate() error { } // GetServiceAccount Retrieve the service account object specified by the command -func (o SecretOptions) GetServiceAccount() (*kapi.ServiceAccount, error) { +func (o SecretOptions) GetServiceAccount() (*corev1.ServiceAccount, error) { r := o.BuilderFunc(). - WithScheme(ocscheme.ReadingInternalScheme). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(o.Namespace). ResourceNames("serviceaccounts", o.TargetName). SingleResourceType(). @@ -104,7 +100,7 @@ func (o SecretOptions) GetServiceAccount() (*kapi.ServiceAccount, error) { } switch t := obj.(type) { - case *kapi.ServiceAccount: + case *corev1.ServiceAccount: return t, nil default: return nil, fmt.Errorf("unhandled object: %#v", t) @@ -112,7 +108,7 @@ func (o SecretOptions) GetServiceAccount() (*kapi.ServiceAccount, error) { } // GetSecretNames Get a list of the names of the secrets in a set of them -func (o SecretOptions) GetSecretNames(secrets []*kapi.Secret) sets.String { +func (o SecretOptions) GetSecretNames(secrets []*corev1.Secret) sets.String { names := sets.String{} for _, secret := range secrets { names.Insert(parseSecretName(secret.Name)) @@ -133,7 +129,7 @@ func parseSecretName(name string) string { // GetMountSecretNames Get a list of the names of the mount secrets associated // with a service account -func (o SecretOptions) GetMountSecretNames(serviceaccount *kapi.ServiceAccount) sets.String { +func (o SecretOptions) GetMountSecretNames(serviceaccount *corev1.ServiceAccount) sets.String { names := sets.String{} for _, secret := range serviceaccount.Secrets { names.Insert(secret.Name) @@ -143,7 +139,7 @@ func (o SecretOptions) GetMountSecretNames(serviceaccount *kapi.ServiceAccount) // GetPullSecretNames Get a list of the names of the pull secrets associated // with a service account. -func (o SecretOptions) GetPullSecretNames(serviceaccount *kapi.ServiceAccount) sets.String { +func (o SecretOptions) GetPullSecretNames(serviceaccount *corev1.ServiceAccount) sets.String { names := sets.String{} for _, secret := range serviceaccount.ImagePullSecrets { names.Insert(secret.Name) @@ -151,24 +147,15 @@ func (o SecretOptions) GetPullSecretNames(serviceaccount *kapi.ServiceAccount) s return names } -// GetOut Retrieve the output writer -func (o SecretOptions) GetOut() io.Writer { - if o.Out == nil { - return ioutil.Discard - } - - return o.Out -} - // GetSecrets Return a list of secret objects in the default namespace // If allowNonExisting is set to true, we will return the non-existing secrets as well. -func (o SecretOptions) GetSecrets(allowNonExisting bool) ([]*kapi.Secret, bool, error) { - secrets := []*kapi.Secret{} +func (o SecretOptions) GetSecrets(allowNonExisting bool) ([]*corev1.Secret, bool, error) { + secrets := []*corev1.Secret{} hasNotFound := false for _, secretName := range o.SecretNames { r := o.BuilderFunc(). - WithScheme(ocscheme.ReadingInternalScheme). + WithScheme(ocscheme.ReadingInternalScheme, ocscheme.ReadingInternalScheme.PrioritizedVersionsAllGroups()...). NamespaceParam(o.Namespace). ResourceNames("secrets", secretName). SingleResourceType(). @@ -184,7 +171,7 @@ func (o SecretOptions) GetSecrets(allowNonExisting bool) ([]*kapi.Secret, bool, fmt.Fprintf(os.Stderr, "secret %q not found\n", secretName) hasNotFound = true if allowNonExisting { - obj = &kapi.Secret{ + obj = &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, }, @@ -197,7 +184,7 @@ func (o SecretOptions) GetSecrets(allowNonExisting bool) ([]*kapi.Secret, bool, } } switch t := obj.(type) { - case *kapi.Secret: + case *corev1.Secret: secrets = append(secrets, t) default: return nil, false, fmt.Errorf("unhandled object: %#v", t) diff --git a/pkg/oc/cli/secrets/sshauth.go b/pkg/oc/cli/secrets/sshauth.go index eca058d11b98..9846ed9fdb12 100644 --- a/pkg/oc/cli/secrets/sshauth.go +++ b/pkg/oc/cli/secrets/sshauth.go @@ -3,16 +3,18 @@ package secrets import ( "errors" "fmt" - "io" "io/ioutil" - api "k8s.io/kubernetes/pkg/apis/core" - kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + "github.com/spf13/cobra" + + corev1 "k8s.io/api/core/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" - "github.com/spf13/cobra" + "github.com/openshift/origin/pkg/oc/util/ocscheme" ) // CreateSSHAuthSecretRecommendedCommandName represents name of subcommand for `oc secrets` command @@ -41,6 +43,10 @@ var ( // CreateSSHAuthSecretOptions holds the credential needed to authenticate against SCM servers. type CreateSSHAuthSecretOptions struct { + PrintFlags *genericclioptions.PrintFlags + + Printer printers.ResourcePrinter + SecretName string PrivateKeyPath string CertificatePath string @@ -48,16 +54,21 @@ type CreateSSHAuthSecretOptions struct { PromptForPassword bool - Out io.Writer + SecretsInterface corev1client.SecretInterface - SecretsInterface kcoreclient.SecretInterface + genericclioptions.IOStreams +} + +func NewCreateSSHAuthSecretOptions(streams genericclioptions.IOStreams) *CreateSSHAuthSecretOptions { + return &CreateSSHAuthSecretOptions{ + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + IOStreams: streams, + } } // NewCmdCreateSSHAuthSecret implements the OpenShift cli secrets new-sshauth subcommand func NewCmdCreateSSHAuthSecret(name, fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams, newSecretFullName, ocEditFullName string) *cobra.Command { - o := &CreateSSHAuthSecretOptions{ - Out: streams.Out, - } + o := NewCreateSSHAuthSecretOptions(streams) cmd := &cobra.Command{ Use: fmt.Sprintf("%s SECRET --ssh-privatekey=FILENAME [--ca-cert=FILENAME] [--gitconfig=FILENAME]", name), @@ -67,25 +78,9 @@ func NewCmdCreateSSHAuthSecret(name, fullName string, f kcmdutil.Factory, stream Deprecated: "use oc create secret", Hidden: true, Run: func(c *cobra.Command, args []string) { - if err := o.Complete(f, args); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if err := o.Validate(); err != nil { - kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) - } - - if len(kcmdutil.GetFlagString(c, "output")) != 0 { - secret, err := o.NewSSHAuthSecret() - kcmdutil.CheckErr(err) - - kcmdutil.CheckErr(kcmdutil.PrintObject(c, secret, streams.Out)) - return - } - - if err := o.CreateSSHAuthSecret(); err != nil { - kcmdutil.CheckErr(err) - } + kcmdutil.CheckErr(o.Complete(f, args)) + kcmdutil.CheckErr(o.Validate(args)) + kcmdutil.CheckErr(o.Run()) }, } @@ -96,13 +91,12 @@ func NewCmdCreateSSHAuthSecret(name, fullName string, f kcmdutil.Factory, stream cmd.Flags().StringVar(&o.GitConfigPath, "gitconfig", "", "Path to a .gitconfig file") cmd.MarkFlagFilename("gitconfig") - kcmdutil.AddPrinterFlags(cmd) - + o.PrintFlags.AddFlags(cmd) return cmd } // CreateSSHAuthSecret saves created Secret structure and prints the secret name to the output on success. -func (o *CreateSSHAuthSecretOptions) CreateSSHAuthSecret() error { +func (o *CreateSSHAuthSecretOptions) Run() error { secret, err := o.NewSSHAuthSecret() if err != nil { return err @@ -112,16 +106,15 @@ func (o *CreateSSHAuthSecretOptions) CreateSSHAuthSecret() error { return err } - fmt.Fprintf(o.GetOut(), "secret/%s\n", secret.Name) - return nil + return o.Printer.PrintObj(secret, o.Out) } // NewSSHAuthSecret builds up the Secret structure containing secret name, type and data structure // containing desired credentials. -func (o *CreateSSHAuthSecretOptions) NewSSHAuthSecret() (*api.Secret, error) { - secret := &api.Secret{} +func (o *CreateSSHAuthSecretOptions) NewSSHAuthSecret() (*corev1.Secret, error) { + secret := &corev1.Secret{} secret.Name = o.SecretName - secret.Type = api.SecretTypeSSHAuth + secret.Type = corev1.SecretTypeSSHAuth secret.Data = map[string][]byte{} if len(o.PrivateKeyPath) != 0 { @@ -154,32 +147,35 @@ func (o *CreateSSHAuthSecretOptions) NewSSHAuthSecret() (*api.Secret, error) { // Complete fills CreateSSHAuthSecretOptions fields with data and checks whether necessary // arguments were provided. func (o *CreateSSHAuthSecretOptions) Complete(f kcmdutil.Factory, args []string) error { - if len(args) != 1 { - return errors.New("must have exactly one argument: secret name") - } o.SecretName = args[0] - if f != nil { - clientConfig, err := f.ToRESTConfig() - if err != nil { - return err - } - client, err := kcoreclient.NewForConfig(clientConfig) - if err != nil { - return err - } - namespace, _, err := f.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - o.SecretsInterface = client.Secrets(namespace) + clientConfig, err := f.ToRESTConfig() + if err != nil { + return err + } + client, err := corev1client.NewForConfig(clientConfig) + if err != nil { + return err + } + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return err + } + o.SecretsInterface = client.Secrets(namespace) + + o.Printer, err = o.PrintFlags.ToPrinter() + if err != nil { + return err } return nil } // Validate check if all necessary fields from CreateSSHAuthSecretOptions are present. -func (o CreateSSHAuthSecretOptions) Validate() error { +func (o CreateSSHAuthSecretOptions) Validate(args []string) error { + if len(args) != 1 { + return errors.New("must have exactly one argument: secret name") + } if len(o.SecretName) == 0 { return errors.New("basic authentication secret name must be present") } @@ -190,13 +186,3 @@ func (o CreateSSHAuthSecretOptions) Validate() error { return nil } - -// GetOut check if the CreateSSHAuthSecretOptions Out Writer is set. Returns it if the Writer -// is present, if not returns Writer on which all Write calls succeed without doing anything. -func (o CreateSSHAuthSecretOptions) GetOut() io.Writer { - if o.Out == nil { - return ioutil.Discard - } - - return o.Out -} diff --git a/pkg/oc/cli/secrets/sshauth_test.go b/pkg/oc/cli/secrets/sshauth_test.go index 9e2e41c93c19..9b03b80cee70 100644 --- a/pkg/oc/cli/secrets/sshauth_test.go +++ b/pkg/oc/cli/secrets/sshauth_test.go @@ -3,56 +3,67 @@ package secrets import ( "testing" - api "k8s.io/kubernetes/pkg/apis/core" + corev1 "k8s.io/api/core/v1" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) func TestValidateSSHAuth(t *testing.T) { tests := []struct { testName string args []string - params CreateSSHAuthSecretOptions + options func(genericclioptions.IOStreams) *CreateSSHAuthSecretOptions expErr bool }{ { testName: "validArgs", args: []string{"testSecret"}, - params: CreateSSHAuthSecretOptions{ - PrivateKeyPath: "./bsFixtures/valid/ssh-privatekey", + options: func(streams genericclioptions.IOStreams) *CreateSSHAuthSecretOptions { + o := NewCreateSSHAuthSecretOptions(streams) + o.SecretName = "testSecret" + o.PrivateKeyPath = "./bsFixtures/valid/ssh-privatekey" + return o }, expErr: false, }, { testName: "validArgsWithCertificate", args: []string{"testSecret"}, - params: CreateSSHAuthSecretOptions{ - PrivateKeyPath: "./bsFixtures/valid/ssh-privatekey", - CertificatePath: "./bsFixtures/valid/ca.crt", + options: func(streams genericclioptions.IOStreams) *CreateSSHAuthSecretOptions { + o := NewCreateSSHAuthSecretOptions(streams) + o.SecretName = "testSecret" + o.PrivateKeyPath = "./bsFixtures/valid/ssh-privatekey" + o.CertificatePath = "./bsFixtures/valid/ca.crt" + return o }, expErr: false, }, { testName: "noName", args: []string{}, - params: CreateSSHAuthSecretOptions{ - PrivateKeyPath: "./bsFixtures/valid/ssh-privatekey", - CertificatePath: "./bsFixtures/valid/ca.crt", + options: func(streams genericclioptions.IOStreams) *CreateSSHAuthSecretOptions { + o := NewCreateSSHAuthSecretOptions(streams) + o.SecretName = "testSecret" + o.PrivateKeyPath = "./bsFixtures/valid/ssh-privatekey" + o.CertificatePath = "./bsFixtures/valid/ca.crt" + return o }, expErr: true, //"Must have exactly one argument: secret name" }, { testName: "noParams", args: []string{"testSecret"}, - params: CreateSSHAuthSecretOptions{}, - expErr: true, //"Must provide SSH authentication credentials" + options: func(streams genericclioptions.IOStreams) *CreateSSHAuthSecretOptions { + o := NewCreateSSHAuthSecretOptions(streams) + o.SecretName = "testSecret" + return o + }, + expErr: true, //"Must provide SSH authentication credentials" }, } for _, test := range tests { - options := test.params - err := options.Complete(nil, test.args) - if err == nil { - err = options.Validate() - } + options := test.options(genericclioptions.NewTestIOStreamsDiscard()) + err := options.Validate(test.args) if test.expErr { if err == nil { @@ -69,7 +80,7 @@ func TestValidateSSHAuth(t *testing.T) { if err != nil { t.Errorf("%s: unexpected error: %v", test.testName, err) } - if secret.Type != api.SecretTypeSSHAuth { + if secret.Type != corev1.SecretTypeSSHAuth { t.Errorf("%s: unexpected secret.Type: %v", test.testName, secret.Type) } } diff --git a/pkg/oc/cli/secrets/unlink_secret_from_object.go b/pkg/oc/cli/secrets/unlink_secret_from_object.go index 14ba4e626d3c..b787a87bc2e8 100644 --- a/pkg/oc/cli/secrets/unlink_secret_from_object.go +++ b/pkg/oc/cli/secrets/unlink_secret_from_object.go @@ -4,12 +4,15 @@ import ( "errors" "fmt" - kapi "k8s.io/kubernetes/pkg/apis/core" + "github.com/spf13/cobra" + + coreapiv1 "k8s.io/api/core/v1" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" - "github.com/spf13/cobra" + "github.com/openshift/origin/pkg/oc/util/ocscheme" ) const UnlinkSecretRecommendedName = "unlink" @@ -27,11 +30,24 @@ var ( type UnlinkSecretOptions struct { SecretOptions + + PrintFlags *genericclioptions.PrintFlags + Printer printers.ResourcePrinter + + genericclioptions.IOStreams +} + +func NewUnlinkSecretOptions(streams genericclioptions.IOStreams) *UnlinkSecretOptions { + return &UnlinkSecretOptions{ + PrintFlags: genericclioptions.NewPrintFlags("updated").WithTypeSetter(ocscheme.PrintingInternalScheme), + SecretOptions: SecretOptions{}, + IOStreams: streams, + } } // NewCmdUnlinkSecret creates a command object for detaching one or more secret references from a service account func NewCmdUnlinkSecret(name, fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := &UnlinkSecretOptions{SecretOptions{Out: streams.Out}} + o := NewUnlinkSecretOptions(streams) cmd := &cobra.Command{ Use: fmt.Sprintf("%s serviceaccount-name secret-name [another-secret-name] ...", name), @@ -46,17 +62,32 @@ func NewCmdUnlinkSecret(name, fullName string, f kcmdutil.Factory, streams gener kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error())) } - if err := o.UnlinkSecrets(); err != nil { + if err := o.Run(); err != nil { kcmdutil.CheckErr(err) } }, } + o.PrintFlags.AddFlags(cmd) return cmd } -func (o UnlinkSecretOptions) UnlinkSecrets() error { +func (o *UnlinkSecretOptions) Complete(f kcmdutil.Factory, args []string) error { + if err := o.SecretOptions.Complete(f, args); err != nil { + return err + } + + var err error + o.Printer, err = o.PrintFlags.ToPrinter() + if err != nil { + return err + } + + return nil +} + +func (o UnlinkSecretOptions) Run() error { serviceaccount, err := o.GetServiceAccount() if err != nil { return err @@ -66,11 +97,11 @@ func (o UnlinkSecretOptions) UnlinkSecrets() error { return err } - return nil + return o.Printer.PrintObj(serviceaccount, o.Out) } // unlinkSecretsFromServiceAccount detaches pull and mount secrets from the service account. -func (o UnlinkSecretOptions) unlinkSecretsFromServiceAccount(serviceaccount *kapi.ServiceAccount) error { +func (o UnlinkSecretOptions) unlinkSecretsFromServiceAccount(serviceaccount *coreapiv1.ServiceAccount) error { // All of the requested secrets must be present in either the Mount or Pull secrets // If any of them are not present, we'll return an error and push no changes. rmSecrets, hasNotFound, err := o.GetSecrets(true) @@ -79,8 +110,8 @@ func (o UnlinkSecretOptions) unlinkSecretsFromServiceAccount(serviceaccount *kap } rmSecretNames := o.GetSecretNames(rmSecrets) - newMountSecrets := []kapi.ObjectReference{} - newPullSecrets := []kapi.LocalObjectReference{} + newMountSecrets := []coreapiv1.ObjectReference{} + newPullSecrets := []coreapiv1.LocalObjectReference{} updated := false // Check the mount secrets @@ -107,7 +138,7 @@ func (o UnlinkSecretOptions) unlinkSecretsFromServiceAccount(serviceaccount *kap // Save the updated Secret lists back to the server serviceaccount.Secrets = newMountSecrets serviceaccount.ImagePullSecrets = newPullSecrets - _, err = o.KubeCoreClient.ServiceAccounts(o.Namespace).Update(serviceaccount) + _, err = o.KubeClient.ServiceAccounts(o.Namespace).Update(serviceaccount) if err != nil { return err } From c799d38b4e0efabdf4c7f5db5656e0ad8ff6a48b Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Thu, 26 Jul 2018 18:03:40 -0400 Subject: [PATCH 07/12] update generated completions --- contrib/completions/bash/oc | 9 +++++++++ contrib/completions/zsh/oc | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/contrib/completions/bash/oc b/contrib/completions/bash/oc index 067373f4f0c2..77544f278c2f 100644 --- a/contrib/completions/bash/oc +++ b/contrib/completions/bash/oc @@ -14950,6 +14950,15 @@ _oc_secrets_unlink() flags_with_completion=() flags_completion=() + flags+=("--allow-missing-template-keys") + local_nonpersistent_flags+=("--allow-missing-template-keys") + flags+=("--output=") + two_word_flags+=("-o") + local_nonpersistent_flags+=("--output=") + flags+=("--template=") + flags_with_completion+=("--template") + flags_completion+=("_filedir") + local_nonpersistent_flags+=("--template=") flags+=("--as=") flags+=("--as-group=") flags+=("--cache-dir=") diff --git a/contrib/completions/zsh/oc b/contrib/completions/zsh/oc index 7ed42a23bb85..e80cdd3b25ca 100644 --- a/contrib/completions/zsh/oc +++ b/contrib/completions/zsh/oc @@ -15092,6 +15092,15 @@ _oc_secrets_unlink() flags_with_completion=() flags_completion=() + flags+=("--allow-missing-template-keys") + local_nonpersistent_flags+=("--allow-missing-template-keys") + flags+=("--output=") + two_word_flags+=("-o") + local_nonpersistent_flags+=("--output=") + flags+=("--template=") + flags_with_completion+=("--template") + flags_completion+=("_filedir") + local_nonpersistent_flags+=("--template=") flags+=("--as=") flags+=("--as-group=") flags+=("--cache-dir=") From 0d58328e7f4aed94904c4701db6ae79ea7aeeedb Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Fri, 27 Jul 2018 17:19:34 -0400 Subject: [PATCH 08/12] update import restrictions --- hack/import-restrictions.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hack/import-restrictions.json b/hack/import-restrictions.json index 615a0a475a9f..6d43677a7d5d 100644 --- a/hack/import-restrictions.json +++ b/hack/import-restrictions.json @@ -476,7 +476,9 @@ "github.com/openshift/origin/pkg/authorization/generated", "github.com/openshift/origin/pkg/build/apis/build/v1", "github.com/openshift/origin/pkg/build/generated", + "github.com/openshift/origin/pkg/image/apis/image/v1", "github.com/openshift/origin/pkg/image/generated", + "github.com/openshift/origin/pkg/image/util", "github.com/openshift/origin/pkg/network/generated", "github.com/openshift/origin/pkg/oauth/generated", "github.com/openshift/origin/pkg/project/generated", From 229c0c9e80380a407e25f06c8cf039c8d3dd1cad Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Wed, 1 Aug 2018 10:39:50 -0400 Subject: [PATCH 09/12] update log cmd to use corev1.PodLogOptions --- pkg/oc/cli/logs/logs.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pkg/oc/cli/logs/logs.go b/pkg/oc/cli/logs/logs.go index e7592258d4ec..8bdc23c90f8e 100644 --- a/pkg/oc/cli/logs/logs.go +++ b/pkg/oc/cli/logs/logs.go @@ -6,9 +6,9 @@ import ( "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - kapi "k8s.io/kubernetes/pkg/apis/core" kcmd "k8s.io/kubernetes/pkg/kubectl/cmd" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -82,10 +82,8 @@ type LogsOptions struct { func NewLogsOptions(streams genericclioptions.IOStreams) *LogsOptions { return &LogsOptions{ - KubeLogOptions: &kcmd.LogsOptions{ - IOStreams: streams, - }, - IOStreams: streams, + KubeLogOptions: kcmd.NewLogsOptions(streams, false), + IOStreams: streams, } } @@ -99,7 +97,7 @@ func NewCmdLogs(name, baseName string, f kcmdutil.Factory, streams genericcliopt cmd.SuggestFor = []string{"builds", "deployments"} cmd.Run = func(cmd *cobra.Command, args []string) { kcmdutil.CheckErr(o.Complete(f, cmd, args)) - kcmdutil.CheckErr(o.Validate()) + kcmdutil.CheckErr(o.Validate(args)) kcmdutil.CheckErr(o.RunLog()) } @@ -151,7 +149,7 @@ func (o *LogsOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args []st // Validate runs the upstream validation for the logs command and then it // will validate any OpenShift-specific log options. -func (o *LogsOptions) Validate() error { +func (o *LogsOptions) Validate(args []string) error { if err := o.KubeLogOptions.Validate(); err != nil { return err } @@ -176,7 +174,7 @@ func (o *LogsOptions) Validate() error { // RunLog will run the upstream logs command and may use an OpenShift // logOptions object. func (o *LogsOptions) RunLog() error { - podLogOptions := o.KubeLogOptions.Options.(*kapi.PodLogOptions) + podLogOptions := o.KubeLogOptions.Options.(*corev1.PodLogOptions) infos, err := o.Builder(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(o.Namespace).DefaultNamespace(). From b492589ba81b621c66a9b3a2a30f21064f55e5c9 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Thu, 2 Aug 2018 13:14:51 +0200 Subject: [PATCH 10/12] React to LogsForObjectFn signature change --- .../client/pod/run_diagnostics_pod.go | 43 +++++++------- .../diagnostics/cluster/network/results.go | 58 ++++++++++--------- .../diagnostics/cluster/network/setup.go | 14 +++-- .../originpolymorphichelpers/logsforobject.go | 28 +++++---- 4 files changed, 79 insertions(+), 64 deletions(-) diff --git a/pkg/oc/cli/admin/diagnostics/diagnostics/client/pod/run_diagnostics_pod.go b/pkg/oc/cli/admin/diagnostics/diagnostics/client/pod/run_diagnostics_pod.go index adce5f883512..3272adf3c0eb 100644 --- a/pkg/oc/cli/admin/diagnostics/diagnostics/client/pod/run_diagnostics_pod.go +++ b/pkg/oc/cli/admin/diagnostics/diagnostics/client/pod/run_diagnostics_pod.go @@ -10,6 +10,7 @@ import ( "syscall" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kapi "k8s.io/kubernetes/pkg/apis/core" kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -142,13 +143,13 @@ func (d *DiagnosticPod) processDiagnosticPodResults(protoPod *kapi.Pod, imageNam r.Debug("DCli2004", fmt.Sprintf("Created diagnostic pod named %v running image %s.", pod.ObjectMeta.Name, imageName)) bytelim := int64(1024000) - podLogsOpts := &kapi.PodLogOptions{ + podLogsOpts := &corev1.PodLogOptions{ TypeMeta: pod.TypeMeta, Container: "pod-diagnostics", Follow: true, LimitBytes: &bytelim, } - req, err := polymorphichelpers.LogsForObjectFn(d.Factory, pod, podLogsOpts, 1*time.Minute) + requests, err := polymorphichelpers.LogsForObjectFn(d.Factory, pod, podLogsOpts, 1*time.Minute, false) if err != nil { r.Error("DCli2005", err, fmt.Sprintf("The request for diagnostic pod logs failed unexpectedly. Error: (%T) %[1]v", err)) return @@ -157,28 +158,30 @@ func (d *DiagnosticPod) processDiagnosticPodResults(protoPod *kapi.Pod, imageNam // wait for pod to be started and logs available var scanner *bufio.Scanner var lastError error +outerLoop: for times := 1; true; times++ { if times <= 50 { - readCloser, err := req.Stream() - if err != nil { - lastError = err - r.Debug("DCli2010", fmt.Sprintf("Could not get diagnostic pod logs (loop %d): (%T[2]) %[2]v", times, err)) + for _, req := range requests { + readCloser, err := req.Stream() + if err != nil { + lastError = err + r.Debug("DCli2010", fmt.Sprintf("Could not get diagnostic pod logs (loop %d): (%T[2]) %[2]v", times, err)) + time.Sleep(time.Duration(times*100) * time.Millisecond) + continue outerLoop + } + // make sure we can actually get something from the stream before going on. + // it seems the creation of docker logs can trail the container start a bit. + lineScanner := bufio.NewScanner(readCloser) + if lineScanner.Scan() { + scanner = lineScanner + break outerLoop // success - drop down to reading the logs. + } + // no luck - try, try again + lastError = fmt.Errorf("Diagnostics pod is ready but not its logs (loop %d). Retry.", times) + r.Debug("DCli2010", lastError.Error()) time.Sleep(time.Duration(times*100) * time.Millisecond) - continue + continue outerLoop } - defer readCloser.Close() - // make sure we can actually get something from the stream before going on. - // it seems the creation of docker logs can trail the container start a bit. - lineScanner := bufio.NewScanner(readCloser) - if lineScanner.Scan() { - scanner = lineScanner - break // success - drop down to reading the logs. - } - // no luck - try, try again - lastError = fmt.Errorf("Diagnostics pod is ready but not its logs (loop %d). Retry.", times) - r.Debug("DCli2010", lastError.Error()) - time.Sleep(time.Duration(times*100) * time.Millisecond) - continue } // tries exhausted r.Warn("DCli2006", err, fmt.Sprintf("Timed out preparing diagnostic pod logs for streaming, so this diagnostic cannot run.\nIt is likely that the image '%s' was not pulled and running yet.\nLast error: (%T[2]) %[2]v", pod.Spec.Containers[0].Image, lastError)) diff --git a/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/results.go b/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/results.go index 37d0bd2cb7d1..ae03927bce74 100644 --- a/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/results.go +++ b/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/results.go @@ -106,40 +106,42 @@ func (d *NetworkDiagnostic) getNetworkPodLogs(pod *kapi.Pod) error { LimitBytes: &bytelim, } - req, err := polymorphichelpers.LogsForObjectFn(d.Factory, pod, opts, 1*time.Minute) + requests, err := polymorphichelpers.LogsForObjectFn(d.Factory, pod, opts, 1*time.Minute, false) if err != nil { return fmt.Errorf("Request for network diagnostic pod on node %q failed unexpectedly: %v", pod.Spec.NodeName, err) } - readCloser, err := req.Stream() - if err != nil { - return fmt.Errorf("Logs for network diagnostic pod on node %q failed: %v", pod.Spec.NodeName, err) - } - defer readCloser.Close() - - scanner := bufio.NewScanner(readCloser) - podLogs, nwarnings, nerrors := "", 0, 0 - errorRegex := regexp.MustCompile(`^\[Note\]\s+Errors\s+seen:\s+(\d+)`) - warnRegex := regexp.MustCompile(`^\[Note\]\s+Warnings\s+seen:\s+(\d+)`) - - for scanner.Scan() { - line := scanner.Text() - podLogs += line + "\n" - if matches := errorRegex.FindStringSubmatch(line); matches != nil { - nerrors, _ = strconv.Atoi(matches[1]) - } else if matches := warnRegex.FindStringSubmatch(line); matches != nil { - nwarnings, _ = strconv.Atoi(matches[1]) + for _, req := range requests { + readCloser, err := req.Stream() + if err != nil { + return fmt.Errorf("Logs for network diagnostic pod on node %q failed: %v", pod.Spec.NodeName, err) + } + defer readCloser.Close() + + scanner := bufio.NewScanner(readCloser) + podLogs, nwarnings, nerrors := "", 0, 0 + errorRegex := regexp.MustCompile(`^\[Note\]\s+Errors\s+seen:\s+(\d+)`) + warnRegex := regexp.MustCompile(`^\[Note\]\s+Warnings\s+seen:\s+(\d+)`) + + for scanner.Scan() { + line := scanner.Text() + podLogs += line + "\n" + if matches := errorRegex.FindStringSubmatch(line); matches != nil { + nerrors, _ = strconv.Atoi(matches[1]) + } else if matches := warnRegex.FindStringSubmatch(line); matches != nil { + nwarnings, _ = strconv.Atoi(matches[1]) + } } - } - if err := scanner.Err(); err != nil { // Scan terminated abnormally - return fmt.Errorf("Unexpected error reading network diagnostic pod on node %q: (%T) %[1]v\nLogs are:\n%[3]s", pod.Spec.NodeName, err, podLogs) - } else { - if nerrors > 0 { - return fmt.Errorf("See the errors below in the output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs) - } else if nwarnings > 0 { - d.res.Warn("DNet4002", nil, fmt.Sprintf("See the warnings below in the output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs)) + if err := scanner.Err(); err != nil { // Scan terminated abnormally + return fmt.Errorf("Unexpected error reading network diagnostic pod on node %q: (%T) %[1]v\nLogs are:\n%[3]s", pod.Spec.NodeName, err, podLogs) } else { - d.res.Info("DNet4003", fmt.Sprintf("Output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs)) + if nerrors > 0 { + return fmt.Errorf("See the errors below in the output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs) + } else if nwarnings > 0 { + d.res.Warn("DNet4002", nil, fmt.Sprintf("See the warnings below in the output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs)) + } else { + d.res.Info("DNet4003", fmt.Sprintf("Output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs)) + } } } return nil diff --git a/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/setup.go b/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/setup.go index 58b94edc0ffa..6e1c7567b70c 100644 --- a/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/setup.go +++ b/pkg/oc/cli/admin/diagnostics/diagnostics/cluster/network/setup.go @@ -194,17 +194,19 @@ func (d *NetworkDiagnostic) getPodLogs(nsList []string) (string, error) { LimitBytes: &limit, } - req, err := polymorphichelpers.LogsForObjectFn(d.Factory, &pod, opts, 10*time.Second) + requests, err := polymorphichelpers.LogsForObjectFn(d.Factory, &pod, opts, 10*time.Second, false) if err != nil { errList = append(errList, err) continue } - data, err := req.DoRaw() - if err != nil { - errList = append(errList, err) - continue + for _, req := range requests { + data, err := req.DoRaw() + if err != nil { + errList = append(errList, err) + continue + } + logData.Insert(string(data[:])) } - logData.Insert(string(data[:])) } } return strings.Join(logData.List(), ", "), kerrors.NewAggregate(errList) diff --git a/pkg/oc/originpolymorphichelpers/logsforobject.go b/pkg/oc/originpolymorphichelpers/logsforobject.go index 2921e15b298f..63b4fe4978d3 100644 --- a/pkg/oc/originpolymorphichelpers/logsforobject.go +++ b/pkg/oc/originpolymorphichelpers/logsforobject.go @@ -29,7 +29,7 @@ import ( ) func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorphichelpers.LogsForObjectFunc { - return func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) { + return func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) { clientConfig, err := restClientGetter.ToRESTConfig() if err != nil { return nil, err @@ -45,7 +45,8 @@ func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorph if err != nil { return nil, err } - return appsmanualclient.NewRolloutLogClient(appsClient.AppsV1().RESTClient(), t.Namespace).Logs(t.Name, *dopts), nil + // TODO: support allContainers flag + return []*rest.Request{appsmanualclient.NewRolloutLogClient(appsClient.AppsV1().RESTClient(), t.Namespace).Logs(t.Name, *dopts)}, nil case *appsv1.DeploymentConfig: dopts, ok := options.(*appsv1.DeploymentLogOptions) if !ok { @@ -55,7 +56,8 @@ func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorph if err != nil { return nil, err } - return appsmanualclientv1.NewRolloutLogClient(appsClient.RESTClient(), t.Namespace).Logs(t.Name, *dopts), nil + // TODO: support allContainers flag + return []*rest.Request{appsmanualclientv1.NewRolloutLogClient(appsClient.RESTClient(), t.Namespace).Logs(t.Name, *dopts)}, nil case *buildv1.Build: bopts, ok := options.(*buildv1.BuildLogOptions) if !ok { @@ -68,7 +70,8 @@ func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorph if err != nil { return nil, err } - return buildmanualclientv1.NewBuildLogClient(buildClient.RESTClient(), t.Namespace).Logs(t.Name, *bopts), nil + // TODO: support allContainers flag + return []*rest.Request{buildmanualclientv1.NewBuildLogClient(buildClient.RESTClient(), t.Namespace).Logs(t.Name, *bopts)}, nil case *buildv1.BuildConfig: bopts, ok := options.(*buildv1.BuildLogOptions) if !ok { @@ -93,10 +96,12 @@ func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorph if bopts.Version != nil { // If a version has been specified, try to get the logs from that build. desired := buildutil.BuildNameForConfigVersion(t.Name, int(*bopts.Version)) - return logClient.Logs(desired, *bopts), nil + // TODO: support allContainers flag + return []*rest.Request{logClient.Logs(desired, *bopts)}, nil } sort.Sort(sort.Reverse(ocbuildapihelpers.BuildSliceByCreationTimestamp(filteredInternalBuildItems))) - return logClient.Logs(filteredInternalBuildItems[0].Name, *bopts), nil + // TODO: support allContainers flag + return []*rest.Request{logClient.Logs(filteredInternalBuildItems[0].Name, *bopts)}, nil case *buildapi.Build: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { @@ -109,7 +114,8 @@ func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorph if err != nil { return nil, err } - return buildmanualclient.NewBuildLogClient(buildClient.Build().RESTClient(), t.Namespace).Logs(t.Name, *bopts), nil + // TODO: support allContainers flag + return []*rest.Request{buildmanualclient.NewBuildLogClient(buildClient.Build().RESTClient(), t.Namespace).Logs(t.Name, *bopts)}, nil case *buildapi.BuildConfig: bopts, ok := options.(*buildapi.BuildLogOptions) if !ok { @@ -131,13 +137,15 @@ func NewLogsForObjectFn(delegate polymorphichelpers.LogsForObjectFunc) polymorph if bopts.Version != nil { // If a version has been specified, try to get the logs from that build. desired := buildutil.BuildNameForConfigVersion(t.Name, int(*bopts.Version)) - return logClient.Logs(desired, *bopts), nil + // TODO: support allContainers flag + return []*rest.Request{logClient.Logs(desired, *bopts)}, nil } sort.Sort(sort.Reverse(ocbuildapihelpers.BuildSliceByCreationTimestampInternal(builds.Items))) - return logClient.Logs(builds.Items[0].Name, *bopts), nil + // TODO: support allContainers flag + return []*rest.Request{logClient.Logs(builds.Items[0].Name, *bopts)}, nil default: - return delegate(restClientGetter, object, options, timeout) + return delegate(restClientGetter, object, options, timeout, allContainers) } } } From fdb192b03191a5c005fbd7a6676b36ffbc7310eb Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Thu, 2 Aug 2018 11:52:44 +0200 Subject: [PATCH 11/12] Change scheme to kubectl and fix test-cmd --- pkg/oc/cli/debug/debug.go | 1 - pkg/oc/cli/secrets/basicauth.go | 21 +++++++------------ pkg/oc/cli/secrets/basicauth_test.go | 12 ----------- pkg/oc/cli/secrets/dockercfg.go | 4 ++-- pkg/oc/cli/secrets/new.go | 5 ++--- pkg/oc/cli/secrets/options.go | 4 +--- pkg/oc/cli/secrets/sshauth.go | 5 ++--- .../cli/secrets/unlink_secret_from_object.go | 5 ++--- 8 files changed, 17 insertions(+), 40 deletions(-) diff --git a/pkg/oc/cli/debug/debug.go b/pkg/oc/cli/debug/debug.go index 0066beeb0a67..2acf18cc1c01 100644 --- a/pkg/oc/cli/debug/debug.go +++ b/pkg/oc/cli/debug/debug.go @@ -589,7 +589,6 @@ func (o *DebugOptions) transformPodForDebug(annotations map[string]string) (*cor // identify the command to be run originalCommand, _ := o.getContainerImageCommand(pod, container) - //originalCommand := []string{""} if len(container.Command) > 0 { originalCommand = container.Command originalCommand = append(originalCommand, container.Args...) diff --git a/pkg/oc/cli/secrets/basicauth.go b/pkg/oc/cli/secrets/basicauth.go index d944ab209b64..c661b991cc7e 100644 --- a/pkg/oc/cli/secrets/basicauth.go +++ b/pkg/oc/cli/secrets/basicauth.go @@ -13,10 +13,10 @@ import ( kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" + "k8s.io/kubernetes/pkg/kubectl/scheme" kterm "k8s.io/kubernetes/pkg/kubectl/util/term" "github.com/openshift/origin/pkg/cmd/util/term" - "github.com/openshift/origin/pkg/oc/util/ocscheme" ) // CreateBasicAuthSecretRecommendedCommandName represents name of subcommand for `oc secrets` command @@ -64,7 +64,7 @@ type CreateBasicAuthSecretOptions struct { func NewCreateBasicAuthSecretOptions(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { return &CreateBasicAuthSecretOptions{ - PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme), IOStreams: streams, } } @@ -105,7 +105,6 @@ func (o *CreateBasicAuthSecretOptions) Run() error { if err != nil { return err } - if _, err := o.SecretsInterface.Create(secret); err != nil { return err } @@ -154,10 +153,12 @@ func (o *CreateBasicAuthSecretOptions) Complete(f kcmdutil.Factory, args []strin if len(args) != 1 { return errors.New("must have exactly one argument: secret name") } - o.SecretName = args[0] if o.PromptForPassword { + if len(o.Password) > 0 { + return errors.New("must provide either --prompt or --password flag") + } if !kterm.IsTerminal(o.In) { return errors.New("provided reader is not a terminal") } @@ -168,16 +169,15 @@ func (o *CreateBasicAuthSecretOptions) Complete(f kcmdutil.Factory, args []strin } } - config, err := f.ToRESTConfig() + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - - clientset, err := corev1client.NewForConfig(config) + config, err := f.ToRESTConfig() if err != nil { return err } - namespace, _, err := f.ToRawKubeConfigLoader().Namespace() + clientset, err := corev1client.NewForConfig(config) if err != nil { return err } @@ -196,14 +196,9 @@ func (o CreateBasicAuthSecretOptions) Validate() error { if len(o.SecretName) == 0 { return errors.New("basic authentication secret name must be present") } - if len(o.Username) == 0 && len(o.Password) == 0 { return errors.New("must provide basic authentication credentials") } - if o.PromptForPassword && len(o.Password) > 0 { - return errors.New("must provide either --prompt or --password flag") - } - return nil } diff --git a/pkg/oc/cli/secrets/basicauth_test.go b/pkg/oc/cli/secrets/basicauth_test.go index a35d1ac94c95..b26148d0af04 100644 --- a/pkg/oc/cli/secrets/basicauth_test.go +++ b/pkg/oc/cli/secrets/basicauth_test.go @@ -67,18 +67,6 @@ func TestValidateBasicAuth(t *testing.T) { }, expErr: true, //"Must provide basic authentication credentials" }, - { - testName: "passwordAndPrompt", - options: func(streams genericclioptions.IOStreams) *CreateBasicAuthSecretOptions { - o := NewCreateBasicAuthSecretOptions(streams) - o.Username = "testUser" - o.Password = "testPassword" - o.SecretName = "testSecret" - o.PromptForPassword = true - return o - }, - expErr: true, //"Must provide either --prompt or --password flag" - }, } for _, test := range tests { diff --git a/pkg/oc/cli/secrets/dockercfg.go b/pkg/oc/cli/secrets/dockercfg.go index 3392434cb2ba..447d31eb52ca 100644 --- a/pkg/oc/cli/secrets/dockercfg.go +++ b/pkg/oc/cli/secrets/dockercfg.go @@ -8,7 +8,6 @@ import ( "github.com/spf13/cobra" - "github.com/openshift/origin/pkg/oc/util/ocscheme" corev1 "k8s.io/api/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/kubernetes/pkg/credentialprovider" @@ -16,6 +15,7 @@ import ( kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) const CreateDockerConfigSecretRecommendedName = "new-dockercfg" @@ -67,7 +67,7 @@ type CreateDockerConfigOptions struct { func NewCreateDockerConfigOptions(streams genericclioptions.IOStreams) *CreateDockerConfigOptions { return &CreateDockerConfigOptions{ - PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme), IOStreams: streams, } } diff --git a/pkg/oc/cli/secrets/new.go b/pkg/oc/cli/secrets/new.go index 781204d2f496..2a7a065cafcc 100644 --- a/pkg/oc/cli/secrets/new.go +++ b/pkg/oc/cli/secrets/new.go @@ -18,8 +18,7 @@ import ( kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" - - "github.com/openshift/origin/pkg/oc/util/ocscheme" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) const NewSecretRecommendedCommandName = "new" @@ -76,7 +75,7 @@ type CreateSecretOptions struct { func NewCreateSecretOptions(streams genericclioptions.IOStreams) *CreateSecretOptions { return &CreateSecretOptions{ - PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme), IOStreams: streams, } } diff --git a/pkg/oc/cli/secrets/options.go b/pkg/oc/cli/secrets/options.go index 3651fc1015d9..e6dc3a4a5f39 100644 --- a/pkg/oc/cli/secrets/options.go +++ b/pkg/oc/cli/secrets/options.go @@ -14,8 +14,6 @@ import ( kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/scheme" - - "github.com/openshift/origin/pkg/oc/util/ocscheme" ) // SecretOptions Structure holding state for processing secret linking and @@ -155,7 +153,7 @@ func (o SecretOptions) GetSecrets(allowNonExisting bool) ([]*corev1.Secret, bool for _, secretName := range o.SecretNames { r := o.BuilderFunc(). - WithScheme(ocscheme.ReadingInternalScheme, ocscheme.ReadingInternalScheme.PrioritizedVersionsAllGroups()...). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(o.Namespace). ResourceNames("secrets", secretName). SingleResourceType(). diff --git a/pkg/oc/cli/secrets/sshauth.go b/pkg/oc/cli/secrets/sshauth.go index 9846ed9fdb12..10b8d1952ea4 100644 --- a/pkg/oc/cli/secrets/sshauth.go +++ b/pkg/oc/cli/secrets/sshauth.go @@ -13,8 +13,7 @@ import ( kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" - - "github.com/openshift/origin/pkg/oc/util/ocscheme" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) // CreateSSHAuthSecretRecommendedCommandName represents name of subcommand for `oc secrets` command @@ -61,7 +60,7 @@ type CreateSSHAuthSecretOptions struct { func NewCreateSSHAuthSecretOptions(streams genericclioptions.IOStreams) *CreateSSHAuthSecretOptions { return &CreateSSHAuthSecretOptions{ - PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(ocscheme.PrintingInternalScheme), + PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme), IOStreams: streams, } } diff --git a/pkg/oc/cli/secrets/unlink_secret_from_object.go b/pkg/oc/cli/secrets/unlink_secret_from_object.go index b787a87bc2e8..64322e94e8a7 100644 --- a/pkg/oc/cli/secrets/unlink_secret_from_object.go +++ b/pkg/oc/cli/secrets/unlink_secret_from_object.go @@ -11,8 +11,7 @@ import ( kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" - - "github.com/openshift/origin/pkg/oc/util/ocscheme" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) const UnlinkSecretRecommendedName = "unlink" @@ -39,7 +38,7 @@ type UnlinkSecretOptions struct { func NewUnlinkSecretOptions(streams genericclioptions.IOStreams) *UnlinkSecretOptions { return &UnlinkSecretOptions{ - PrintFlags: genericclioptions.NewPrintFlags("updated").WithTypeSetter(ocscheme.PrintingInternalScheme), + PrintFlags: genericclioptions.NewPrintFlags("updated").WithTypeSetter(scheme.Scheme), SecretOptions: SecretOptions{}, IOStreams: streams, } From 74c37b889cfc47f6b83c3daada938bb24bd642f3 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Thu, 2 Aug 2018 11:45:18 -0400 Subject: [PATCH 12/12] manually bind flag values from upstream logs cmd --- pkg/oc/cli/logs/logs.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/oc/cli/logs/logs.go b/pkg/oc/cli/logs/logs.go index 8bdc23c90f8e..102fb8d52f0d 100644 --- a/pkg/oc/cli/logs/logs.go +++ b/pkg/oc/cli/logs/logs.go @@ -123,10 +123,31 @@ func isPipelineBuild(obj runtime.Object) (bool, *buildv1.BuildConfig, bool, *bui // resource a user requested to view its logs and creates the appropriate logOptions // object for it. func (o *LogsOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args []string) error { + // manually bind all flag values from the upstream command + // TODO: once the upstream command supports binding flags + // by outside callers, this will no longer be needed. + o.KubeLogOptions.AllContainers = kcmdutil.GetFlagBool(cmd, "all-containers") + o.KubeLogOptions.Container = kcmdutil.GetFlagString(cmd, "container") + o.KubeLogOptions.Selector = kcmdutil.GetFlagString(cmd, "selector") + o.KubeLogOptions.Follow = kcmdutil.GetFlagBool(cmd, "follow") + o.KubeLogOptions.Previous = kcmdutil.GetFlagBool(cmd, "previous") + o.KubeLogOptions.Timestamps = kcmdutil.GetFlagBool(cmd, "timestamps") + o.KubeLogOptions.SinceTime = kcmdutil.GetFlagString(cmd, "since-time") + o.KubeLogOptions.LimitBytes = kcmdutil.GetFlagInt64(cmd, "limit-bytes") + o.KubeLogOptions.Tail = kcmdutil.GetFlagInt64(cmd, "tail") + o.KubeLogOptions.SinceSeconds = kcmdutil.GetFlagDuration(cmd, "since") + o.KubeLogOptions.ContainerNameSpecified = cmd.Flag("container").Changed + if err := o.KubeLogOptions.Complete(f, cmd, args); err != nil { return err } + var err error + o.KubeLogOptions.GetPodTimeout, err = kcmdutil.GetPodRunningTimeoutFlag(cmd) + if err != nil { + return err + } + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err