fix: stream logs for non-tty dokku run on scheduler-k3s
For short-lived commands the run pod can transition Running to Succeeded between the running-pod check and the kubectl exec SPDY upgrade, leaving the upgrade to fail with `container not found`. When stdout is not a TTY (and DOKKU_FORCE_TTY is not set) the exec attach is only being used to capture stdout, so stream the run pod logs in follow mode instead. The pod's `TTLSecondsAfterFinished` of 60s keeps the kubelet's log file readable for the duration of the call, eliminating the race.
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ryanuber/columnize"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/kubectl/pkg/util/term"
|
||||
"k8s.io/kubernetes/pkg/client/conditions"
|
||||
)
|
||||
|
||||
@@ -1456,6 +1457,12 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
|
||||
attachToPod := os.Getenv("DOKKU_DETACH_CONTAINER") != "1"
|
||||
|
||||
forceTTY := common.ToBool(os.Getenv("DOKKU_FORCE_TTY"))
|
||||
disableTTY := common.ToBool(os.Getenv("DOKKU_DISABLE_TTY"))
|
||||
ttyProbe := term.TTY{Out: os.Stdout}
|
||||
hasTTY := ttyProbe.MonitorSize(ttyProbe.GetSize()) != nil
|
||||
streamLogsOnly := attachToPod && !forceTTY && (disableTTY || !hasTTY)
|
||||
|
||||
imagePullSecrets := getComputedImagePullSecrets(appName)
|
||||
if imagePullSecrets == "" {
|
||||
imagePullSecrets = GetImagePullSecretName(appName)
|
||||
@@ -1568,6 +1575,45 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
Timeout: 10,
|
||||
Waiter: isPodReady,
|
||||
})
|
||||
if streamLogsOnly && (err == nil || errors.Is(err, conditions.ErrPodCompleted)) {
|
||||
if streamErr := clientset.StreamLogs(ctx, StreamLogsInput{
|
||||
Follow: true,
|
||||
LabelSelector: []string{batchJobSelector},
|
||||
Namespace: namespace,
|
||||
Quiet: true,
|
||||
}); streamErr != nil {
|
||||
return fmt.Errorf("Error streaming logs: %w", streamErr)
|
||||
}
|
||||
|
||||
pods, listErr := clientset.ListPods(ctx, ListPodsInput{
|
||||
Namespace: namespace,
|
||||
LabelSelector: batchJobSelector,
|
||||
})
|
||||
if listErr != nil {
|
||||
return fmt.Errorf("Error getting pod: %w", listErr)
|
||||
}
|
||||
if len(pods) == 0 {
|
||||
return fmt.Errorf("No pods found matching specified labels")
|
||||
}
|
||||
selectedPod := pods[0]
|
||||
switch selectedPod.Status.Phase {
|
||||
case v1.PodSucceeded:
|
||||
return nil
|
||||
case v1.PodFailed:
|
||||
for _, status := range selectedPod.Status.ContainerStatuses {
|
||||
if status.Name != fmt.Sprintf("%s-%s", appName, processType) {
|
||||
continue
|
||||
}
|
||||
if status.State.Terminated == nil {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("Unable to attach as the pod has already exited with a failed exit code: %s", status.State.Terminated.Message)
|
||||
}
|
||||
return fmt.Errorf("Unable to attach as the pod has already exited with a failed exit code")
|
||||
default:
|
||||
return fmt.Errorf("Unable to attach as the pod is in an unknown state: %s", selectedPod.Status.Phase)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, conditions.ErrPodCompleted) {
|
||||
pods, podErr := clientset.ListPods(ctx, ListPodsInput{
|
||||
|
||||
Reference in New Issue
Block a user