start splitting polymorphic functions out of the factory

pull/8/head
David Eads 2018-05-16 10:47:29 -04:00
parent 0db40da909
commit eabfcfaa2b
24 changed files with 758 additions and 568 deletions

View File

@ -205,6 +205,7 @@ filegroup(
"//pkg/kubectl/genericclioptions:all-srcs", "//pkg/kubectl/genericclioptions:all-srcs",
"//pkg/kubectl/metricsutil:all-srcs", "//pkg/kubectl/metricsutil:all-srcs",
"//pkg/kubectl/plugins:all-srcs", "//pkg/kubectl/plugins:all-srcs",
"//pkg/kubectl/polymorphichelpers:all-srcs",
"//pkg/kubectl/proxy:all-srcs", "//pkg/kubectl/proxy:all-srcs",
"//pkg/kubectl/scheme:all-srcs", "//pkg/kubectl/scheme:all-srcs",
"//pkg/kubectl/util:all-srcs", "//pkg/kubectl/util:all-srcs",

View File

@ -82,6 +82,7 @@ go_library(
"//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/metricsutil:go_default_library", "//pkg/kubectl/metricsutil:go_default_library",
"//pkg/kubectl/plugins:go_default_library", "//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/polymorphichelpers:go_default_library",
"//pkg/kubectl/proxy:go_default_library", "//pkg/kubectl/proxy:go_default_library",
"//pkg/kubectl/scheme:go_default_library", "//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util:go_default_library", "//pkg/kubectl/util:go_default_library",
@ -193,6 +194,7 @@ go_test(
"//pkg/apis/batch:go_default_library", "//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/create:go_default_library", "//pkg/kubectl/cmd/create:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library",
@ -201,6 +203,7 @@ go_test(
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/plugins:go_default_library", "//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/polymorphichelpers:go_default_library",
"//pkg/kubectl/scheme:go_default_library", "//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library", "//pkg/kubectl/util/i18n:go_default_library",
"//pkg/kubectl/util/term:go_default_library", "//pkg/kubectl/util/term:go_default_library",

View File

@ -26,14 +26,13 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
) )
@ -46,10 +45,11 @@ type ClusterInfoDumpOptions struct {
AllNamespaces bool AllNamespaces bool
Namespaces []string Namespaces []string
timeout time.Duration Timeout time.Duration
clientset internalclientset.Interface Clientset internalclientset.Interface
namespace string Namespace string
logsForObject func(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) RESTClientGetter genericclioptions.RESTClientGetter
LogsForObject polymorphichelpers.LogsForObjectFunc
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -126,25 +126,27 @@ func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command)
o.PrintFlags.OutputFormat = &jsonOutputFmt o.PrintFlags.OutputFormat = &jsonOutputFmt
o.PrintObj = printer.PrintObj o.PrintObj = printer.PrintObj
o.timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd) o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil { if err != nil {
return err return err
} }
o.clientset, err = f.ClientSet() o.Clientset, err = f.ClientSet()
if err != nil { if err != nil {
return err return err
} }
o.namespace, _, err = f.DefaultNamespace() o.Namespace, _, err = f.DefaultNamespace()
if err != nil { if err != nil {
return err return err
} }
o.logsForObject = f.LogsForObject // TODO this should eventually just be the completed kubeconfigflag struct
o.RESTClientGetter = f
o.LogsForObject = polymorphichelpers.LogsForObjectFn
return nil return nil
} }
func (o *ClusterInfoDumpOptions) Run() error { func (o *ClusterInfoDumpOptions) Run() error {
nodes, err := o.clientset.Core().Nodes().List(metav1.ListOptions{}) nodes, err := o.Clientset.Core().Nodes().List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -155,7 +157,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
var namespaces []string var namespaces []string
if o.AllNamespaces { if o.AllNamespaces {
namespaceList, err := o.clientset.Core().Namespaces().List(metav1.ListOptions{}) namespaceList, err := o.Clientset.Core().Namespaces().List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -166,14 +168,14 @@ func (o *ClusterInfoDumpOptions) Run() error {
if len(o.Namespaces) == 0 { if len(o.Namespaces) == 0 {
namespaces = []string{ namespaces = []string{
metav1.NamespaceSystem, metav1.NamespaceSystem,
o.namespace, o.Namespace,
} }
} }
} }
for _, namespace := range namespaces { for _, namespace := range namespaces {
// TODO: this is repetitive in the extreme. Use reflection or // TODO: this is repetitive in the extreme. Use reflection or
// something to make this a for loop. // something to make this a for loop.
events, err := o.clientset.Core().Events(namespace).List(metav1.ListOptions{}) events, err := o.Clientset.Core().Events(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -181,7 +183,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
return err return err
} }
rcs, err := o.clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{}) rcs, err := o.Clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -189,7 +191,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
return err return err
} }
svcs, err := o.clientset.Core().Services(namespace).List(metav1.ListOptions{}) svcs, err := o.Clientset.Core().Services(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -197,7 +199,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
return err return err
} }
sets, err := o.clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{}) sets, err := o.Clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -205,7 +207,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
return err return err
} }
deps, err := o.clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{}) deps, err := o.Clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -213,7 +215,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
return err return err
} }
rps, err := o.clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{}) rps, err := o.Clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -221,7 +223,7 @@ func (o *ClusterInfoDumpOptions) Run() error {
return err return err
} }
pods, err := o.clientset.Core().Pods(namespace).List(metav1.ListOptions{}) pods, err := o.Clientset.Core().Pods(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -234,7 +236,7 @@ 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))) 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))) 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(pod, &api.PodLogOptions{Container: container.Name}, timeout) request, err := o.LogsForObject(o.RESTClientGetter, pod, &api.PodLogOptions{Container: container.Name}, timeout)
if err != nil { if err != nil {
// Print error and return. // Print error and return.
writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error()))) writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))

View File

@ -399,7 +399,7 @@ func NewDownloader(f cmdutil.Factory) (*Downloader, error) {
var err error var err error
var d Downloader var d Downloader
d.mapper, err = f.RESTMapper() d.mapper, err = f.ToRESTMapper()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -26,16 +26,15 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
"k8s.io/kubernetes/pkg/kubectl/util" "k8s.io/kubernetes/pkg/kubectl/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
) )
@ -82,13 +81,10 @@ type LogsOptions struct {
AllContainers bool AllContainers bool
Options runtime.Object Options runtime.Object
Mapper meta.RESTMapper Object runtime.Object
Typer runtime.ObjectTyper GetPodTimeout time.Duration
Decoder runtime.Decoder RESTClientGetter genericclioptions.RESTClientGetter
LogsForObject polymorphichelpers.LogsForObjectFunc
Object runtime.Object
GetPodTimeout time.Duration
LogsForObject func(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error)
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -196,7 +192,8 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str
return err return err
} }
o.Options = logOptions o.Options = logOptions
o.LogsForObject = f.LogsForObject o.RESTClientGetter = f
o.LogsForObject = polymorphichelpers.LogsForObjectFn
if len(selector) != 0 { if len(selector) != 0 {
if logOptions.Follow { if logOptions.Follow {
@ -286,7 +283,7 @@ func (o LogsOptions) getPodLogs(pod *api.Pod) error {
} }
func (o LogsOptions) getLogs(obj runtime.Object) error { func (o LogsOptions) getLogs(obj runtime.Object) error {
req, err := o.LogsForObject(obj, o.Options, o.GetPodTimeout) req, err := o.LogsForObject(o.RESTClientGetter, obj, o.Options, o.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -18,19 +18,27 @@ package cmd
import ( import (
"bytes" "bytes"
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
) )
@ -74,6 +82,16 @@ func TestLog(t *testing.T) {
} }
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig() 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() streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdLogs(tf, streams) cmd := NewCmdLogs(tf, streams)
@ -221,3 +239,20 @@ func TestLogComplete(t *testing.T) {
} }
} }
} }
type logTestMock struct {
client internalclientset.Interface
}
func (m logTestMock) logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*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
default:
return nil, fmt.Errorf("cannot get the logs from %T", object)
}
}

View File

@ -41,6 +41,7 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/util/interrupt" "k8s.io/kubernetes/pkg/util/interrupt"
@ -525,13 +526,13 @@ func handleAttachPod(f cmdutil.Factory, podClient coreclient.PodsGetter, ns, nam
} }
// logOpts logs output from opts to the pods log. // logOpts logs output from opts to the pods log.
func logOpts(f cmdutil.Factory, pod *api.Pod, opts *AttachOptions) error { func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *api.Pod, opts *AttachOptions) error {
ctrName, err := opts.GetContainerName(pod) ctrName, err := opts.GetContainerName(pod)
if err != nil { if err != nil {
return err return err
} }
req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout) req, err := polymorphichelpers.LogsForObjectFn(restClientGetter, pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -10,7 +10,6 @@ go_library(
visibility = ["//build/visible_to:pkg_kubectl_cmd_testing_CONSUMERS"], visibility = ["//build/visible_to:pkg_kubectl_cmd_testing_CONSUMERS"],
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",

View File

@ -44,7 +44,6 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -446,24 +445,6 @@ func testRESTMapper() meta.RESTMapper {
return expander return expander
} }
func (f *TestFactory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
c, err := f.ClientSet()
if err != nil {
panic(err)
}
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 c.Core().Pods(f.Namespace).GetLogs(t.Name, opts), nil
default:
return nil, fmt.Errorf("cannot get the logs from %T", object)
}
}
func (f *TestFactory) ScaleClient() (scaleclient.ScalesGetter, error) { func (f *TestFactory) ScaleClient() (scaleclient.ScalesGetter, error) {
return f.ScaleGetter, nil return f.ScaleGetter, nil
} }

View File

@ -22,13 +22,14 @@ go_library(
"//pkg/apis/core/v1:go_default_library", "//pkg/apis/core/v1:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library", "//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/cmd/util/openapi/validation:go_default_library", "//pkg/kubectl/cmd/util/openapi/validation:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/polymorphichelpers:go_default_library",
"//pkg/kubectl/validation:go_default_library", "//pkg/kubectl/validation:go_default_library",
"//pkg/printers:go_default_library", "//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library", "//pkg/printers/internalversion:go_default_library",
@ -49,13 +50,11 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library",
@ -69,7 +68,6 @@ go_library(
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = [ srcs = [
"factory_object_mapping_test.go",
"factory_test.go", "factory_test.go",
"helpers_test.go", "helpers_test.go",
], ],
@ -77,28 +75,17 @@ go_test(
deps = [ deps = [
"//pkg/api/testapi:go_default_library", "//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library", "//pkg/api/testing:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//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/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
) )

View File

@ -18,7 +18,6 @@ package util
import ( import (
"fmt" "fmt"
"sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -27,21 +26,17 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
scaleclient "k8s.io/client-go/scale" scaleclient "k8s.io/client-go/scale"
api "k8s.io/kubernetes/pkg/apis/core" 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/client/clientset_generated/internalclientset"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/validation" "k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
@ -67,14 +62,7 @@ type Factory interface {
// Generally provides discovery, negotiation, and no-dep calls. // Generally provides discovery, negotiation, and no-dep calls.
// TODO The polymorphic calls probably deserve their own interface. // TODO The polymorphic calls probably deserve their own interface.
type ClientAccessFactory interface { type ClientAccessFactory interface {
// Returns a client.Config for accessing the Kubernetes server. genericclioptions.RESTClientGetter
ToRESTConfig() (*restclient.Config, error)
// Returns interfaces for dealing with arbitrary runtime.Objects.
ToRESTMapper() (meta.RESTMapper, error)
// Returns a discovery client
ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
// Returns kubeconfig loader
ToRawKubeConfigLoader() clientcmd.ClientConfig
// ClientSet gives you back an internal, generated clientset // ClientSet gives you back an internal, generated clientset
ClientSet() (internalclientset.Interface, error) ClientSet() (internalclientset.Interface, error)
@ -151,8 +139,6 @@ type ObjectMappingFactory interface {
// Returns a Describer for displaying the specified RESTMapping type or an error. // Returns a Describer for displaying the specified RESTMapping type or an error.
Describer(mapping *meta.RESTMapping) (printers.Describer, error) Describer(mapping *meta.RESTMapping) (printers.Describer, error)
// LogsForObject returns a request for the logs associated with the provided object
LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error)
// Returns a HistoryViewer for viewing change history // Returns a HistoryViewer for viewing change history
HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error)
// Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error // Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error
@ -191,7 +177,7 @@ type factory struct {
// NewFactory creates a factory with the default Kubernetes resources defined // NewFactory creates a factory with the default Kubernetes resources defined
// Receives a clientGetter capable of providing a discovery client and a REST client configuration. // Receives a clientGetter capable of providing a discovery client and a REST client configuration.
func NewFactory(clientGetter RESTClientGetter) Factory { func NewFactory(clientGetter genericclioptions.RESTClientGetter) Factory {
clientAccessFactory := NewClientAccessFactory(clientGetter) clientAccessFactory := NewClientAccessFactory(clientGetter)
objectMappingFactory := NewObjectMappingFactory(clientAccessFactory) objectMappingFactory := NewObjectMappingFactory(clientAccessFactory)
builderFactory := NewBuilderFactory(clientAccessFactory, objectMappingFactory) builderFactory := NewBuilderFactory(clientAccessFactory, objectMappingFactory)
@ -203,51 +189,6 @@ func NewFactory(clientGetter RESTClientGetter) Factory {
} }
} }
// 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) {
options := metav1.ListOptions{LabelSelector: selector}
podList, err := client.Pods(namespace).List(options)
if err != nil {
return nil, 0, err
}
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)
}
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
}
// Watch until we observe a pod
options.ResourceVersion = podList.ResourceVersion
w, err := client.Pods(namespace).Watch(options)
if err != nil {
return nil, 0, err
}
defer w.Stop()
condition := func(event watch.Event) (bool, error) {
return event.Type == watch.Added || event.Type == watch.Modified, nil
}
event, err := watch.Until(timeout, w, condition)
if err != nil {
return nil, 0, err
}
pod, ok := event.Object.(*api.Pod)
if !ok {
return nil, 0, fmt.Errorf("%#v is not a pod event", event)
}
return pod, 1, nil
}
func makePortsString(ports []api.ServicePort, useNodePort bool) string { func makePortsString(ports []api.ServicePort, useNodePort bool) string {
pieces := make([]string, len(ports)) pieces := make([]string, len(ports))
for ix := range ports { for ix := range ports {

View File

@ -55,21 +55,15 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
) )
type RESTClientGetter interface {
ToRESTConfig() (*restclient.Config, error)
ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
ToRESTMapper() (meta.RESTMapper, error)
ToRawKubeConfigLoader() clientcmd.ClientConfig
}
type ring0Factory struct { type ring0Factory struct {
clientGetter RESTClientGetter clientGetter genericclioptions.RESTClientGetter
} }
func NewClientAccessFactory(clientGetter RESTClientGetter) ClientAccessFactory { func NewClientAccessFactory(clientGetter genericclioptions.RESTClientGetter) ClientAccessFactory {
if clientGetter == nil { if clientGetter == nil {
panic("attempt to instantiate client_access_factory with nil clientGetter") panic("attempt to instantiate client_access_factory with nil clientGetter")
} }

View File

@ -19,28 +19,18 @@ limitations under the License.
package util package util
import ( import (
"errors"
"fmt" "fmt"
"os"
"reflect" "reflect"
"sort" "sort"
"sync" "sync"
"time" "time"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1" apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
@ -50,6 +40,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
openapivalidation "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation" openapivalidation "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
"k8s.io/kubernetes/pkg/kubectl/validation" "k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
@ -152,186 +143,6 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES
return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil
} }
func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
clientset, err := f.clientAccessFactory.ClientSet()
if err != nil {
return nil, err
}
opts, ok := options.(*api.PodLogOptions)
if !ok {
return nil, errors.New("provided options object is not a PodLogOptions")
}
switch t := object.(type) {
case *api.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
case *corev1.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
}
namespace, selector, err := selectorsForObject(object)
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)
if err != nil {
return nil, err
}
if numPods > 1 {
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
}
return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
}
func selectorsForObject(object runtime.Object) (namespace string, selector labels.Selector, err error) {
switch t := object.(type) {
case *extensions.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.ReplicationController:
namespace = t.Namespace
selector = labels.SelectorFromSet(t.Spec.Selector)
case *corev1.ReplicationController:
namespace = t.Namespace
selector = labels.SelectorFromSet(t.Spec.Selector)
case *apps.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta1.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensions.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensions.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *batch.Job:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *batchv1.Job:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.Service:
namespace = t.Namespace
if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
return "", nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
case *corev1.Service:
namespace = t.Namespace
if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
return "", nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
default:
return "", nil, fmt.Errorf("selector for %T not implemented", object)
}
return namespace, selector, nil
}
func (f *ring1Factory) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) { func (f *ring1Factory) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
external, err := f.clientAccessFactory.KubernetesClientSet() external, err := f.clientAccessFactory.KubernetesClientSet()
if err != nil { if err != nil {
@ -394,12 +205,12 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout tim
} }
namespace, selector, err := selectorsForObject(object) namespace, selector, err := polymorphichelpers.SelectorsForObject(object)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot attach to %T: %v", object, err) return nil, fmt.Errorf("cannot attach to %T: %v", object, err)
} }
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
pod, _, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy) pod, _, err := polymorphichelpers.GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy)
return pod, err return pod, err
} }

View File

@ -17,25 +17,14 @@ limitations under the License.
package util package util
import ( import (
"fmt"
"sort"
"strings" "strings"
"testing" "testing"
"time"
"k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch"
testcore "k8s.io/client-go/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
) )
@ -187,200 +176,6 @@ func TestCanBeExposed(t *testing.T) {
} }
} }
func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *api.PodList {
pods := []api.Pod{}
for i := 0; i < count; i++ {
newPod := api.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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
}
pods = append(pods, newPod)
}
if isUnready > -1 && isUnready < count {
pods[isUnready].Status.Conditions[0].Status = api.ConditionFalse
}
if isUnhealthy > -1 && isUnhealthy < count {
pods[isUnhealthy].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 5}}
}
return &api.PodList{
Items: pods,
}
}
func TestGetFirstPod(t *testing.T) {
labelSet := map[string]string{"test": "selector"}
tests := []struct {
name string
podList *api.PodList
watching []watch.Event
sortBy func([]*v1.Pod) sort.Interface
expected *api.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{
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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
expectedNum: 2,
},
{
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{
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: api.ConditionTrue,
Type: api.PodReady,
},
},
ContainerStatuses: []api.ContainerStatus{{RestartCount: 5}},
},
},
expectedNum: 2,
},
{
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{
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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
expectedNum: 2,
},
{
name: "kubectl attach - wait for ready pod",
podList: newPodList(1, 1, -1, labelSet),
watching: []watch.Event{
{
Type: watch.Modified,
Object: &api.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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
},
},
sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
expected: &api.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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
expectedNum: 1,
},
}
for i := range tests {
test := tests[i]
fake := fake.NewSimpleClientset(test.podList)
if len(test.watching) > 0 {
watcher := watch.NewFake()
for _, event := range test.watching {
switch event.Type {
case watch.Added:
go watcher.Add(event.Object)
case watch.Modified:
go watcher.Modify(event.Object)
}
}
fake.PrependWatchReactor("pods", testcore.DefaultWatchReactor(watcher, nil))
}
selector := labels.Set(labelSet).AsSelector()
pod, numPods, err := GetFirstPod(fake.Core(), metav1.NamespaceDefault, selector.String(), 1*time.Minute, test.sortBy)
pod.Spec.SecurityContext = nil
if !test.expectedErr && err != nil {
t.Errorf("%s: unexpected error: %v", test.name, err)
continue
}
if test.expectedErr && err == nil {
t.Errorf("%s: expected an error", test.name)
continue
}
if test.expectedNum != numPods {
t.Errorf("%s: expected %d pods, got %d", test.name, test.expectedNum, numPods)
continue
}
if !apiequality.Semantic.DeepEqual(test.expected, pod) {
t.Errorf("%s:\nexpected pod:\n%#v\ngot:\n%#v\n\n", test.name, test.expected, pod)
}
}
}
func TestMakePortsString(t *testing.T) { func TestMakePortsString(t *testing.T) {
tests := []struct { tests := []struct {
ports []api.ServicePort ports []api.ServicePort

View File

@ -28,6 +28,7 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/version" "k8s.io/kubernetes/pkg/version"
) )
@ -37,14 +38,14 @@ const (
// MatchVersionFlags is for setting the "match server version" function. // MatchVersionFlags is for setting the "match server version" function.
type MatchVersionFlags struct { type MatchVersionFlags struct {
Delegate RESTClientGetter Delegate genericclioptions.RESTClientGetter
RequireMatchedServerVersion bool RequireMatchedServerVersion bool
checkServerVersion sync.Once checkServerVersion sync.Once
matchesServerVersionErr error matchesServerVersionErr error
} }
var _ RESTClientGetter = &MatchVersionFlags{} var _ genericclioptions.RESTClientGetter = &MatchVersionFlags{}
func (f *MatchVersionFlags) checkMatchingServerVersion() error { func (f *MatchVersionFlags) checkMatchingServerVersion() error {
f.checkServerVersion.Do(func() { f.checkServerVersion.Do(func() {
@ -102,7 +103,7 @@ func (f *MatchVersionFlags) AddFlags(flags *pflag.FlagSet) {
flags.BoolVar(&f.RequireMatchedServerVersion, flagMatchBinaryVersion, f.RequireMatchedServerVersion, "Require server version to match client version") flags.BoolVar(&f.RequireMatchedServerVersion, flagMatchBinaryVersion, f.RequireMatchedServerVersion, "Require server version to match client version")
} }
func NewMatchVersionFlags(delegate RESTClientGetter) *MatchVersionFlags { func NewMatchVersionFlags(delegate genericclioptions.RESTClientGetter) *MatchVersionFlags {
return &MatchVersionFlags{ return &MatchVersionFlags{
Delegate: delegate, Delegate: delegate,
} }

View File

@ -54,6 +54,22 @@ const (
var defaultCacheDir = filepath.Join(homedir.HomeDir(), ".kube", "http-cache") var defaultCacheDir = filepath.Join(homedir.HomeDir(), ".kube", "http-cache")
// RESTClientGetter is an interface that the ConfigFlags describe to provide an easier way to mock for commands
// and eliminate the direct coupling to a struct type. Users may wish to duplicate this type in their own packages
// as per the golang type overlapping.
type RESTClientGetter interface {
// ToRESTConfig returns restconfig
ToRESTConfig() (*rest.Config, error)
// ToDiscoveryClient returns discovery client
ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
// ToRESTMapper returns a restmapper
ToRESTMapper() (meta.RESTMapper, error)
// ToRawKubeConfigLoader return kubeconfig loader as-is
ToRawKubeConfigLoader() clientcmd.ClientConfig
}
var _ RESTClientGetter = &ConfigFlags{}
// ConfigFlags composes the set of values necessary // ConfigFlags composes the set of values necessary
// for obtaining a REST client config // for obtaining a REST client config
type ConfigFlags struct { type ConfigFlags struct {

View File

@ -0,0 +1,74 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"helpers.go",
"interface.go",
"logsforobject.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/batch:go_default_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/genericclioptions:go_default_library",
"//vendor/k8s.io/api/apps/v1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"helpers_test.go",
"logsforobject_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/apis/apps:go_default_library",
"//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",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,235 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package polymorphichelpers
import (
"fmt"
"sort"
"time"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"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) {
options := metav1.ListOptions{LabelSelector: selector}
podList, err := client.Pods(namespace).List(options)
if err != nil {
return nil, 0, err
}
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)
}
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
}
// Watch until we observe a pod
options.ResourceVersion = podList.ResourceVersion
w, err := client.Pods(namespace).Watch(options)
if err != nil {
return nil, 0, err
}
defer w.Stop()
condition := func(event watch.Event) (bool, error) {
return event.Type == watch.Added || event.Type == watch.Modified, nil
}
event, err := watch.Until(timeout, w, condition)
if err != nil {
return nil, 0, err
}
pod, ok := event.Object.(*api.Pod)
if !ok {
return nil, 0, fmt.Errorf("%#v is not a pod event", event)
}
return pod, 1, nil
}
// SelectorsForObject returns the pod label selector for a given object
func SelectorsForObject(object runtime.Object) (namespace string, selector labels.Selector, err error) {
switch t := object.(type) {
case *extensions.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.ReplicationController:
namespace = t.Namespace
selector = labels.SelectorFromSet(t.Spec.Selector)
case *corev1.ReplicationController:
namespace = t.Namespace
selector = labels.SelectorFromSet(t.Spec.Selector)
case *apps.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta1.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensions.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensions.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *batch.Job:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *batchv1.Job:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.Service:
namespace = t.Namespace
if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
return "", nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
case *corev1.Service:
namespace = t.Namespace
if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
return "", nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
default:
return "", nil, fmt.Errorf("selector for %T not implemented", object)
}
return namespace, selector, nil
}

View File

@ -0,0 +1,229 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package polymorphichelpers
import (
"fmt"
"sort"
"testing"
"time"
"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"
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"
)
func TestGetFirstPod(t *testing.T) {
labelSet := map[string]string{"test": "selector"}
tests := []struct {
name string
podList *api.PodList
watching []watch.Event
sortBy func([]*v1.Pod) sort.Interface
expected *api.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{
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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
expectedNum: 2,
},
{
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{
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: api.ConditionTrue,
Type: api.PodReady,
},
},
ContainerStatuses: []api.ContainerStatus{{RestartCount: 5}},
},
},
expectedNum: 2,
},
{
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{
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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
expectedNum: 2,
},
{
name: "kubectl attach - wait for ready pod",
podList: newPodList(1, 1, -1, labelSet),
watching: []watch.Event{
{
Type: watch.Modified,
Object: &api.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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
},
},
sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
expected: &api.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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
},
expectedNum: 1,
},
}
for i := range tests {
test := tests[i]
fake := fake.NewSimpleClientset(test.podList)
if len(test.watching) > 0 {
watcher := watch.NewFake()
for _, event := range test.watching {
switch event.Type {
case watch.Added:
go watcher.Add(event.Object)
case watch.Modified:
go watcher.Modify(event.Object)
}
}
fake.PrependWatchReactor("pods", testcore.DefaultWatchReactor(watcher, nil))
}
selector := labels.Set(labelSet).AsSelector()
pod, numPods, err := GetFirstPod(fake.Core(), metav1.NamespaceDefault, selector.String(), 1*time.Minute, test.sortBy)
pod.Spec.SecurityContext = nil
if !test.expectedErr && err != nil {
t.Errorf("%s: unexpected error: %v", test.name, err)
continue
}
if test.expectedErr && err == nil {
t.Errorf("%s: expected an error", test.name)
continue
}
if test.expectedNum != numPods {
t.Errorf("%s: expected %d pods, got %d", test.name, test.expectedNum, numPods)
continue
}
if !apiequality.Semantic.DeepEqual(test.expected, pod) {
t.Errorf("%s:\nexpected pod:\n%#v\ngot:\n%#v\n\n", test.name, test.expected, pod)
}
}
}
func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *api.PodList {
pods := []api.Pod{}
for i := 0; i < count; i++ {
newPod := api.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: api.ConditionTrue,
Type: api.PodReady,
},
},
},
}
pods = append(pods, newPod)
}
if isUnready > -1 && isUnready < count {
pods[isUnready].Status.Conditions[0].Status = api.ConditionFalse
}
if isUnhealthy > -1 && isUnhealthy < count {
pods[isUnhealthy].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 5}}
}
return &api.PodList{
Items: pods,
}
}

View File

@ -0,0 +1,31 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package polymorphichelpers
import (
"time"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// 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)
// LogsForObjectFn gives a way to easily override the function for unit testing if needed.
var LogsForObjectFn LogsForObjectFunc = logsForObject

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package polymorphichelpers
import (
"errors"
"fmt"
"os"
"sort"
"time"
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"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"
)
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) {
clientConfig, err := restClientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
clientset, err := internalclientset.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
return logsForObjectWithClient(clientset, object, options, timeout)
}
// this is split for easy test-ability
func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration) (*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.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
case *corev1.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
}
namespace, selector, err := SelectorsForObject(object)
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)
if err != nil {
return nil, err
}
if numPods > 1 {
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
}
return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package polymorphichelpers
import ( import (
"reflect" "reflect"
@ -30,26 +30,9 @@ import (
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
) )
type fakeClientAccessFactory struct {
ClientAccessFactory
fakeClientset *fake.Clientset
}
func (f *fakeClientAccessFactory) ClientSet() (internalclientset.Interface, error) {
return f.fakeClientset, nil
}
func newFakeClientAccessFactory(objs []runtime.Object) *fakeClientAccessFactory {
return &fakeClientAccessFactory{
fakeClientset: fake.NewSimpleClientset(objs...),
}
}
var ( var (
podsResource = schema.GroupVersionResource{Resource: "pods"} podsResource = schema.GroupVersionResource{Resource: "pods"}
podsKind = schema.GroupVersionKind{Kind: "Pod"} podsKind = schema.GroupVersionKind{Kind: "Pod"}
@ -146,20 +129,19 @@ func TestLogsForObject(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
caf := newFakeClientAccessFactory(test.pods) fakeClientset := fake.NewSimpleClientset(test.pods...)
omf := NewObjectMappingFactory(caf) _, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second)
_, err := omf.LogsForObject(test.obj, test.opts, 20*time.Second)
if err != nil { if err != nil {
t.Errorf("%s: unexpected error: %v", test.name, err) t.Errorf("%s: unexpected error: %v", test.name, err)
continue continue
} }
for i := range test.actions { for i := range test.actions {
if len(caf.fakeClientset.Actions()) < i { if len(fakeClientset.Actions()) < i {
t.Errorf("%s: action %d does not exists in actual actions: %#v", t.Errorf("%s: action %d does not exists in actual actions: %#v",
test.name, i, caf.fakeClientset.Actions()) test.name, i, fakeClientset.Actions())
continue continue
} }
got := caf.fakeClientset.Actions()[i] got := fakeClientset.Actions()[i]
want := test.actions[i] want := test.actions[i]
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
t.Errorf("%s: unexpected action: %s", test.name, diff.ObjectDiff(got, want)) t.Errorf("%s: unexpected action: %s", test.name, diff.ObjectDiff(got, want))

View File

@ -15,7 +15,7 @@ go_library(
importpath = "k8s.io/kubernetes/test/e2e/kubectl", importpath = "k8s.io/kubernetes/test/e2e/kubectl",
deps = [ deps = [
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/polymorphichelpers:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//test/e2e/generated:go_default_library", "//test/e2e/generated:go_default_library",
"//test/e2e/scheduling:go_default_library", "//test/e2e/scheduling:go_default_library",

View File

@ -58,7 +58,6 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/generated" "k8s.io/kubernetes/test/e2e/generated"
"k8s.io/kubernetes/test/e2e/scheduling" "k8s.io/kubernetes/test/e2e/scheduling"
@ -67,6 +66,7 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
imageutils "k8s.io/kubernetes/test/utils/image" imageutils "k8s.io/kubernetes/test/utils/image"
) )
@ -529,7 +529,7 @@ var _ = SIGDescribe("Kubectl client", func() {
ExecOrDie() ExecOrDie()
Expect(runOutput).ToNot(ContainSubstring("stdin closed")) Expect(runOutput).ToNot(ContainSubstring("stdin closed"))
g := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } g := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
runTestPod, _, err := util.GetFirstPod(f.InternalClientset.Core(), ns, "run=run-test-3", 1*time.Minute, g) runTestPod, _, err := polymorphichelpers.GetFirstPod(f.InternalClientset.Core(), ns, "run=run-test-3", 1*time.Minute, g)
if err != nil { if err != nil {
os.Exit(1) os.Exit(1)
} }