Add support to pull log for last terminated container

This commit is contained in:
Dawn Chen 2015-05-07 11:34:16 -07:00
parent ffa5947010
commit 86479cc56c
5 changed files with 52 additions and 18 deletions

View File

@ -1373,21 +1373,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)
}
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)
}
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
// 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 +1407,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.

View File

@ -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) {

View File

@ -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

View File

@ -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)

View File

@ -212,6 +212,9 @@ func LogLocation(getter ResourceGetter, connInfo client.ConnectionInfoGetter, ct
if opts.Follow {
params.Add("follow", "true")
}
if opts.Previous {
params.Add("previous", "true")
}
loc := &url.URL{
Scheme: nodeScheme,
Host: fmt.Sprintf("%s:%d", nodeHost, nodePort),