171 lines
5.9 KiB
Go
171 lines
5.9 KiB
Go
package inventory
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/image"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/docker/docker/api/types/system"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/shcizo/package-updater/internal/protocol"
|
|
)
|
|
|
|
type fakeDocker struct {
|
|
containers []container.Summary
|
|
images []image.Summary
|
|
services []swarm.Service
|
|
info system.Info
|
|
containerErr error
|
|
serviceListCalls int
|
|
}
|
|
|
|
func (f *fakeDocker) ContainerList(context.Context, container.ListOptions) ([]container.Summary, error) {
|
|
return f.containers, f.containerErr
|
|
}
|
|
|
|
func (f *fakeDocker) ImageList(context.Context, image.ListOptions) ([]image.Summary, error) {
|
|
return f.images, nil
|
|
}
|
|
|
|
func (f *fakeDocker) ServiceList(context.Context, swarm.ServiceListOptions) ([]swarm.Service, error) {
|
|
f.serviceListCalls++
|
|
return f.services, nil
|
|
}
|
|
|
|
func (f *fakeDocker) Info(context.Context) (system.Info, error) { return f.info, nil }
|
|
|
|
func composeContainer(id, project, service, img, imageID, state string) container.Summary {
|
|
return container.Summary{
|
|
ID: id, Image: img, ImageID: imageID, State: state, Names: []string{"/" + project + "-" + service + "-1"},
|
|
Labels: map[string]string{
|
|
labelProject: project,
|
|
labelService: service,
|
|
labelWorkingDir: "/home/shcizo/self-hosted/" + project,
|
|
},
|
|
}
|
|
}
|
|
|
|
func managerInfo() system.Info {
|
|
var info system.Info
|
|
info.Name = "swarm-mgr"
|
|
info.Swarm.ControlAvailable = true
|
|
info.Swarm.Cluster = &swarm.ClusterInfo{ID: "cluster-1"}
|
|
return info
|
|
}
|
|
|
|
func TestSnapshot_ComposeContainerWithRepoDigest(t *testing.T) {
|
|
cli := &fakeDocker{
|
|
containers: []container.Summary{composeContainer("abc123", "slusk", "web", "gitea.shcizo.se/shcizo/slusk:1.4.2", "sha256:img1", "running")},
|
|
images: []image.Summary{{ID: "sha256:img1", RepoDigests: []string{"gitea.shcizo.se/shcizo/slusk@sha256:d1"}}},
|
|
}
|
|
got, err := New(cli, "").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []protocol.Service{{
|
|
Kind: protocol.KindCompose, ComposeProject: "slusk", ComposeService: "web",
|
|
WorkingDir: "/home/shcizo/self-hosted/slusk",
|
|
Image: "gitea.shcizo.se/shcizo/slusk:1.4.2", Digest: "sha256:d1", Status: "running",
|
|
}}, got)
|
|
}
|
|
|
|
func TestSnapshot_ReplicasMergeAndRunningWins(t *testing.T) {
|
|
cli := &fakeDocker{containers: []container.Summary{
|
|
composeContainer("a1", "slusk", "worker", "slusk:1", "sha256:i", "exited"),
|
|
composeContainer("a2", "slusk", "worker", "slusk:1", "sha256:i", "running"),
|
|
}}
|
|
got, err := New(cli, "").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, got, 1)
|
|
assert.Equal(t, "running", got[0].Status)
|
|
}
|
|
|
|
func TestSnapshot_PlainContainerAndSwarmTaskSkipped(t *testing.T) {
|
|
cli := &fakeDocker{containers: []container.Summary{
|
|
{ID: "p1", Names: []string{"/adhoc"}, Image: "redis:alpine", State: "exited"},
|
|
{ID: "t1", Names: []string{"/gitea_gitea.1.x"}, Image: "gitea/gitea:1.27.3", State: "running",
|
|
Labels: map[string]string{labelSwarmTask: "svc1"}},
|
|
}}
|
|
got, err := New(cli, "").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []protocol.Service{{
|
|
Kind: protocol.KindContainer, ContainerName: "adhoc", Image: "redis:alpine", Status: "stopped",
|
|
}}, got)
|
|
}
|
|
|
|
func TestSnapshot_SelfFlag(t *testing.T) {
|
|
cli := &fakeDocker{containers: []container.Summary{
|
|
composeContainer("0123456789abcdef", "updater-agent", "agent", "pu:1", "sha256:i", "running"),
|
|
}}
|
|
got, err := New(cli, "0123456789ab").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, got, 1)
|
|
assert.True(t, got[0].Self)
|
|
}
|
|
|
|
func TestSnapshot_SwarmServicesOnlyOnManager(t *testing.T) {
|
|
svc := swarm.Service{
|
|
Spec: swarm.ServiceSpec{
|
|
Annotations: swarm.Annotations{Name: "gitea_gitea"},
|
|
TaskTemplate: swarm.TaskSpec{ContainerSpec: &swarm.ContainerSpec{Image: "gitea/gitea:1.27.3@sha256:87a6"}},
|
|
},
|
|
ServiceStatus: &swarm.ServiceStatus{RunningTasks: 1, DesiredTasks: 1},
|
|
}
|
|
|
|
worker := &fakeDocker{services: []swarm.Service{svc}}
|
|
got, err := New(worker, "").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Empty(t, got)
|
|
assert.Equal(t, 0, worker.serviceListCalls, "workers must not call the manager-only API")
|
|
|
|
manager := &fakeDocker{services: []swarm.Service{svc}, info: managerInfo()}
|
|
got, err = New(manager, "").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []protocol.Service{{
|
|
Kind: protocol.KindSwarm, SwarmService: "gitea_gitea",
|
|
Image: "gitea/gitea:1.27.3@sha256:87a6", Digest: "sha256:87a6", Status: "running",
|
|
}}, got)
|
|
}
|
|
|
|
func TestSwarmStatus(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in *swarm.ServiceStatus
|
|
want string
|
|
}{
|
|
{"no status", nil, "unknown"},
|
|
{"scaled to zero", &swarm.ServiceStatus{DesiredTasks: 0}, "stopped"},
|
|
{"all running", &swarm.ServiceStatus{RunningTasks: 2, DesiredTasks: 2}, "running"},
|
|
{"some running", &swarm.ServiceStatus{RunningTasks: 1, DesiredTasks: 2}, "degraded"},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) { assert.Equal(t, c.want, swarmStatus(c.in)) })
|
|
}
|
|
}
|
|
|
|
func TestSnapshot_SortedByKey(t *testing.T) {
|
|
cli := &fakeDocker{containers: []container.Summary{
|
|
{ID: "2", Names: []string{"/zeta"}, Image: "z", State: "running"},
|
|
{ID: "1", Names: []string{"/alpha"}, Image: "a", State: "running"},
|
|
}}
|
|
got, err := New(cli, "").Snapshot(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, got, 2)
|
|
assert.Equal(t, "alpha", got[0].ContainerName)
|
|
}
|
|
|
|
func TestSnapshot_PropagatesDockerErrors(t *testing.T) {
|
|
cli := &fakeDocker{containerErr: errors.New("daemon down")}
|
|
_, err := New(cli, "").Snapshot(context.Background())
|
|
assert.ErrorContains(t, err, "daemon down")
|
|
}
|
|
|
|
func TestHello(t *testing.T) {
|
|
got, err := New(&fakeDocker{info: managerInfo()}, "").Hello(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, protocol.Hello{Hostname: "swarm-mgr", SwarmManager: true, SwarmClusterID: "cluster-1"}, got)
|
|
}
|