Allow a selector when retrieving logs

pull/6/head
Michael Fraenkel 2016-09-14 21:23:00 -04:00 committed by Michael Fraenkel
parent 23389b2dc6
commit acb032fc72
1 changed files with 57 additions and 17 deletions

View File

@ -40,6 +40,9 @@ var (
# Return snapshot logs from pod nginx with only one container # Return snapshot logs from pod nginx with only one container
kubectl logs nginx kubectl logs nginx
# Return snapshot logs for the pods defined by label app=nginx
kubectl logs -lapp=nginx
# Return snapshot of previous terminated ruby container logs from pod web-1 # Return snapshot of previous terminated ruby container logs from pod web-1
kubectl logs -p -c ruby web-1 kubectl logs -p -c ruby web-1
@ -51,6 +54,8 @@ var (
# Show all logs from pod nginx written in the last hour # Show all logs from pod nginx written in the last hour
kubectl logs --since=1h nginx`) kubectl logs --since=1h nginx`)
selectorTail int64 = 10
) )
const ( const (
@ -91,8 +96,7 @@ func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command {
if err := o.Validate(); err != nil { if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error())) cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error()))
} }
_, err := o.RunLogs() cmdutil.CheckErr(o.RunLogs())
cmdutil.CheckErr(err)
}, },
Aliases: []string{"log"}, Aliases: []string{"log"},
} }
@ -100,7 +104,7 @@ func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Bool("timestamps", false, "Include timestamps on each line in the log output") cmd.Flags().Bool("timestamps", false, "Include timestamps on each line in the log output")
cmd.Flags().Int64("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.") cmd.Flags().Int64("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.")
cmd.Flags().BoolP("previous", "p", false, "If true, print the logs for the previous instance of the container in a pod if it exists.") cmd.Flags().BoolP("previous", "p", false, "If true, print the logs for the previous instance of the container in a pod if it exists.")
cmd.Flags().Int64("tail", -1, "Lines of recent log file to display. Defaults to -1, showing all log lines.") cmd.Flags().Int64("tail", -1, "Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.")
cmd.Flags().String("since-time", "", "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", "", "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")
@ -108,16 +112,23 @@ func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command {
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)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.")
return cmd return cmd
} }
func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
containerName := cmdutil.GetFlagString(cmd, "container") containerName := cmdutil.GetFlagString(cmd, "container")
selector := cmdutil.GetFlagString(cmd, "selector")
switch len(args) { switch len(args) {
case 0: case 0:
return cmdutil.UsageError(cmd, logsUsageStr) if len(selector) == 0 {
return cmdutil.UsageError(cmd, logsUsageStr)
}
case 1: case 1:
o.ResourceArg = args[0] o.ResourceArg = args[0]
if len(selector) != 0 {
return cmdutil.UsageError(cmd, "only a selector (-l) or a POD name is allowed")
}
case 2: case 2:
if cmd.Flag("container").Changed { if cmd.Flag("container").Changed {
return cmdutil.UsageError(cmd, "only one of -c or an inline [CONTAINER] arg is allowed") return cmdutil.UsageError(cmd, "only one of -c or an inline [CONTAINER] arg is allowed")
@ -162,18 +173,35 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm
o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping) o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping)
o.Out = out o.Out = out
if len(selector) != 0 {
if logOptions.Follow {
return cmdutil.UsageError(cmd, "only one of follow (-f) or selector (-l) is allowed")
}
if len(logOptions.Container) != 0 {
return cmdutil.UsageError(cmd, "a container cannot be specified when using a selector (-l)")
}
if logOptions.TailLines == nil {
logOptions.TailLines = &selectorTail
}
}
mapper, typer := f.Object() mapper, typer := f.Object()
decoder := f.Decoder(true) decoder := f.Decoder(true)
if o.Object == nil { if o.Object == nil {
infos, err := resource.NewBuilder(mapper, typer, o.ClientMapper, decoder). builder := resource.NewBuilder(mapper, typer, o.ClientMapper, decoder).
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
ResourceNames("pods", o.ResourceArg). SingleResourceType()
SingleResourceType(). if o.ResourceArg != "" {
Do().Infos() builder.ResourceNames("pods", o.ResourceArg)
}
if selector != "" {
builder.ResourceTypes("pods").SelectorParam(selector)
}
infos, err := builder.Do().Infos()
if err != nil { if err != nil {
return err return err
} }
if len(infos) != 1 { if selector == "" && len(infos) != 1 {
return errors.New("expected a resource") return errors.New("expected a resource")
} }
o.Object = infos[0].Object o.Object = infos[0].Object
@ -183,9 +211,6 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm
} }
func (o LogsOptions) Validate() error { func (o LogsOptions) Validate() error {
if len(o.ResourceArg) == 0 {
return errors.New("a pod must be specified")
}
logsOptions, ok := o.Options.(*api.PodLogOptions) logsOptions, ok := o.Options.(*api.PodLogOptions)
if !ok { if !ok {
return errors.New("unexpected logs options object") return errors.New("unexpected logs options object")
@ -198,17 +223,32 @@ func (o LogsOptions) Validate() error {
} }
// RunLogs retrieves a pod log // RunLogs retrieves a pod log
func (o LogsOptions) RunLogs() (int64, error) { func (o LogsOptions) RunLogs() error {
req, err := o.LogsForObject(o.Object, o.Options) switch t := o.Object.(type) {
case *api.PodList:
for _, p := range t.Items {
if err := o.getLogs(&p); err != nil {
return err
}
}
return nil
default:
return o.getLogs(o.Object)
}
}
func (o LogsOptions) getLogs(obj runtime.Object) error {
req, err := o.LogsForObject(obj, o.Options)
if err != nil { if err != nil {
return 0, err return err
} }
readCloser, err := req.Stream() readCloser, err := req.Stream()
if err != nil { if err != nil {
return 0, err return err
} }
defer readCloser.Close() defer readCloser.Close()
return io.Copy(o.Out, readCloser) _, err = io.Copy(o.Out, readCloser)
return err
} }