a8524e5ff4
Verified the Docker SDK v28.5.2+incompatible ServiceList signature via go doc before implementing: the options type is swarm.ServiceListOptions, not types.ServiceListOptions as the brief assumed. Everything else (Service.ID, Service.Spec via embedded Annotations for Name/Labels, TaskTemplate.ContainerSpec.Image) matched the brief exactly. Reuses discovery.ImagesMatch and discovery.HasOptIn rather than duplicating matching/opt-in logic. Opt-in label is read from the service spec's own labels since Swarm services have no local compose file to anchor a STACKS_ROOT path check against (unlike Compose mode).
87 lines
2.7 KiB
Go
87 lines
2.7 KiB
Go
package discovery_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/shcizo/package-updater/internal/discovery"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type fakeSwarmDockerClient struct {
|
|
services []swarm.Service
|
|
err error
|
|
}
|
|
|
|
func (f *fakeSwarmDockerClient) ServiceList(_ context.Context, _ swarm.ServiceListOptions) ([]swarm.Service, error) {
|
|
return f.services, f.err
|
|
}
|
|
|
|
func mkService(id, name, image string, labels map[string]string) swarm.Service {
|
|
return swarm.Service{
|
|
ID: id,
|
|
Spec: swarm.ServiceSpec{
|
|
Annotations: swarm.Annotations{Name: name, Labels: labels},
|
|
TaskTemplate: swarm.TaskSpec{
|
|
ContainerSpec: &swarm.ContainerSpec{Image: image},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestSwarmFindJobs_MatchAndOptIn(t *testing.T) {
|
|
fake := &fakeSwarmDockerClient{services: []swarm.Service{
|
|
mkService("svc-myapp", "myapp_web", "registry.example.com/myapp:v1", map[string]string{
|
|
"se.shcizo.auto-update": "true",
|
|
}),
|
|
mkService("svc-other", "other_web", "registry.example.com/other:v1", map[string]string{
|
|
"se.shcizo.auto-update": "true",
|
|
}),
|
|
}}
|
|
d := discovery.NewSwarm(fake, "se.shcizo.auto-update")
|
|
jobs, err := d.FindJobs(context.Background(), "registry.example.com/myapp")
|
|
require.NoError(t, err)
|
|
require.Len(t, jobs, 1)
|
|
require.Equal(t, "myapp_web", jobs[0].Service)
|
|
require.Equal(t, "svc-myapp", jobs[0].ServiceID)
|
|
require.Equal(t, "registry.example.com/myapp", jobs[0].Image)
|
|
require.False(t, jobs[0].Refused)
|
|
require.Empty(t, jobs[0].WorkingDir)
|
|
require.Empty(t, jobs[0].ConfigFiles)
|
|
}
|
|
|
|
func TestSwarmFindJobs_SkipsWithoutOptIn(t *testing.T) {
|
|
fake := &fakeSwarmDockerClient{services: []swarm.Service{
|
|
mkService("svc-myapp", "myapp_web", "registry.example.com/myapp:v1", nil),
|
|
}}
|
|
d := discovery.NewSwarm(fake, "se.shcizo.auto-update")
|
|
jobs, err := d.FindJobs(context.Background(), "registry.example.com/myapp")
|
|
require.NoError(t, err)
|
|
require.Empty(t, jobs)
|
|
}
|
|
|
|
func TestSwarmFindJobs_DockerError(t *testing.T) {
|
|
fake := &fakeSwarmDockerClient{err: errors.New("connection refused")}
|
|
d := discovery.NewSwarm(fake, "se.shcizo.auto-update")
|
|
_, err := d.FindJobs(context.Background(), "registry.example.com/myapp")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSwarmFindJobs_NoContainerSpecIsSkipped(t *testing.T) {
|
|
fake := &fakeSwarmDockerClient{services: []swarm.Service{
|
|
{
|
|
ID: "svc-weird",
|
|
Spec: swarm.ServiceSpec{
|
|
Annotations: swarm.Annotations{Name: "weird", Labels: map[string]string{"se.shcizo.auto-update": "true"}},
|
|
TaskTemplate: swarm.TaskSpec{ContainerSpec: nil},
|
|
},
|
|
},
|
|
}}
|
|
d := discovery.NewSwarm(fake, "se.shcizo.auto-update")
|
|
jobs, err := d.FindJobs(context.Background(), "registry.example.com/myapp")
|
|
require.NoError(t, err)
|
|
require.Empty(t, jobs)
|
|
}
|