Be able to specify the timeout to wait for pod for kubectl logs/attach

This commit is contained in:
shiywang 2017-02-22 00:19:13 +08:00
parent b0ce93f9be
commit 52e4be2578
11 changed files with 97 additions and 29 deletions

View File

@ -516,6 +516,7 @@ pod-infra-container-image
pod-manifest-path pod-manifest-path
pod-network-cidr pod-network-cidr
pod-running pod-running
pod-running-timeout
pods-per-core pods-per-core
policy-config-file policy-config-file
poll-interval poll-interval

View File

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
"time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -55,6 +56,11 @@ var (
`) `)
) )
const (
defaultPodAttachTimeout = 60 * time.Second
defaultPodLogsTimeout = 20 * time.Second
)
func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
options := &AttachOptions{ options := &AttachOptions{
StreamOptions: StreamOptions{ StreamOptions: StreamOptions{
@ -76,7 +82,7 @@ func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer)
cmdutil.CheckErr(options.Run()) cmdutil.CheckErr(options.Run())
}, },
} }
// TODO support UID cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
cmd.Flags().StringVarP(&options.ContainerName, "container", "c", "", "Container name. If omitted, the first container in the pod will be chosen") cmd.Flags().StringVarP(&options.ContainerName, "container", "c", "", "Container name. If omitted, the first container in the pod will be chosen")
cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", false, "Pass stdin to the container") cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", false, "Pass stdin to the container")
cmd.Flags().BoolVarP(&options.TTY, "tty", "t", false, "Stdin is a TTY") cmd.Flags().BoolVarP(&options.TTY, "tty", "t", false, "Stdin is a TTY")
@ -114,9 +120,10 @@ type AttachOptions struct {
Pod *api.Pod Pod *api.Pod
Attach RemoteAttach Attach RemoteAttach
PodClient coreclient.PodsGetter PodClient coreclient.PodsGetter
Config *restclient.Config GetPodTimeout time.Duration
Config *restclient.Config
} }
// Complete verifies command line arguments and loads data from the command environment // Complete verifies command line arguments and loads data from the command environment
@ -133,6 +140,11 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [
return err return err
} }
p.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil {
return cmdutil.UsageError(cmd, err.Error())
}
mapper, typer := f.Object() mapper, typer := f.Object()
builder := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). builder := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(namespace).DefaultNamespace() NamespaceParam(namespace).DefaultNamespace()
@ -149,7 +161,7 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [
return err return err
} }
attachablePod, err := f.AttachablePodForObject(obj) attachablePod, err := f.AttachablePodForObject(obj, p.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -24,6 +24,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"testing" "testing"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -34,6 +35,7 @@ import (
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/term" "k8s.io/kubernetes/pkg/util/term"
) )
@ -57,18 +59,21 @@ func TestPodAndContainerAttach(t *testing.T) {
expectError bool expectError bool
expectedPod string expectedPod string
expectedContainer string expectedContainer string
timeout time.Duration
obj runtime.Object obj runtime.Object
}{ }{
{ {
p: &AttachOptions{}, p: &AttachOptions{},
expectError: true, expectError: true,
name: "empty", name: "empty",
timeout: 1,
}, },
{ {
p: &AttachOptions{}, p: &AttachOptions{},
args: []string{"one", "two", "three"}, args: []string{"one", "two", "three"},
expectError: true, expectError: true,
name: "too many args", name: "too many args",
timeout: 2,
}, },
{ {
p: &AttachOptions{}, p: &AttachOptions{},
@ -76,6 +81,7 @@ func TestPodAndContainerAttach(t *testing.T) {
expectedPod: "foo", expectedPod: "foo",
name: "no container, no flags", name: "no container, no flags",
obj: attachPod(), obj: attachPod(),
timeout: defaultPodLogsTimeout,
}, },
{ {
p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}}, p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}},
@ -84,6 +90,7 @@ func TestPodAndContainerAttach(t *testing.T) {
expectedContainer: "bar", expectedContainer: "bar",
name: "container in flag", name: "container in flag",
obj: attachPod(), obj: attachPod(),
timeout: 10000000,
}, },
{ {
p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "initfoo"}}, p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "initfoo"}},
@ -92,6 +99,7 @@ func TestPodAndContainerAttach(t *testing.T) {
expectedContainer: "initfoo", expectedContainer: "initfoo",
name: "init container in flag", name: "init container in flag",
obj: attachPod(), obj: attachPod(),
timeout: 30,
}, },
{ {
p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}}, p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}},
@ -99,6 +107,7 @@ func TestPodAndContainerAttach(t *testing.T) {
expectError: true, expectError: true,
name: "non-existing container in flag", name: "non-existing container in flag",
obj: attachPod(), obj: attachPod(),
timeout: 10,
}, },
{ {
p: &AttachOptions{}, p: &AttachOptions{},
@ -106,6 +115,7 @@ func TestPodAndContainerAttach(t *testing.T) {
expectedPod: "foo", expectedPod: "foo",
name: "no container, no flags, pods and name", name: "no container, no flags, pods and name",
obj: attachPod(), obj: attachPod(),
timeout: 10000,
}, },
{ {
p: &AttachOptions{}, p: &AttachOptions{},
@ -113,6 +123,16 @@ func TestPodAndContainerAttach(t *testing.T) {
expectedPod: "foo", expectedPod: "foo",
name: "no container, no flags, pod/name", name: "no container, no flags, pod/name",
obj: attachPod(), obj: attachPod(),
timeout: 1,
},
{
p: &AttachOptions{},
args: []string{"pod/foo"},
expectedPod: "foo",
name: "invalid get pod timeout value",
obj: attachPod(),
expectError: true,
timeout: 0,
}, },
} }
@ -133,6 +153,8 @@ func TestPodAndContainerAttach(t *testing.T) {
cmd := &cobra.Command{} cmd := &cobra.Command{}
options := test.p options := test.p
cmdutil.AddPodRunningTimeoutFlag(cmd, test.timeout)
err := options.Complete(f, cmd, test.args) err := options.Complete(f, cmd, test.args)
if test.expectError && err == nil { if test.expectError && err == nil {
t.Errorf("unexpected non-error (%s)", test.name) t.Errorf("unexpected non-error (%s)", test.name)
@ -227,9 +249,11 @@ func TestAttach(t *testing.T) {
Out: bufOut, Out: bufOut,
Err: bufErr, Err: bufErr,
}, },
Attach: remoteAttach, Attach: remoteAttach,
GetPodTimeout: 1000,
} }
cmd := &cobra.Command{} cmd := &cobra.Command{}
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
if err := params.Complete(f, cmd, []string{"foo"}); err != nil { if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -310,9 +334,11 @@ func TestAttachWarnings(t *testing.T) {
Stdin: test.stdin, Stdin: test.stdin,
TTY: test.tty, TTY: test.tty,
}, },
Attach: ex, Attach: ex,
GetPodTimeout: 1000,
} }
cmd := &cobra.Command{} cmd := &cobra.Command{}
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
if err := params.Complete(f, cmd, []string{"foo"}); err != nil { if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -46,6 +46,7 @@ func NewCmdClusterInfoDump(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd.Flags().String("output-directory", "", i18n.T("Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory")) cmd.Flags().String("output-directory", "", i18n.T("Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
cmd.Flags().StringSlice("namespaces", []string{}, "A comma separated list of namespaces to dump.") cmd.Flags().StringSlice("namespaces", []string{}, "A comma separated list of namespaces to dump.")
cmd.Flags().Bool("all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.") cmd.Flags().Bool("all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.")
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
return cmd return cmd
} }
@ -88,6 +89,11 @@ func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename str
} }
func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error { func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
timeout, err := cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil {
return cmdutil.UsageError(cmd, err.Error())
}
clientset, err := f.ClientSet() clientset, err := f.ClientSet()
if err != nil { if err != nil {
return err return err
@ -190,7 +196,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, args []string, out i
pod := &pods.Items[ix] pod := &pods.Items[ix]
writer := setupOutputWriter(cmd, out, path.Join(namespace, pod.Name, "logs.txt")) writer := setupOutputWriter(cmd, out, path.Join(namespace, pod.Name, "logs.txt"))
writer.Write([]byte(fmt.Sprintf("==== START logs for %s/%s ====\n", pod.Namespace, pod.Name))) writer.Write([]byte(fmt.Sprintf("==== START logs for %s/%s ====\n", pod.Namespace, pod.Name)))
request, err := f.LogsForObject(pod, &api.PodLogOptions{}) request, err := f.LogsForObject(pod, &api.PodLogOptions{}, timeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -80,7 +80,8 @@ type LogsOptions struct {
Decoder runtime.Decoder Decoder runtime.Decoder
Object runtime.Object Object runtime.Object
LogsForObject func(object, options runtime.Object) (*restclient.Request, error) GetPodTimeout time.Duration
LogsForObject func(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error)
Out io.Writer Out io.Writer
} }
@ -113,10 +114,10 @@ func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command {
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().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().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().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().Bool("interactive", false, "If true, prompt the user for input when required.")
cmd.Flags().MarkDeprecated("interactive", "This flag is no longer respected and there is no replacement.") cmd.Flags().MarkDeprecated("interactive", "This flag is no longer respected and there is no replacement.")
cmdutil.AddInclude3rdPartyFlags(cmd) cmdutil.AddInclude3rdPartyFlags(cmd)
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.")
return cmd return cmd
} }
@ -173,6 +174,10 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm
sec := int64(math.Ceil(float64(sinceSeconds) / float64(time.Second))) sec := int64(math.Ceil(float64(sinceSeconds) / float64(time.Second)))
logOptions.SinceSeconds = &sec logOptions.SinceSeconds = &sec
} }
o.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil {
return err
}
o.Options = logOptions o.Options = logOptions
o.LogsForObject = f.LogsForObject o.LogsForObject = f.LogsForObject
o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping) o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping)
@ -243,7 +248,7 @@ func (o LogsOptions) RunLogs() error {
} }
func (o LogsOptions) getLogs(obj runtime.Object) error { func (o LogsOptions) getLogs(obj runtime.Object) error {
req, err := o.LogsForObject(obj, o.Options) req, err := o.LogsForObject(obj, o.Options, o.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -21,9 +21,8 @@ import (
"io" "io"
"os" "os"
"github.com/spf13/cobra"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -108,6 +107,7 @@ func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *co
cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddRecordFlag(cmd) cmdutil.AddRecordFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd) cmdutil.AddInclude3rdPartyFlags(cmd)
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
return cmd return cmd
} }
@ -149,6 +149,11 @@ func Run(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobr
return cmdutil.UsageError(cmd, "NAME is required for run") return cmdutil.UsageError(cmd, "NAME is required for run")
} }
timeout, err := cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil {
return cmdutil.UsageError(cmd, err.Error())
}
// validate image name // validate image name
imageName := cmdutil.GetFlagString(cmd, "image") imageName := cmdutil.GetFlagString(cmd, "image")
validImageRef := reference.ReferenceRegexp.MatchString(imageName) validImageRef := reference.ReferenceRegexp.MatchString(imageName)
@ -287,8 +292,8 @@ func Run(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobr
TTY: tty, TTY: tty,
Quiet: quiet, Quiet: quiet,
}, },
GetPodTimeout: timeout,
CommandName: cmd.Parent().CommandPath() + " attach", CommandName: cmd.Parent().CommandPath() + " attach",
Attach: &DefaultRemoteAttach{}, Attach: &DefaultRemoteAttach{},
} }
@ -304,7 +309,7 @@ func Run(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobr
} }
opts.PodClient = clientset.Core() opts.PodClient = clientset.Core()
attachablePod, err := f.AttachablePodForObject(obj) attachablePod, err := f.AttachablePodForObject(obj, opts.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }
@ -446,7 +451,7 @@ func handleAttachPod(f cmdutil.Factory, podClient coreclient.PodsGetter, ns, nam
return err return err
} }
if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed { if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed {
req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: ctrName}) req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }
@ -467,7 +472,7 @@ func handleAttachPod(f cmdutil.Factory, podClient coreclient.PodsGetter, ns, nam
stderr := opts.Err stderr := opts.Err
if err := opts.Run(); err != nil { if err := opts.Run(); err != nil {
fmt.Fprintf(stderr, "Error attaching, falling back to logs: %v\n", err) fmt.Fprintf(stderr, "Error attaching, falling back to logs: %v\n", err)
req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: ctrName}) req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: ctrName}, opts.GetPodTimeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -376,7 +376,7 @@ func (f *FakeFactory) LabelsForObject(runtime.Object) (map[string]string, error)
return nil, nil return nil, nil
} }
func (f *FakeFactory) LogsForObject(object, options runtime.Object) (*restclient.Request, error) { func (f *FakeFactory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
return nil, nil return nil, nil
} }
@ -423,7 +423,7 @@ func (f *FakeFactory) CanBeAutoscaled(schema.GroupKind) error {
return nil return nil
} }
func (f *FakeFactory) AttachablePodForObject(ob runtime.Object) (*api.Pod, error) { func (f *FakeFactory) AttachablePodForObject(ob runtime.Object, timeout time.Duration) (*api.Pod, error) {
return nil, nil return nil, nil
} }
@ -613,7 +613,7 @@ func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options printers.Pri
return f.tf.Printer, f.tf.Err return f.tf.Printer, f.tf.Err
} }
func (f *fakeAPIFactory) LogsForObject(object, options runtime.Object) (*restclient.Request, error) { func (f *fakeAPIFactory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
c, err := f.ClientSet() c, err := f.ClientSet()
if err != nil { if err != nil {
panic(err) panic(err)
@ -635,7 +635,7 @@ func (f *fakeAPIFactory) LogsForObject(object, options runtime.Object) (*restcli
} }
} }
func (f *fakeAPIFactory) AttachablePodForObject(object runtime.Object) (*api.Pod, error) { func (f *fakeAPIFactory) AttachablePodForObject(object runtime.Object, timeout time.Duration) (*api.Pod, error) {
switch t := object.(type) { switch t := object.(type) {
case *api.Pod: case *api.Pod:
return t, nil return t, nil

View File

@ -198,7 +198,7 @@ type ObjectMappingFactory interface {
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 returns a request for the logs associated with the provided object
LogsForObject(object, options runtime.Object) (*restclient.Request, error) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error)
// Returns a Scaler for changing the size of the specified RESTMapping type or an error // Returns a Scaler for changing the size of the specified RESTMapping type or an error
Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error) Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error)
// Returns a Reaper for gracefully shutting down resources. // Returns a Reaper for gracefully shutting down resources.
@ -211,7 +211,7 @@ type ObjectMappingFactory interface {
StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusViewer, error)
// AttachablePodForObject returns the pod to which to attach given an object. // AttachablePodForObject returns the pod to which to attach given an object.
AttachablePodForObject(object runtime.Object) (*api.Pod, error) AttachablePodForObject(object runtime.Object, timeout time.Duration) (*api.Pod, error)
// Returns a schema that can validate objects stored on disk. // Returns a schema that can validate objects stored on disk.
Validator(validate bool, cacheDir string) (validation.Schema, error) Validator(validate bool, cacheDir string) (validation.Schema, error)

View File

@ -208,7 +208,7 @@ 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) (*restclient.Request, error) { func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
clientset, err := f.clientAccessFactory.ClientSetForVersion(nil) clientset, err := f.clientAccessFactory.ClientSetForVersion(nil)
if err != nil { if err != nil {
return nil, err return nil, err
@ -265,7 +265,7 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclien
} }
sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) } sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
pod, numPods, err := GetFirstPod(clientset.Core(), namespace, selector, 20*time.Second, sortBy) pod, numPods, err := GetFirstPod(clientset.Core(), namespace, selector, timeout, sortBy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -325,7 +325,7 @@ func (f *ring1Factory) StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusVi
return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), clientset) return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
} }
func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod, error) { func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout time.Duration) (*api.Pod, error) {
clientset, err := f.clientAccessFactory.ClientSetForVersion(nil) clientset, err := f.clientAccessFactory.ClientSetForVersion(nil)
if err != nil { if err != nil {
return nil, err return nil, err
@ -374,7 +374,7 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod,
} }
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
pod, _, err := GetFirstPod(clientset.Core(), namespace, selector, 1*time.Minute, sortBy) pod, _, err := GetFirstPod(clientset.Core(), namespace, selector, timeout, sortBy)
return pod, err return pod, err
} }

View File

@ -31,6 +31,7 @@ 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/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"time"
) )
type fakeClientAccessFactory struct { type fakeClientAccessFactory struct {
@ -146,7 +147,7 @@ func TestLogsForObject(t *testing.T) {
for _, test := range tests { for _, test := range tests {
caf := newFakeClientAccessFactory(test.pods) caf := newFakeClientAccessFactory(test.pods)
omf := NewObjectMappingFactory(caf) omf := NewObjectMappingFactory(caf)
_, err := omf.LogsForObject(test.obj, test.opts) _, 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

View File

@ -387,6 +387,14 @@ func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
return d return d
} }
func GetPodRunningTimeoutFlag(cmd *cobra.Command) (time.Duration, error) {
timeout := GetFlagDuration(cmd, "pod-running-timeout")
if timeout <= 0 {
return timeout, fmt.Errorf("--pod-running-timeout must be higher than zero")
}
return timeout, nil
}
func AddValidateFlags(cmd *cobra.Command) { func AddValidateFlags(cmd *cobra.Command) {
cmd.Flags().Bool("validate", true, "If true, use a schema to validate the input before sending it") cmd.Flags().Bool("validate", true, "If true, use a schema to validate the input before sending it")
cmd.Flags().String("schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName)) cmd.Flags().String("schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName))
@ -403,6 +411,10 @@ func AddDryRunFlag(cmd *cobra.Command) {
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.") cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
} }
func AddPodRunningTimeoutFlag(cmd *cobra.Command, defaultTimeout time.Duration) {
cmd.Flags().Duration("pod-running-timeout", defaultTimeout, "The length of time (like 5s, 2m, or 3h, higher than zero) to wait until at least one pod is running")
}
func AddApplyAnnotationFlags(cmd *cobra.Command) { func AddApplyAnnotationFlags(cmd *cobra.Command) {
cmd.Flags().Bool(ApplyAnnotationsFlag, false, "If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.") cmd.Flags().Bool(ApplyAnnotationsFlag, false, "If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.")
} }