mirror of https://github.com/portainer/portainer
fix(container): apply less accurate solution to calculate container status for swarm environment [BE-12256] (#1225)
parent
eedf37d18a
commit
666d51482e
|
@ -3,6 +3,7 @@ package stats
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
@ -20,7 +21,11 @@ type DockerClient interface {
|
||||||
ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error)
|
ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateContainerStats(ctx context.Context, cli DockerClient, containers []container.Summary) (ContainerStats, error) {
|
func CalculateContainerStats(ctx context.Context, cli DockerClient, isSwarm bool, containers []container.Summary) (ContainerStats, error) {
|
||||||
|
if isSwarm {
|
||||||
|
return CalculateContainerStatsForSwarm(containers), nil
|
||||||
|
}
|
||||||
|
|
||||||
var running, stopped, healthy, unhealthy int
|
var running, stopped, healthy, unhealthy int
|
||||||
|
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
|
@ -90,3 +95,31 @@ func getContainerStatus(state *container.State) ContainerStats {
|
||||||
|
|
||||||
return stat
|
return stat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a temporary workaround to calculate container stats for Swarm
|
||||||
|
// TODO: Remove this once we have a proper way to calculate container stats for Swarm
|
||||||
|
func CalculateContainerStatsForSwarm(containers []container.Summary) ContainerStats {
|
||||||
|
var running, stopped, healthy, unhealthy int
|
||||||
|
for _, container := range containers {
|
||||||
|
switch container.State {
|
||||||
|
case "running":
|
||||||
|
running++
|
||||||
|
case "exited", "stopped":
|
||||||
|
stopped++
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(container.Status, "(healthy)") {
|
||||||
|
healthy++
|
||||||
|
} else if strings.Contains(container.Status, "(unhealthy)") {
|
||||||
|
unhealthy++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContainerStats{
|
||||||
|
Running: running,
|
||||||
|
Stopped: stopped,
|
||||||
|
Healthy: healthy,
|
||||||
|
Unhealthy: unhealthy,
|
||||||
|
Total: len(containers),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ func TestCalculateContainerStats(t *testing.T) {
|
||||||
|
|
||||||
// Call the function and measure time
|
// Call the function and measure time
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
stats, err := CalculateContainerStats(context.Background(), mockClient, containers)
|
stats, err := CalculateContainerStats(context.Background(), mockClient, false, containers)
|
||||||
require.NoError(t, err, "failed to calculate container stats")
|
require.NoError(t, err, "failed to calculate container stats")
|
||||||
duration := time.Since(startTime)
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ func TestCalculateContainerStatsAllErrors(t *testing.T) {
|
||||||
mockClient.On("ContainerInspect", mock.Anything, "container2").Return(container.InspectResponse{}, errors.New("permission denied"))
|
mockClient.On("ContainerInspect", mock.Anything, "container2").Return(container.InspectResponse{}, errors.New("permission denied"))
|
||||||
|
|
||||||
// Call the function
|
// Call the function
|
||||||
stats, err := CalculateContainerStats(context.Background(), mockClient, containers)
|
stats, err := CalculateContainerStats(context.Background(), mockClient, false, containers)
|
||||||
|
|
||||||
// Assert that an error was returned
|
// Assert that an error was returned
|
||||||
require.Error(t, err, "should return error when all containers fail to inspect")
|
require.Error(t, err, "should return error when all containers fail to inspect")
|
||||||
|
@ -232,3 +232,22 @@ func TestGetContainerStatus(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCalculateContainerStatsForSwarm(t *testing.T) {
|
||||||
|
containers := []container.Summary{
|
||||||
|
{State: "running"},
|
||||||
|
{State: "running", Status: "Up 5 minutes (healthy)"},
|
||||||
|
{State: "exited"},
|
||||||
|
{State: "stopped"},
|
||||||
|
{State: "running", Status: "Up 10 minutes"},
|
||||||
|
{State: "running", Status: "Up about an hour (unhealthy)"},
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := CalculateContainerStatsForSwarm(containers)
|
||||||
|
|
||||||
|
assert.Equal(t, 4, stats.Running)
|
||||||
|
assert.Equal(t, 2, stats.Stopped)
|
||||||
|
assert.Equal(t, 1, stats.Healthy)
|
||||||
|
assert.Equal(t, 1, stats.Unhealthy)
|
||||||
|
assert.Equal(t, 6, stats.Total)
|
||||||
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ func (h *Handler) dashboard(w http.ResponseWriter, r *http.Request) *httperror.H
|
||||||
stackCount = len(stacks)
|
stackCount = len(stacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
containersStats, err := stats.CalculateContainerStats(r.Context(), cli, containers)
|
containersStats, err := stats.CalculateContainerStats(r.Context(), cli, info.Swarm.ControlAvailable, containers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httperror.InternalServerError("Unable to retrieve Docker containers stats", err)
|
return httperror.InternalServerError("Unable to retrieve Docker containers stats", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,16 +209,16 @@ func dockerSnapshotContainers(snapshot *portainer.DockerSnapshot, cli *client.Cl
|
||||||
snapshot.GpuUseAll = gpuUseAll
|
snapshot.GpuUseAll = gpuUseAll
|
||||||
snapshot.GpuUseList = gpuUseList
|
snapshot.GpuUseList = gpuUseList
|
||||||
|
|
||||||
stats, err := stats.CalculateContainerStats(ctx, cli, containers)
|
result, err := stats.CalculateContainerStats(ctx, cli, snapshot.Swarm, containers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to calculate container stats: %w", err)
|
return fmt.Errorf("failed to calculate container stats: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot.ContainerCount = stats.Total
|
snapshot.ContainerCount = result.Total
|
||||||
snapshot.RunningContainerCount = stats.Running
|
snapshot.RunningContainerCount = result.Running
|
||||||
snapshot.StoppedContainerCount = stats.Stopped
|
snapshot.StoppedContainerCount = result.Stopped
|
||||||
snapshot.HealthyContainerCount = stats.Healthy
|
snapshot.HealthyContainerCount = result.Healthy
|
||||||
snapshot.UnhealthyContainerCount = stats.Unhealthy
|
snapshot.UnhealthyContainerCount = result.Unhealthy
|
||||||
snapshot.StackCount += len(stacks)
|
snapshot.StackCount += len(stacks)
|
||||||
|
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
|
|
Loading…
Reference in New Issue