Support extended pod logging options

Increase the supported controls on pod logging. Add validaiton to pod
log options. Ensure the Kubelet is using a consistent, structured way to
process pod log arguments.

Add ?sinceSeconds=<durationInSeconds>, &sinceTime=<RFC3339>, ?timestamps=<bool>,
?tailLines=<number>, and ?limitBytes=<number>
This commit is contained in:
Clayton Coleman
2015-09-09 23:46:11 -04:00
parent a02bcefa10
commit c2e90cd154
30 changed files with 734 additions and 102 deletions

View File

@@ -19,26 +19,35 @@ package cmd
import (
"fmt"
"io"
"math"
"os"
"strconv"
"strings"
"time"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
client "k8s.io/kubernetes/pkg/client/unversioned"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/sets"
)
const (
log_example = `# Return snapshot of ruby-container logs from pod 123456-7890.
$ kubectl logs 123456-7890 ruby-container
log_example = `# Return snapshot logs from pod nginx with only one container
$ kubectl logs nginx
# Return snapshot of previous terminated ruby-container logs from pod 123456-7890.
$ kubectl logs -p 123456-7890 ruby-container
# Return snapshot of previous terminated ruby container logs from pod web-1
$ kubectl logs -p -c ruby web-1
# Start streaming of ruby-container logs from pod 123456-7890.
$ kubectl logs -f 123456-7890 ruby-container`
# Begin streaming the logs of the ruby container in pod web-1
$ kubectl logs -f -c ruby web-1
# Display only the most recent 20 lines of output in pod nginx
$ kubectl logs --tail=20 nginx
# Show all logs from pod nginx written in the last hour
$ kubectl logs --since=1h nginx`
)
func selectContainer(pod *api.Pod, in io.Reader, out io.Writer) string {
@@ -82,8 +91,13 @@ func NewCmdLog(f *cmdutil.Factory, out io.Writer) *cobra.Command {
Aliases: []string{"log"},
}
cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.")
cmd.Flags().Bool("timestamps", false, "Include timestamps on each line in the log output")
cmd.Flags().Bool("interactive", true, "If true, prompt the user for input when required. Default true.")
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().Int("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.")
cmd.Flags().Int("tail", -1, "Lines of recent log file to display. Defaults to -1, showing all log lines.")
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().StringVarP(&params.containerName, "container", "c", "", "Container name")
return cmd
}
@@ -102,6 +116,12 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
return cmdutil.UsageError(cmd, "log POD [CONTAINER]")
}
sinceSeconds := cmdutil.GetFlagDuration(cmd, "since")
sinceTime := cmdutil.GetFlagString(cmd, "since-time")
if len(sinceTime) > 0 && sinceSeconds > 0 {
return cmdutil.UsageError(cmd, "only one of --since, --since-time may be specified")
}
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
@@ -137,28 +157,57 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
}
}
follow := false
if cmdutil.GetFlagBool(cmd, "follow") {
follow = true
logOptions := &api.PodLogOptions{
Container: container,
Follow: cmdutil.GetFlagBool(cmd, "follow"),
Previous: cmdutil.GetFlagBool(cmd, "previous"),
Timestamps: cmdutil.GetFlagBool(cmd, "timestamps"),
}
if sinceSeconds > 0 {
// round up to the nearest second
sec := int64(math.Ceil(float64(sinceSeconds) / float64(time.Second)))
logOptions.SinceSeconds = &sec
}
if t, err := api.ParseRFC3339(sinceTime, unversioned.Now); err == nil {
logOptions.SinceTime = &t
}
if limitBytes := cmdutil.GetFlagInt(cmd, "limit-bytes"); limitBytes != 0 {
i := int64(limitBytes)
logOptions.LimitBytes = &i
}
if tail := cmdutil.GetFlagInt(cmd, "tail"); tail >= 0 {
i := int64(tail)
logOptions.TailLines = &i
}
previous := false
if cmdutil.GetFlagBool(cmd, "previous") {
previous = true
}
return handleLog(client, namespace, podID, container, follow, previous, out)
return handleLog(client, namespace, podID, logOptions, out)
}
func handleLog(client *client.Client, namespace, podID, container string, follow, previous bool, out io.Writer) error {
readCloser, err := client.RESTClient.Get().
func handleLog(client *client.Client, namespace, podID string, logOptions *api.PodLogOptions, out io.Writer) error {
// TODO: transform this into a PodLogOptions call
req := client.RESTClient.Get().
Namespace(namespace).
Name(podID).
Resource("pods").
SubResource("log").
Param("follow", strconv.FormatBool(follow)).
Param("container", container).
Param("previous", strconv.FormatBool(previous)).
Stream()
Param("follow", strconv.FormatBool(logOptions.Follow)).
Param("container", logOptions.Container).
Param("previous", strconv.FormatBool(logOptions.Previous)).
Param("timestamps", strconv.FormatBool(logOptions.Timestamps))
if logOptions.SinceSeconds != nil {
req.Param("sinceSeconds", strconv.FormatInt(*logOptions.SinceSeconds, 10))
}
if logOptions.SinceTime != nil {
req.Param("sinceTime", logOptions.SinceTime.Format(time.RFC3339))
}
if logOptions.LimitBytes != nil {
req.Param("limitBytes", strconv.FormatInt(*logOptions.LimitBytes, 10))
}
if logOptions.TailLines != nil {
req.Param("tailLines", strconv.FormatInt(*logOptions.TailLines, 10))
}
readCloser, err := req.Stream()
if err != nil {
return err
}