Merge pull request #32752 from fraenkel/logs_selector

Automatic merge from submit-queue (batch tested with PRs 36071, 32752, 37998, 38350, 38401)

Allow a selector when retrieving logs

#19873

initial commit to see if I am headed in the right direction.
Its missing all the test cases, but the selector path works.
This commit is contained in:
Kubernetes Submit Queue 2016-12-08 16:25:54 -08:00 committed by GitHub
commit e3b9546028

View File

@ -40,6 +40,9 @@ var (
# Return snapshot logs from pod nginx with only one container
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
kubectl logs -p -c ruby web-1
@ -51,6 +54,8 @@ var (
# Show all logs from pod nginx written in the last hour
kubectl logs --since=1h nginx`)
selectorTail int64 = 10
)
const (
@ -91,8 +96,7 @@ func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command {
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error()))
}
_, err := o.RunLogs()
cmdutil.CheckErr(err)
cmdutil.CheckErr(o.RunLogs())
},
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().Int64("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.")
cmd.Flags().BoolP("previous", "p", false, "If true, print the logs for the previous instance of the container in a pod if it exists.")
cmd.Flags().Int64("tail", -1, "Lines of recent log file to display. Defaults to -1, 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().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")
@ -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().MarkDeprecated("interactive", "This flag is no longer respected and there is no replacement.")
cmdutil.AddInclude3rdPartyFlags(cmd)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.")
return cmd
}
func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
containerName := cmdutil.GetFlagString(cmd, "container")
selector := cmdutil.GetFlagString(cmd, "selector")
switch len(args) {
case 0:
return cmdutil.UsageError(cmd, logsUsageStr)
if len(selector) == 0 {
return cmdutil.UsageError(cmd, logsUsageStr)
}
case 1:
o.ResourceArg = args[0]
if len(selector) != 0 {
return cmdutil.UsageError(cmd, "only a selector (-l) or a POD name is allowed")
}
case 2:
if cmd.Flag("container").Changed {
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.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()
decoder := f.Decoder(true)
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().
ResourceNames("pods", o.ResourceArg).
SingleResourceType().
Do().Infos()
SingleResourceType()
if o.ResourceArg != "" {
builder.ResourceNames("pods", o.ResourceArg)
}
if selector != "" {
builder.ResourceTypes("pods").SelectorParam(selector)
}
infos, err := builder.Do().Infos()
if err != nil {
return err
}
if len(infos) != 1 {
if selector == "" && len(infos) != 1 {
return errors.New("expected a resource")
}
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 {
if len(o.ResourceArg) == 0 {
return errors.New("a pod must be specified")
}
logsOptions, ok := o.Options.(*api.PodLogOptions)
if !ok {
return errors.New("unexpected logs options object")
@ -198,17 +223,32 @@ func (o LogsOptions) Validate() error {
}
// RunLogs retrieves a pod log
func (o LogsOptions) RunLogs() (int64, error) {
req, err := o.LogsForObject(o.Object, o.Options)
func (o LogsOptions) RunLogs() error {
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 {
return 0, err
return err
}
readCloser, err := req.Stream()
if err != nil {
return 0, err
return err
}
defer readCloser.Close()
return io.Copy(o.Out, readCloser)
_, err = io.Copy(o.Out, readCloser)
return err
}