mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 13:29:41 +02:00
fix(update-schedules): display enriched error logs for agent updates [BE-11756] (#693)
This commit is contained in:
parent
bc29419c17
commit
3b05505527
1 changed files with 65 additions and 12 deletions
|
@ -1,14 +1,19 @@
|
||||||
package compose
|
package compose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/portainer/portainer/pkg/libstack"
|
"github.com/portainer/portainer/pkg/libstack"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/v2/types"
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +40,7 @@ type service struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// docker container state can be one of "created", "running", "paused", "restarting", "removing", "exited", or "dead"
|
// docker container state can be one of "created", "running", "paused", "restarting", "removing", "exited", or "dead"
|
||||||
func getServiceStatus(service service) (libstack.Status, string) {
|
func getServiceStatus(ctx context.Context, service service) (libstack.Status, string) {
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("service", service.Name).
|
Str("service", service.Name).
|
||||||
Str("state", service.State).
|
Str("state", service.State).
|
||||||
|
@ -50,22 +55,70 @@ func getServiceStatus(service service) (libstack.Status, string) {
|
||||||
case "removing":
|
case "removing":
|
||||||
return libstack.StatusRemoving, ""
|
return libstack.StatusRemoving, ""
|
||||||
case "exited":
|
case "exited":
|
||||||
if service.ExitCode != 0 {
|
if service.ExitCode == 0 {
|
||||||
return libstack.StatusError, fmt.Sprintf("service %s exited with code %d", service.Name, service.ExitCode)
|
return libstack.StatusCompleted, ""
|
||||||
}
|
|
||||||
return libstack.StatusCompleted, ""
|
|
||||||
case "dead":
|
|
||||||
if service.ExitCode != 0 {
|
|
||||||
return libstack.StatusError, fmt.Sprintf("service %s exited with code %d", service.Name, service.ExitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return libstack.StatusRemoved, ""
|
errorMessage, err := getContainerLogsTail(ctx, service)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("service", service.Name).
|
||||||
|
Msg("failed to get logs from container")
|
||||||
|
errorMessage = fmt.Sprintf("service %s exited with code %d", service.Name, service.ExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return libstack.StatusError, errorMessage
|
||||||
|
case "dead":
|
||||||
|
if service.ExitCode == 0 {
|
||||||
|
return libstack.StatusRemoved, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessage, err := getContainerLogsTail(ctx, service)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("service", service.Name).
|
||||||
|
Msg("failed to get logs from container")
|
||||||
|
errorMessage = fmt.Sprintf("service %s exited with code %d", service.Name, service.ExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return libstack.StatusError, errorMessage
|
||||||
default:
|
default:
|
||||||
return libstack.StatusUnknown, ""
|
return libstack.StatusUnknown, ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func aggregateStatuses(services []service) (libstack.Status, string) {
|
func getContainerLogsTail(ctx context.Context, service service) (string, error) {
|
||||||
|
var combinedOutput bytes.Buffer
|
||||||
|
|
||||||
|
if err := withCli(ctx, libstack.Options{ProjectName: service.Project}, func(ctx context.Context, cli *command.DockerCli) error {
|
||||||
|
out, err := cli.Client().ContainerLogs(ctx, service.Name, container.LogsOptions{
|
||||||
|
ShowStdout: true,
|
||||||
|
ShowStderr: true,
|
||||||
|
Timestamps: true,
|
||||||
|
Follow: false,
|
||||||
|
Tail: "20",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to get logs from container")
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(&combinedOutput, out)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to read container logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return "", errors.Wrap(err, "unable to get logs from container")
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedOutput.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func aggregateStatuses(ctx context.Context, services []service) (libstack.Status, string) {
|
||||||
servicesCount := len(services)
|
servicesCount := len(services)
|
||||||
|
|
||||||
if servicesCount == 0 {
|
if servicesCount == 0 {
|
||||||
|
@ -78,7 +131,7 @@ func aggregateStatuses(services []service) (libstack.Status, string) {
|
||||||
statusCounts := make(map[libstack.Status]int)
|
statusCounts := make(map[libstack.Status]int)
|
||||||
errorMessage := ""
|
errorMessage := ""
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
status, serviceError := getServiceStatus(service)
|
status, serviceError := getServiceStatus(ctx, service)
|
||||||
if serviceError != "" {
|
if serviceError != "" {
|
||||||
errorMessage = serviceError
|
errorMessage = serviceError
|
||||||
}
|
}
|
||||||
|
@ -148,7 +201,7 @@ func (c *ComposeDeployer) WaitForStatus(ctx context.Context, name string, status
|
||||||
return waitResult
|
return waitResult
|
||||||
}
|
}
|
||||||
|
|
||||||
aggregateStatus, errorMessage := aggregateStatuses(services)
|
aggregateStatus, errorMessage := aggregateStatuses(ctx, services)
|
||||||
if aggregateStatus == status {
|
if aggregateStatus == status {
|
||||||
return waitResult
|
return waitResult
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue