diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index cea8c5e1065c..0bfafe58b27f 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -298,7 +298,7 @@ func (dm *DockerManager) inspectContainer(dockerID, containerName, tPath string) if found { data, err := ioutil.ReadFile(path) if err != nil { - glog.Errorf("Error on reading termination-log %s: %v", path, err) + //glog.Errorf("Error on reading termination-log %s: %v", path, err) } else { result.status.State.Termination.Message = string(data) } diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index ccf7daa547be..1785c77b1762 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1373,21 +1373,33 @@ func (kl *Kubelet) validatePodPhase(podStatus *api.PodStatus) error { 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) if !found { return "", fmt.Errorf("container %q not found in pod", containerName) } - if cStatus.State.Waiting != nil { - return "", fmt.Errorf("container %q is in waiting state.", containerName) + if previous { + if cStatus.LastTerminationState.Termination == nil { + return "", fmt.Errorf("previous terminated container %q not found in pod", containerName) + } else { + cID = cStatus.LastTerminationState.Termination.ContainerID + } + } else { + if cStatus.State.Waiting != nil { + return "", fmt.Errorf("container %q is in waiting state.", containerName) + } else { + cID = cStatus.ContainerID + } } - return kubecontainer.TrimRuntimePrefix(cStatus.ContainerID), nil + return kubecontainer.TrimRuntimePrefix(cID), nil } // 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 // 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. podStatus, err := kl.GetPodStatus(podFullName) if err != nil { @@ -1397,7 +1409,7 @@ func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail stri // No log is available if pod is not in a "known" phase (e.g. Unknown). return err } - containerID, err := kl.validateContainerStatus(&podStatus, containerName) + containerID, err := kl.validateContainerStatus(&podStatus, containerName, previous) if err != nil { // No log is available if the container status is missing or is in the // waiting state. diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index f02cb66ee6c8..a9d599d4731d 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -3141,6 +3141,9 @@ func TestValidateContainerStatus(t *testing.T) { State: api.ContainerState{ Running: &api.ContainerStateRunning{}, }, + LastTerminationState: api.ContainerState{ + Termination: &api.ContainerStateTerminated{}, + }, }, }, success: true, @@ -3172,7 +3175,7 @@ func TestValidateContainerStatus(t *testing.T) { for i, tc := range testCases { _, err := kubelet.validateContainerStatus(&api.PodStatus{ ContainerStatuses: tc.statuses, - }, containerName) + }, containerName, false) if tc.success { if err != nil { t.Errorf("[case %d]: unexpected failure - %v", i, err) @@ -3183,9 +3186,19 @@ func TestValidateContainerStatus(t *testing.T) { } if _, err := kubelet.validateContainerStatus(&api.PodStatus{ ContainerStatuses: testCases[0].statuses, - }, "blah"); err == nil { + }, "blah", false); err == nil { 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) { diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index 7da16532edbd..550e31823744 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -107,7 +107,7 @@ type HostInterface interface { GetPodStatus(name string) (api.PodStatus, 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 - 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) PortForward(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error StreamingConnectionIdleTimeout() time.Duration @@ -230,6 +230,7 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) { uriValues := u.Query() follow, _ := strconv.ParseBool(uriValues.Get("follow")) + previous, _ := strconv.ParseBool(uriValues.Get("previous")) tail := uriValues.Get("tail") pod, ok := s.host.GetPodByName(podNamespace, podID) @@ -256,7 +257,7 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) { fw := flushwriter.Wrap(w) w.Header().Set("Transfer-Encoding", "chunked") 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 { s.error(w, err) return diff --git a/pkg/kubelet/server_test.go b/pkg/kubelet/server_test.go index 795040c81c42..e6d46b058051 100644 --- a/pkg/kubelet/server_test.go +++ b/pkg/kubelet/server_test.go @@ -53,7 +53,7 @@ type fakeKubelet struct { 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 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 hostnameFunc func() string } @@ -90,8 +90,8 @@ func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) { fk.logFunc(w, req) } -func (fk *fakeKubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error { - return fk.containerLogsFunc(podFullName, containerName, tail, follow, stdout, stderr) +func (fk *fakeKubelet) GetKubeletContainerLogs(podFullName, containerName, tail string, follow, previous bool, stdout, stderr io.Writer) error { + return fk.containerLogsFunc(podFullName, containerName, tail, follow, previous, stdout, stderr) } func (fk *fakeKubelet) GetHostname() 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) { - fw.fakeKubelet.containerLogsFunc = func(podFullName, containerName, tail string, follow bool, stdout, stderr io.Writer) error { +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, previous bool, stdout, stderr io.Writer) error { if podFullName != expectedPodName { t.Errorf("expected %s, got %s", expectedPodName, podFullName) } @@ -567,6 +567,10 @@ func setGetContainerLogsFunc(fw *serverTestFramework, t *testing.T, expectedPodN if follow != expectedFollow { t.Errorf("expected %t, got %t", expectedFollow, follow) } + if previous != expectedPrevious { + t.Errorf("expected %t, got %t", expectedPrevious, previous) + } + io.WriteString(stdout, output) return nil } @@ -581,8 +585,9 @@ func TestContainerLogs(t *testing.T) { expectedContainerName := "baz" expectedTail := "" expectedFollow := false + expectedPrevious := false 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) if err != nil { t.Errorf("Got error GETing: %v", err) @@ -608,8 +613,9 @@ func TestContainerLogsWithTail(t *testing.T) { expectedContainerName := "baz" expectedTail := "5" expectedFollow := false + expectedPrevious := false 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") if err != nil { t.Errorf("Got error GETing: %v", err) @@ -635,8 +641,9 @@ func TestContainerLogsWithFollow(t *testing.T) { expectedContainerName := "baz" expectedTail := "" expectedFollow := true + expectedPrevious := false 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") if err != nil { t.Errorf("Got error GETing: %v", err) diff --git a/pkg/registry/pod/rest.go b/pkg/registry/pod/rest.go index 4e2ec94e91c8..91d661068458 100644 --- a/pkg/registry/pod/rest.go +++ b/pkg/registry/pod/rest.go @@ -32,6 +32,8 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors" + + "github.com/golang/glog" ) // podStrategy implements behavior for Pods @@ -212,6 +214,10 @@ func LogLocation(getter ResourceGetter, connInfo client.ConnectionInfoGetter, ct if opts.Follow { params.Add("follow", "true") } + if opts.Previous { + params.Add("previous", "true") + } + glog.Infof("????????LogLocation=%v", opts) loc := &url.URL{ Scheme: nodeScheme, Host: fmt.Sprintf("%s:%d", nodeHost, nodePort),