Add support to pull log for last terminated container
This commit is contained in:
parent
ffa5947010
commit
86479cc56c
@ -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)
|
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 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 {
|
if cStatus.State.Waiting != nil {
|
||||||
return "", fmt.Errorf("container %q is in waiting state.", containerName)
|
return "", fmt.Errorf("container %q is in waiting state.", containerName)
|
||||||
}
|
}
|
||||||
return kubecontainer.TrimRuntimePrefix(cStatus.ContainerID), nil
|
cID = cStatus.ContainerID
|
||||||
|
}
|
||||||
|
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 {
|
||||||
@ -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).
|
// 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.
|
||||||
|
@ -3141,6 +3141,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,
|
||||||
@ -3172,7 +3175,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)
|
||||||
@ -3183,9 +3186,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 {
|
|||||||
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) {
|
|||||||
|
|
||||||
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) {
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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
|
|||||||
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),
|
||||||
|
Loading…
Reference in New Issue
Block a user