Commit 43029345 authored by Victor Marmol's avatar Victor Marmol

Merge pull request #7973 from dchen1107/podstatus

Add kubectl log --previous support to view last terminated container log
parents fbd0143f 86479cc5
...@@ -399,6 +399,8 @@ _kubectl_log() ...@@ -399,6 +399,8 @@ _kubectl_log()
flags+=("--help") flags+=("--help")
flags+=("-h") flags+=("-h")
flags+=("--interactive") flags+=("--interactive")
flags+=("--previous")
flags+=("-p")
must_have_one_flag=() must_have_one_flag=()
must_have_one_noun=() must_have_one_noun=()
......
...@@ -8,7 +8,7 @@ Print the logs for a container in a pod. ...@@ -8,7 +8,7 @@ Print the logs for a container in a pod.
Print the logs for a container in a pod. If the pod has only one container, the container name is optional. Print the logs for a container in a pod. If the pod has only one container, the container name is optional.
``` ```
kubectl log [-f] POD [CONTAINER] kubectl log [-f] [-p] POD [CONTAINER]
``` ```
### Examples ### Examples
...@@ -17,6 +17,9 @@ kubectl log [-f] POD [CONTAINER] ...@@ -17,6 +17,9 @@ kubectl log [-f] POD [CONTAINER]
// Returns snapshot of ruby-container logs from pod 123456-7890. // Returns snapshot of ruby-container logs from pod 123456-7890.
$ kubectl log 123456-7890 ruby-container $ kubectl log 123456-7890 ruby-container
// Returns snapshot of previous terminated ruby-container logs from pod 123456-7890.
$ kubectl log -p 123456-7890 ruby-container
// Starts streaming of ruby-container logs from pod 123456-7890. // Starts streaming of ruby-container logs from pod 123456-7890.
$ kubectl log -f 123456-7890 ruby-container $ kubectl log -f 123456-7890 ruby-container
``` ```
...@@ -27,6 +30,7 @@ $ kubectl log -f 123456-7890 ruby-container ...@@ -27,6 +30,7 @@ $ kubectl log -f 123456-7890 ruby-container
-f, --follow=false: Specify if the logs should be streamed. -f, --follow=false: Specify if the logs should be streamed.
-h, --help=false: help for log -h, --help=false: help for log
--interactive=true: If true, prompt the user for input when required. Default true. --interactive=true: If true, prompt the user for input when required. Default true.
-p, --previous=false: If true, print the logs for the previous instance of the container in a pod if it exists.
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
......
...@@ -29,6 +29,10 @@ Print the logs for a container in a pod. If the pod has only one container, the ...@@ -29,6 +29,10 @@ Print the logs for a container in a pod. If the pod has only one container, the
\fB\-\-interactive\fP=true \fB\-\-interactive\fP=true
If true, prompt the user for input when required. Default true. If true, prompt the user for input when required. Default true.
.PP
\fB\-p\fP, \fB\-\-previous\fP=false
If true, print the logs for the previous instance of the container in a pod if it exists.
.SH OPTIONS INHERITED FROM PARENT COMMANDS .SH OPTIONS INHERITED FROM PARENT COMMANDS
.PP .PP
...@@ -136,6 +140,9 @@ Print the logs for a container in a pod. If the pod has only one container, the ...@@ -136,6 +140,9 @@ Print the logs for a container in a pod. If the pod has only one container, the
// Returns snapshot of ruby\-container logs from pod 123456\-7890. // Returns snapshot of ruby\-container logs from pod 123456\-7890.
$ kubectl log 123456\-7890 ruby\-container $ kubectl log 123456\-7890 ruby\-container
// Returns snapshot of previous terminated ruby\-container logs from pod 123456\-7890.
$ kubectl log \-p 123456\-7890 ruby\-container
// Starts streaming of ruby\-container logs from pod 123456\-7890. // Starts streaming of ruby\-container logs from pod 123456\-7890.
$ kubectl log \-f 123456\-7890 ruby\-container $ kubectl log \-f 123456\-7890 ruby\-container
......
...@@ -1332,6 +1332,9 @@ type PodLogOptions struct { ...@@ -1332,6 +1332,9 @@ type PodLogOptions struct {
// If true, follow the logs for the pod // If true, follow the logs for the pod
Follow bool Follow bool
// If true, return previous terminated container logs
Previous bool
} }
// PodExecOptions is the query options to a Pod's remote exec call // PodExecOptions is the query options to a Pod's remote exec call
......
...@@ -1808,6 +1808,7 @@ func init() { ...@@ -1808,6 +1808,7 @@ func init() {
} }
out.Container = in.Container out.Container = in.Container
out.Follow = in.Follow out.Follow = in.Follow
out.Previous = in.Previous
return nil return nil
}, },
func(in *newer.PodLogOptions, out *PodLogOptions, s conversion.Scope) error { func(in *newer.PodLogOptions, out *PodLogOptions, s conversion.Scope) error {
...@@ -1816,6 +1817,7 @@ func init() { ...@@ -1816,6 +1817,7 @@ func init() {
} }
out.Container = in.Container out.Container = in.Container
out.Follow = in.Follow out.Follow = in.Follow
out.Previous = in.Previous
return nil return nil
}, },
func(in *PodProxyOptions, out *newer.PodProxyOptions, s conversion.Scope) error { func(in *PodProxyOptions, out *newer.PodProxyOptions, s conversion.Scope) error {
......
...@@ -1322,6 +1322,9 @@ type PodLogOptions struct { ...@@ -1322,6 +1322,9 @@ type PodLogOptions struct {
// If true, follow the logs for the pod // If true, follow the logs for the pod
Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"` Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"`
// If true, return previous terminated container logs
Previous bool `json:"previous,omitempty" description:"return previous terminated container logs; defaults to false"`
} }
// PodExecOptions is the query options to a Pod's remote exec call // PodExecOptions is the query options to a Pod's remote exec call
......
...@@ -1190,6 +1190,9 @@ type PodLogOptions struct { ...@@ -1190,6 +1190,9 @@ type PodLogOptions struct {
// If true, follow the logs for the pod // If true, follow the logs for the pod
Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"` Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"`
// If true, return previous terminated container logs
Previous bool `json:"previous,omitempty" description:"return previous terminated container logs; defaults to false"`
} }
// PodExecOptions is the query options to a Pod's remote exec call // PodExecOptions is the query options to a Pod's remote exec call
......
...@@ -1210,6 +1210,9 @@ type PodLogOptions struct { ...@@ -1210,6 +1210,9 @@ type PodLogOptions struct {
// If true, follow the logs for the pod // If true, follow the logs for the pod
Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"` Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"`
// If true, return previous terminated container logs
Previous bool `json:"previous,omitempty" description:"return previous terminated container logs; defaults to false"`
} }
// PodExecOptions is the query options to a Pod's remote exec call // PodExecOptions is the query options to a Pod's remote exec call
......
...@@ -2560,6 +2560,7 @@ func convert_v1beta3_PodLogOptions_To_api_PodLogOptions(in *PodLogOptions, out * ...@@ -2560,6 +2560,7 @@ func convert_v1beta3_PodLogOptions_To_api_PodLogOptions(in *PodLogOptions, out *
} }
out.Container = in.Container out.Container = in.Container
out.Follow = in.Follow out.Follow = in.Follow
out.Previous = in.Previous
return nil return nil
} }
...@@ -2572,6 +2573,7 @@ func convert_api_PodLogOptions_To_v1beta3_PodLogOptions(in *newer.PodLogOptions, ...@@ -2572,6 +2573,7 @@ func convert_api_PodLogOptions_To_v1beta3_PodLogOptions(in *newer.PodLogOptions,
} }
out.Container = in.Container out.Container = in.Container
out.Follow = in.Follow out.Follow = in.Follow
out.Previous = in.Previous
return nil return nil
} }
......
...@@ -1322,6 +1322,9 @@ type PodLogOptions struct { ...@@ -1322,6 +1322,9 @@ type PodLogOptions struct {
// If true, follow the logs for the pod // If true, follow the logs for the pod
Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"` Follow bool `json:"follow,omitempty" description:"follow the log stream of the pod; defaults to false"`
// If true, return previous terminated container logs
Previous bool `json:"previous,omitempty" description:"return previous terminated container logs; defaults to false"`
} }
// PodExecOptions is the query options to a Pod's remote exec call // PodExecOptions is the query options to a Pod's remote exec call
......
...@@ -31,6 +31,9 @@ const ( ...@@ -31,6 +31,9 @@ const (
log_example = `// Returns snapshot of ruby-container logs from pod 123456-7890. log_example = `// Returns snapshot of ruby-container logs from pod 123456-7890.
$ kubectl log 123456-7890 ruby-container $ kubectl log 123456-7890 ruby-container
// Returns snapshot of previous terminated ruby-container logs from pod 123456-7890.
$ kubectl log -p 123456-7890 ruby-container
// Starts streaming of ruby-container logs from pod 123456-7890. // Starts streaming of ruby-container logs from pod 123456-7890.
$ kubectl log -f 123456-7890 ruby-container` $ kubectl log -f 123456-7890 ruby-container`
) )
...@@ -60,7 +63,7 @@ func selectContainer(pod *api.Pod, in io.Reader, out io.Writer) string { ...@@ -60,7 +63,7 @@ func selectContainer(pod *api.Pod, in io.Reader, out io.Writer) string {
// NewCmdLog creates a new pod log command // NewCmdLog creates a new pod log command
func NewCmdLog(f *cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdLog(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "log [-f] POD [CONTAINER]", Use: "log [-f] [-p] POD [CONTAINER]",
Short: "Print the logs for a container in a pod.", Short: "Print the logs for a container in a pod.",
Long: "Print the logs for a container in a pod. If the pod has only one container, the container name is optional.", Long: "Print the logs for a container in a pod. If the pod has only one container, the container name is optional.",
Example: log_example, Example: log_example,
...@@ -71,6 +74,7 @@ func NewCmdLog(f *cmdutil.Factory, out io.Writer) *cobra.Command { ...@@ -71,6 +74,7 @@ func NewCmdLog(f *cmdutil.Factory, out io.Writer) *cobra.Command {
} }
cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.") cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.")
cmd.Flags().Bool("interactive", true, "If true, prompt the user for input when required. Default true.") 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.")
return cmd return cmd
} }
...@@ -115,6 +119,11 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string ...@@ -115,6 +119,11 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
follow = true follow = true
} }
previous := false
if cmdutil.GetFlagBool(cmd, "previous") {
previous = true
}
readCloser, err := client.RESTClient.Get(). readCloser, err := client.RESTClient.Get().
Namespace(namespace). Namespace(namespace).
Name(podID). Name(podID).
...@@ -122,6 +131,7 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string ...@@ -122,6 +131,7 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
SubResource("log"). SubResource("log").
Param("follow", strconv.FormatBool(follow)). Param("follow", strconv.FormatBool(follow)).
Param("container", container). Param("container", container).
Param("previous", strconv.FormatBool(previous)).
Stream() Stream()
if err != nil { if err != nil {
return err return err
......
...@@ -1386,21 +1386,31 @@ func (kl *Kubelet) validatePodPhase(podStatus *api.PodStatus) error { ...@@ -1386,21 +1386,31 @@ func (kl *Kubelet) validatePodPhase(podStatus *api.PodStatus) error {
return fmt.Errorf("pod is not in 'Running', 'Succeeded' or 'Failed' state - State: %q", podStatus.Phase) return fmt.Errorf("pod is not in 'Running', 'Succeeded' or 'Failed' state - State: %q", podStatus.Phase)
} }
func (kl *Kubelet) validateContainerStatus(podStatus *api.PodStatus, containerName string) (containerID string, err error) { func (kl *Kubelet) validateContainerStatus(podStatus *api.PodStatus, containerName string, previous bool) (containerID string, err error) {
var cID string
cStatus, found := api.GetContainerStatus(podStatus.ContainerStatuses, containerName) cStatus, found := api.GetContainerStatus(podStatus.ContainerStatuses, containerName)
if !found { if !found {
return "", fmt.Errorf("container %q not found in pod", containerName) return "", fmt.Errorf("container %q not found in pod", containerName)
} }
if cStatus.State.Waiting != nil { if previous {
return "", fmt.Errorf("container %q is in waiting state.", containerName) if cStatus.LastTerminationState.Termination == nil {
return "", fmt.Errorf("previous terminated container %q not found in pod", containerName)
}
cID = cStatus.LastTerminationState.Termination.ContainerID
} else {
if cStatus.State.Waiting != nil {
return "", fmt.Errorf("container %q is in waiting state.", containerName)
}
cID = cStatus.ContainerID
} }
return kubecontainer.TrimRuntimePrefix(cStatus.ContainerID), nil return kubecontainer.TrimRuntimePrefix(cID), nil
} }
// GetKubeletContainerLogs returns logs from the container // GetKubeletContainerLogs returns logs from the container
// TODO: this method is returning logs of random container attempts, when it should be returning the most recent attempt // TODO: this method is returning logs of random container attempts, when it should be returning the most recent attempt
// or all of them. // or all of them.
func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error { func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow, previous bool, stdout, stderr io.Writer) error {
// TODO(vmarmol): Refactor to not need the pod status and verification. // TODO(vmarmol): Refactor to not need the pod status and verification.
podStatus, err := kl.GetPodStatus(podFullName) podStatus, err := kl.GetPodStatus(podFullName)
if err != nil { if err != nil {
...@@ -1410,7 +1420,7 @@ func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail stri ...@@ -1410,7 +1420,7 @@ func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail stri
// No log is available if pod is not in a "known" phase (e.g. Unknown). // No log is available if pod is not in a "known" phase (e.g. Unknown).
return err return err
} }
containerID, err := kl.validateContainerStatus(&podStatus, containerName) containerID, err := kl.validateContainerStatus(&podStatus, containerName, previous)
if err != nil { if err != nil {
// No log is available if the container status is missing or is in the // No log is available if the container status is missing or is in the
// waiting state. // waiting state.
......
...@@ -3134,6 +3134,9 @@ func TestValidateContainerStatus(t *testing.T) { ...@@ -3134,6 +3134,9 @@ func TestValidateContainerStatus(t *testing.T) {
State: api.ContainerState{ State: api.ContainerState{
Running: &api.ContainerStateRunning{}, Running: &api.ContainerStateRunning{},
}, },
LastTerminationState: api.ContainerState{
Termination: &api.ContainerStateTerminated{},
},
}, },
}, },
success: true, success: true,
...@@ -3165,7 +3168,7 @@ func TestValidateContainerStatus(t *testing.T) { ...@@ -3165,7 +3168,7 @@ func TestValidateContainerStatus(t *testing.T) {
for i, tc := range testCases { for i, tc := range testCases {
_, err := kubelet.validateContainerStatus(&api.PodStatus{ _, err := kubelet.validateContainerStatus(&api.PodStatus{
ContainerStatuses: tc.statuses, ContainerStatuses: tc.statuses,
}, containerName) }, containerName, false)
if tc.success { if tc.success {
if err != nil { if err != nil {
t.Errorf("[case %d]: unexpected failure - %v", i, err) t.Errorf("[case %d]: unexpected failure - %v", i, err)
...@@ -3176,9 +3179,19 @@ func TestValidateContainerStatus(t *testing.T) { ...@@ -3176,9 +3179,19 @@ func TestValidateContainerStatus(t *testing.T) {
} }
if _, err := kubelet.validateContainerStatus(&api.PodStatus{ if _, err := kubelet.validateContainerStatus(&api.PodStatus{
ContainerStatuses: testCases[0].statuses, ContainerStatuses: testCases[0].statuses,
}, "blah"); err == nil { }, "blah", false); err == nil {
t.Errorf("expected error with invalid container name") t.Errorf("expected error with invalid container name")
} }
if _, err := kubelet.validateContainerStatus(&api.PodStatus{
ContainerStatuses: testCases[0].statuses,
}, containerName, true); err != nil {
t.Errorf("unexpected error with for previous terminated container - %v", err)
}
if _, err := kubelet.validateContainerStatus(&api.PodStatus{
ContainerStatuses: testCases[1].statuses,
}, containerName, true); err == nil {
t.Errorf("expected error with for previous terminated container")
}
} }
func TestUpdateNewNodeStatus(t *testing.T) { func TestUpdateNewNodeStatus(t *testing.T) {
......
...@@ -107,7 +107,7 @@ type HostInterface interface { ...@@ -107,7 +107,7 @@ type HostInterface interface {
GetPodStatus(name string) (api.PodStatus, error) GetPodStatus(name string) (api.PodStatus, error)
RunInContainer(name string, uid types.UID, container string, cmd []string) ([]byte, error) 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 ExecInContainer(name string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error
GetKubeletContainerLogs(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error GetKubeletContainerLogs(podFullName, containerName, tail string, follow, previous bool, stdout, stderr io.Writer) error
ServeLogs(w http.ResponseWriter, req *http.Request) ServeLogs(w http.ResponseWriter, req *http.Request)
PortForward(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error PortForward(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error
StreamingConnectionIdleTimeout() time.Duration StreamingConnectionIdleTimeout() time.Duration
...@@ -230,6 +230,7 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) { ...@@ -230,6 +230,7 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) {
uriValues := u.Query() uriValues := u.Query()
follow, _ := strconv.ParseBool(uriValues.Get("follow")) follow, _ := strconv.ParseBool(uriValues.Get("follow"))
previous, _ := strconv.ParseBool(uriValues.Get("previous"))
tail := uriValues.Get("tail") tail := uriValues.Get("tail")
pod, ok := s.host.GetPodByName(podNamespace, podID) pod, ok := s.host.GetPodByName(podNamespace, podID)
...@@ -256,7 +257,7 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) { ...@@ -256,7 +257,7 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) {
fw := flushwriter.Wrap(w) fw := flushwriter.Wrap(w)
w.Header().Set("Transfer-Encoding", "chunked") w.Header().Set("Transfer-Encoding", "chunked")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
err = s.host.GetKubeletContainerLogs(kubecontainer.GetPodFullName(pod), containerName, tail, follow, fw, fw) err = s.host.GetKubeletContainerLogs(kubecontainer.GetPodFullName(pod), containerName, tail, follow, previous, fw, fw)
if err != nil { if err != nil {
s.error(w, err) s.error(w, err)
return return
......
...@@ -53,7 +53,7 @@ type fakeKubelet struct { ...@@ -53,7 +53,7 @@ type fakeKubelet struct {
containerVersionFunc func() (kubecontainer.Version, error) containerVersionFunc func() (kubecontainer.Version, error)
execFunc func(pod string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error execFunc func(pod string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error
portForwardFunc func(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error portForwardFunc func(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error
containerLogsFunc func(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error containerLogsFunc func(podFullName, containerName, tail string, follow, pervious bool, stdout, stderr io.Writer) error
streamingConnectionIdleTimeoutFunc func() time.Duration streamingConnectionIdleTimeoutFunc func() time.Duration
hostnameFunc func() string hostnameFunc func() string
} }
...@@ -90,8 +90,8 @@ func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) { ...@@ -90,8 +90,8 @@ func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
fk.logFunc(w, req) fk.logFunc(w, req)
} }
func (fk *fakeKubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error { func (fk *fakeKubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow, previous bool, stdout, stderr io.Writer) error {
return fk.containerLogsFunc(podFullName, containerName, tail, follow, stdout, stderr) return fk.containerLogsFunc(podFullName, containerName, tail, follow, previous, stdout, stderr)
} }
func (fk *fakeKubelet) GetHostname() string { func (fk *fakeKubelet) GetHostname() string {
...@@ -553,8 +553,8 @@ func setPodByNameFunc(fw *serverTestFramework, namespace, pod, container string) ...@@ -553,8 +553,8 @@ func setPodByNameFunc(fw *serverTestFramework, namespace, pod, container string)
} }
} }
func setGetContainerLogsFunc(fw *serverTestFramework, t *testing.T, expectedPodName, expectedContainerName, expectedTail string, expectedFollow bool, output string) { func setGetContainerLogsFunc(fw *serverTestFramework, t *testing.T, expectedPodName, expectedContainerName, expectedTail string, expectedFollow, expectedPrevious bool, output string) {
fw.fakeKubelet.containerLogsFunc = func(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error { fw.fakeKubelet.containerLogsFunc = func(podFullName, containerName, tail string, follow, previous bool, stdout, stderr io.Writer) error {
if podFullName != expectedPodName { if podFullName != expectedPodName {
t.Errorf("expected %s, got %s", expectedPodName, podFullName) t.Errorf("expected %s, got %s", expectedPodName, podFullName)
} }
...@@ -567,6 +567,10 @@ func setGetContainerLogsFunc(fw *serverTestFramework, t *testing.T, expectedPodN ...@@ -567,6 +567,10 @@ func setGetContainerLogsFunc(fw *serverTestFramework, t *testing.T, expectedPodN
if follow != expectedFollow { if follow != expectedFollow {
t.Errorf("expected %t, got %t", expectedFollow, follow) t.Errorf("expected %t, got %t", expectedFollow, follow)
} }
if previous != expectedPrevious {
t.Errorf("expected %t, got %t", expectedPrevious, previous)
}
io.WriteString(stdout, output) io.WriteString(stdout, output)
return nil return nil
} }
...@@ -581,8 +585,9 @@ func TestContainerLogs(t *testing.T) { ...@@ -581,8 +585,9 @@ func TestContainerLogs(t *testing.T) {
expectedContainerName := "baz" expectedContainerName := "baz"
expectedTail := "" expectedTail := ""
expectedFollow := false expectedFollow := false
expectedPrevious := false
setPodByNameFunc(fw, podNamespace, podName, expectedContainerName) setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, expectedTail, expectedFollow, output) setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, expectedTail, expectedFollow, expectedPrevious, output)
resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName) resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName)
if err != nil { if err != nil {
t.Errorf("Got error GETing: %v", err) t.Errorf("Got error GETing: %v", err)
...@@ -608,8 +613,9 @@ func TestContainerLogsWithTail(t *testing.T) { ...@@ -608,8 +613,9 @@ func TestContainerLogsWithTail(t *testing.T) {
expectedContainerName := "baz" expectedContainerName := "baz"
expectedTail := "5" expectedTail := "5"
expectedFollow := false expectedFollow := false
expectedPrevious := false
setPodByNameFunc(fw, podNamespace, podName, expectedContainerName) setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, expectedTail, expectedFollow, output) setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, expectedTail, expectedFollow, expectedPrevious, output)
resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?tail=5") resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?tail=5")
if err != nil { if err != nil {
t.Errorf("Got error GETing: %v", err) t.Errorf("Got error GETing: %v", err)
...@@ -635,8 +641,9 @@ func TestContainerLogsWithFollow(t *testing.T) { ...@@ -635,8 +641,9 @@ func TestContainerLogsWithFollow(t *testing.T) {
expectedContainerName := "baz" expectedContainerName := "baz"
expectedTail := "" expectedTail := ""
expectedFollow := true expectedFollow := true
expectedPrevious := false
setPodByNameFunc(fw, podNamespace, podName, expectedContainerName) setPodByNameFunc(fw, podNamespace, podName, expectedContainerName)
setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, expectedTail, expectedFollow, output) setGetContainerLogsFunc(fw, t, expectedPodName, expectedContainerName, expectedTail, expectedFollow, expectedPrevious, output)
resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?follow=1") resp, err := http.Get(fw.testHTTPServer.URL + "/containerLogs/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?follow=1")
if err != nil { if err != nil {
t.Errorf("Got error GETing: %v", err) t.Errorf("Got error GETing: %v", err)
......
...@@ -212,6 +212,9 @@ func LogLocation(getter ResourceGetter, connInfo client.ConnectionInfoGetter, ct ...@@ -212,6 +212,9 @@ func LogLocation(getter ResourceGetter, connInfo client.ConnectionInfoGetter, ct
if opts.Follow { if opts.Follow {
params.Add("follow", "true") params.Add("follow", "true")
} }
if opts.Previous {
params.Add("previous", "true")
}
loc := &url.URL{ loc := &url.URL{
Scheme: nodeScheme, Scheme: nodeScheme,
Host: fmt.Sprintf("%s:%d", nodeHost, nodePort), Host: fmt.Sprintf("%s:%d", nodeHost, nodePort),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment