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:
@@ -36,7 +36,11 @@ import (
|
||||
cadvisorApi "github.com/google/cadvisor/info/v1"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
apierrs "k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/healthz"
|
||||
"k8s.io/kubernetes/pkg/httplog"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
@@ -44,6 +48,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/flushwriter"
|
||||
"k8s.io/kubernetes/pkg/util/httpstream"
|
||||
"k8s.io/kubernetes/pkg/util/httpstream/spdy"
|
||||
"k8s.io/kubernetes/pkg/util/limitwriter"
|
||||
)
|
||||
|
||||
// Server is a http.Handler which exposes kubelet functionality over HTTP.
|
||||
@@ -102,7 +107,7 @@ type HostInterface interface {
|
||||
RunInContainer(name string, uid types.UID, container string, cmd []string) ([]byte, error)
|
||||
ExecInContainer(name string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error
|
||||
AttachContainer(name string, uid types.UID, container string, in io.Reader, out, err io.WriteCloser, tty bool) error
|
||||
GetKubeletContainerLogs(podFullName, containerName, tail string, follow, previous bool, stdout, stderr io.Writer) error
|
||||
GetKubeletContainerLogs(podFullName, containerName string, logOptions *api.PodLogOptions, stdout, stderr io.Writer) error
|
||||
ServeLogs(w http.ResponseWriter, req *http.Request)
|
||||
PortForward(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error
|
||||
StreamingConnectionIdleTimeout() time.Duration
|
||||
@@ -308,6 +313,7 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
||||
|
||||
if len(podID) == 0 {
|
||||
// TODO: Why return JSON when the rest return plaintext errors?
|
||||
// TODO: Why return plaintext errors?
|
||||
response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Missing podID."}`))
|
||||
return
|
||||
}
|
||||
@@ -322,9 +328,32 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
||||
return
|
||||
}
|
||||
|
||||
follow, _ := strconv.ParseBool(request.QueryParameter("follow"))
|
||||
previous, _ := strconv.ParseBool(request.QueryParameter("previous"))
|
||||
tail := request.QueryParameter("tail")
|
||||
query := request.Request.URL.Query()
|
||||
// backwards compatibility for the "tail" query parameter
|
||||
if tail := request.QueryParameter("tail"); len(tail) > 0 {
|
||||
query["tailLines"] = []string{tail}
|
||||
// "all" is the same as omitting tail
|
||||
if tail == "all" {
|
||||
delete(query, "tailLines")
|
||||
}
|
||||
}
|
||||
// container logs on the kubelet are locked to v1
|
||||
versioned := &v1.PodLogOptions{}
|
||||
if err := api.Scheme.Convert(&query, versioned); err != nil {
|
||||
response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to decode query."}`))
|
||||
return
|
||||
}
|
||||
out, err := api.Scheme.ConvertToVersion(versioned, "")
|
||||
if err != nil {
|
||||
response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to convert request query."}`))
|
||||
return
|
||||
}
|
||||
logOptions := out.(*api.PodLogOptions)
|
||||
logOptions.TypeMeta = unversioned.TypeMeta{}
|
||||
if errs := validation.ValidatePodLogOptions(logOptions); len(errs) > 0 {
|
||||
response.WriteError(apierrs.StatusUnprocessableEntity, fmt.Errorf(`{"message": "Invalid request."}`))
|
||||
return
|
||||
}
|
||||
|
||||
pod, ok := s.host.GetPodByName(podNamespace, podID)
|
||||
if !ok {
|
||||
@@ -348,11 +377,15 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
||||
return
|
||||
}
|
||||
fw := flushwriter.Wrap(response.ResponseWriter)
|
||||
if logOptions.LimitBytes != nil {
|
||||
fw = limitwriter.New(fw, *logOptions.LimitBytes)
|
||||
}
|
||||
response.Header().Set("Transfer-Encoding", "chunked")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
err := s.host.GetKubeletContainerLogs(kubecontainer.GetPodFullName(pod), containerName, tail, follow, previous, fw, fw)
|
||||
if err != nil {
|
||||
response.WriteError(http.StatusInternalServerError, err)
|
||||
if err := s.host.GetKubeletContainerLogs(kubecontainer.GetPodFullName(pod), containerName, logOptions, fw, fw); err != nil {
|
||||
if err != limitwriter.ErrMaximumWrite {
|
||||
response.WriteError(http.StatusInternalServerError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user