mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #20258 from smarterclayton/get_logs_for_any_container
Auto commit by PR queue bot
This commit is contained in:
commit
348858061d
@ -2422,32 +2422,51 @@ func (kl *Kubelet) LatestLoopEntryTime() time.Time {
|
|||||||
return val.(time.Time)
|
return val.(time.Time)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kl *Kubelet) validatePodPhase(podStatus *api.PodStatus) error {
|
// validateContainerLogStatus returns the container ID for the desired container to retrieve logs for, based on the state
|
||||||
switch podStatus.Phase {
|
// of the container. The previous flag will only return the logs for the the last terminated container, otherwise, the current
|
||||||
case api.PodRunning, api.PodSucceeded, api.PodFailed:
|
// running container is preferred over a previous termination. If info about the container is not available then a specific
|
||||||
return nil
|
// error is returned to the end user.
|
||||||
}
|
func (kl *Kubelet) validateContainerLogStatus(podName string, podStatus *api.PodStatus, containerName string, previous bool) (containerID kubecontainer.ContainerID, err 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, previous bool) (containerID kubecontainer.ContainerID, err error) {
|
|
||||||
var cID string
|
var cID string
|
||||||
|
|
||||||
cStatus, found := api.GetContainerStatus(podStatus.ContainerStatuses, containerName)
|
cStatus, found := api.GetContainerStatus(podStatus.ContainerStatuses, containerName)
|
||||||
if !found {
|
if !found {
|
||||||
return kubecontainer.ContainerID{}, fmt.Errorf("container %q not found", containerName)
|
return kubecontainer.ContainerID{}, fmt.Errorf("container %q in pod %q is not available", containerName, podName)
|
||||||
}
|
}
|
||||||
if previous {
|
lastState := cStatus.LastTerminationState
|
||||||
if cStatus.LastTerminationState.Terminated == nil {
|
waiting, running, terminated := cStatus.State.Waiting, cStatus.State.Running, cStatus.State.Terminated
|
||||||
return kubecontainer.ContainerID{}, fmt.Errorf("previous terminated container %q not found", containerName)
|
|
||||||
}
|
switch {
|
||||||
cID = cStatus.LastTerminationState.Terminated.ContainerID
|
case previous:
|
||||||
} else {
|
if lastState.Terminated == nil {
|
||||||
if cStatus.State.Waiting != nil {
|
return kubecontainer.ContainerID{}, fmt.Errorf("previous terminated container %q in pod %q not found", containerName, podName)
|
||||||
return kubecontainer.ContainerID{}, fmt.Errorf("container %q is in waiting state.", containerName)
|
|
||||||
}
|
}
|
||||||
|
cID = lastState.Terminated.ContainerID
|
||||||
|
|
||||||
|
case running != nil:
|
||||||
cID = cStatus.ContainerID
|
cID = cStatus.ContainerID
|
||||||
|
|
||||||
|
case terminated != nil:
|
||||||
|
cID = terminated.ContainerID
|
||||||
|
|
||||||
|
case lastState.Terminated != nil:
|
||||||
|
cID = lastState.Terminated.ContainerID
|
||||||
|
|
||||||
|
case waiting != nil:
|
||||||
|
// output some info for the most common pending failures
|
||||||
|
switch reason := waiting.Reason; reason {
|
||||||
|
case kubecontainer.ErrImagePull.Error():
|
||||||
|
return kubecontainer.ContainerID{}, fmt.Errorf("container %q in pod %q is waiting to start: image can't be pulled", containerName, podName)
|
||||||
|
case kubecontainer.ErrImagePullBackOff.Error():
|
||||||
|
return kubecontainer.ContainerID{}, fmt.Errorf("container %q in pod %q is waiting to start: trying and failing to pull image", containerName, podName)
|
||||||
|
default:
|
||||||
|
return kubecontainer.ContainerID{}, fmt.Errorf("container %q in pod %q is waiting to start: %v", containerName, podName, reason)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// unrecognized state
|
||||||
|
return kubecontainer.ContainerID{}, fmt.Errorf("container %q in pod %q is waiting to start - no logs yet", containerName, podName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return kubecontainer.ParseContainerID(cID), nil
|
return kubecontainer.ParseContainerID(cID), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2466,7 +2485,7 @@ func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName string, lo
|
|||||||
|
|
||||||
pod, ok := kl.GetPodByName(namespace, name)
|
pod, ok := kl.GetPodByName(namespace, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unable to get logs for container %q in pod %q namespace %q: unable to find pod", containerName, name, namespace)
|
return fmt.Errorf("pod %q cannot be found - no logs available", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
podUID := pod.UID
|
podUID := pod.UID
|
||||||
@ -2481,15 +2500,9 @@ func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName string, lo
|
|||||||
podStatus = pod.Status
|
podStatus = pod.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := kl.validatePodPhase(&podStatus); err != nil {
|
containerID, err := kl.validateContainerLogStatus(pod.Name, &podStatus, containerName, logOptions.Previous)
|
||||||
// No log is available if pod is not in a "known" phase (e.g. Unknown).
|
|
||||||
return fmt.Errorf("Pod %q in namespace %q : %v", name, namespace, err)
|
|
||||||
}
|
|
||||||
containerID, err := kl.validateContainerStatus(&podStatus, containerName, logOptions.Previous)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// No log is available if the container status is missing or is in the
|
return err
|
||||||
// waiting state.
|
|
||||||
return fmt.Errorf("Pod %q in namespace %q: %v", name, namespace, err)
|
|
||||||
}
|
}
|
||||||
return kl.containerRuntime.GetContainerLogs(pod, containerID, logOptions, stdout, stderr)
|
return kl.containerRuntime.GetContainerLogs(pod, containerID, logOptions, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
@ -2399,33 +2399,7 @@ func TestPurgingObsoleteStatusMapEntries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatePodStatus(t *testing.T) {
|
func TestValidateContainerLogStatus(t *testing.T) {
|
||||||
testKubelet := newTestKubelet(t)
|
|
||||||
kubelet := testKubelet.kubelet
|
|
||||||
testCases := []struct {
|
|
||||||
podPhase api.PodPhase
|
|
||||||
success bool
|
|
||||||
}{
|
|
||||||
{api.PodRunning, true},
|
|
||||||
{api.PodSucceeded, true},
|
|
||||||
{api.PodFailed, true},
|
|
||||||
{api.PodPending, false},
|
|
||||||
{api.PodUnknown, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
err := kubelet.validatePodPhase(&api.PodStatus{Phase: tc.podPhase})
|
|
||||||
if tc.success {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("[case %d]: unexpected failure - %v", i, err)
|
|
||||||
}
|
|
||||||
} else if err == nil {
|
|
||||||
t.Errorf("[case %d]: unexpected success", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateContainerStatus(t *testing.T) {
|
|
||||||
testKubelet := newTestKubelet(t)
|
testKubelet := newTestKubelet(t)
|
||||||
kubelet := testKubelet.kubelet
|
kubelet := testKubelet.kubelet
|
||||||
containerName := "x"
|
containerName := "x"
|
||||||
@ -2447,6 +2421,17 @@ func TestValidateContainerStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
statuses: []api.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
State: api.ContainerState{
|
||||||
|
Running: &api.ContainerStateRunning{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statuses: []api.ContainerStatus{
|
statuses: []api.ContainerStatus{
|
||||||
{
|
{
|
||||||
@ -2469,10 +2454,28 @@ func TestValidateContainerStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
success: false,
|
success: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
statuses: []api.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ErrImagePull"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statuses: []api.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ErrImagePullBackOff"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
_, err := kubelet.validateContainerStatus(&api.PodStatus{
|
_, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
|
||||||
ContainerStatuses: tc.statuses,
|
ContainerStatuses: tc.statuses,
|
||||||
}, containerName, false)
|
}, containerName, false)
|
||||||
if tc.success {
|
if tc.success {
|
||||||
@ -2483,21 +2486,31 @@ func TestValidateContainerStatus(t *testing.T) {
|
|||||||
t.Errorf("[case %d]: unexpected success", i)
|
t.Errorf("[case %d]: unexpected success", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := kubelet.validateContainerStatus(&api.PodStatus{
|
if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
|
||||||
ContainerStatuses: testCases[0].statuses,
|
ContainerStatuses: testCases[0].statuses,
|
||||||
}, "blah", false); 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{
|
if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
|
||||||
ContainerStatuses: testCases[0].statuses,
|
ContainerStatuses: testCases[0].statuses,
|
||||||
}, containerName, true); err != nil {
|
}, containerName, true); err != nil {
|
||||||
t.Errorf("unexpected error with for previous terminated container - %v", err)
|
t.Errorf("unexpected error with for previous terminated container - %v", err)
|
||||||
}
|
}
|
||||||
if _, err := kubelet.validateContainerStatus(&api.PodStatus{
|
if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
|
||||||
|
ContainerStatuses: testCases[0].statuses,
|
||||||
|
}, containerName, false); err != nil {
|
||||||
|
t.Errorf("unexpected error with for most recent container - %v", err)
|
||||||
|
}
|
||||||
|
if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
|
||||||
ContainerStatuses: testCases[1].statuses,
|
ContainerStatuses: testCases[1].statuses,
|
||||||
}, containerName, true); err == nil {
|
}, containerName, true); err == nil {
|
||||||
t.Errorf("expected error with for previous terminated container")
|
t.Errorf("expected error with for previous terminated container")
|
||||||
}
|
}
|
||||||
|
if _, err := kubelet.validateContainerLogStatus("podName", &api.PodStatus{
|
||||||
|
ContainerStatuses: testCases[1].statuses,
|
||||||
|
}, containerName, false); err != nil {
|
||||||
|
t.Errorf("unexpected error with for most recent container")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateDiskSpacePolicy creates a new DiskSpaceManager with a new policy. This new manager along
|
// updateDiskSpacePolicy creates a new DiskSpaceManager with a new policy. This new manager along
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -416,7 +417,7 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
|||||||
|
|
||||||
pod, ok := s.host.GetPodByName(podNamespace, podID)
|
pod, ok := s.host.GetPodByName(podNamespace, podID)
|
||||||
if !ok {
|
if !ok {
|
||||||
response.WriteError(http.StatusNotFound, fmt.Errorf("Pod %q does not exist", podID))
|
response.WriteError(http.StatusNotFound, fmt.Errorf("pod %q does not exist\n", podID))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check if containerName is valid.
|
// Check if containerName is valid.
|
||||||
@ -427,12 +428,12 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !containerExists {
|
if !containerExists {
|
||||||
response.WriteError(http.StatusNotFound, fmt.Errorf("Container %q not found in Pod %q", containerName, podID))
|
response.WriteError(http.StatusNotFound, fmt.Errorf("container %q not found in pod %q\n", containerName, podID))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := response.ResponseWriter.(http.Flusher); !ok {
|
if _, ok := response.ResponseWriter.(http.Flusher); !ok {
|
||||||
response.WriteError(http.StatusInternalServerError, fmt.Errorf("unable to convert %v into http.Flusher", response))
|
response.WriteError(http.StatusInternalServerError, fmt.Errorf("unable to convert %v into http.Flusher, cannot show logs\n", reflect.TypeOf(response)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fw := flushwriter.Wrap(response.ResponseWriter)
|
fw := flushwriter.Wrap(response.ResponseWriter)
|
||||||
@ -440,10 +441,9 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
|||||||
fw = limitwriter.New(fw, *logOptions.LimitBytes)
|
fw = limitwriter.New(fw, *logOptions.LimitBytes)
|
||||||
}
|
}
|
||||||
response.Header().Set("Transfer-Encoding", "chunked")
|
response.Header().Set("Transfer-Encoding", "chunked")
|
||||||
response.WriteHeader(http.StatusOK)
|
|
||||||
if err := s.host.GetKubeletContainerLogs(kubecontainer.GetPodFullName(pod), containerName, logOptions, fw, fw); err != nil {
|
if err := s.host.GetKubeletContainerLogs(kubecontainer.GetPodFullName(pod), containerName, logOptions, fw, fw); err != nil {
|
||||||
if err != limitwriter.ErrMaximumWrite {
|
if err != limitwriter.ErrMaximumWrite {
|
||||||
response.WriteError(http.StatusInternalServerError, err)
|
response.WriteError(http.StatusBadRequest, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user