217 lines
8.0 KiB
Go
217 lines
8.0 KiB
Go
//go:build integration
|
|
|
|
package store
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/shcizo/package-updater/internal/master/model"
|
|
"github.com/shcizo/package-updater/internal/protocol"
|
|
)
|
|
|
|
const sluskRepo = "gitea.shcizo.se/shcizo/slusk"
|
|
|
|
// Digests of the fixture services; only well-formed ones are recorded as versions.
|
|
var (
|
|
digD1 = "sha256:" + strings.Repeat("d", 63) + "1"
|
|
digGitea = "sha256:" + strings.Repeat("a", 63) + "1"
|
|
digPg = "sha256:" + strings.Repeat("b", 63) + "1"
|
|
)
|
|
|
|
func versionsOf(t *testing.T, s *Store, repo string) []model.ImageVersion {
|
|
t.Helper()
|
|
got, err := s.RecentVersions(context.Background(), repo, 100)
|
|
require.NoError(t, err)
|
|
return got
|
|
}
|
|
|
|
func TestSetAvailable_RecordsVersion(t *testing.T) {
|
|
s := openTestStore(t)
|
|
ctx := context.Background()
|
|
_, svc := jobFixture(t, s, "a")
|
|
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: sluskRepo + ":1.5.0", Digest: "sha256:n1", Source: model.SourcePoll, At: t0}))
|
|
|
|
got, err := s.GetService(ctx, svc)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, sluskRepo+":1.5.0", got.AvailableRef)
|
|
assert.Equal(t, "sha256:n1", got.AvailableDigest)
|
|
assert.Equal(t, model.SourcePoll, got.AvailableSource)
|
|
vs := versionsOf(t, s, sluskRepo)
|
|
var found model.ImageVersion
|
|
for _, v := range vs {
|
|
if v.Digest == "sha256:n1" {
|
|
found = v
|
|
}
|
|
}
|
|
require.NotZero(t, found.ID)
|
|
assert.Equal(t, "1.5.0", found.Tag)
|
|
assert.Equal(t, model.SourcePoll, found.Source)
|
|
|
|
// Seeing the same version again keeps the first sighting.
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: sluskRepo + ":1.5.0", Digest: "sha256:n1", Source: model.SourceWebhook, At: t0.Add(time.Hour)}))
|
|
again, err := s.GetImageVersion(ctx, found.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, model.SourcePoll, again.Source)
|
|
assert.True(t, t0.Equal(again.FirstSeenAt))
|
|
assert.Len(t, versionsOf(t, s, sluskRepo), len(vs))
|
|
}
|
|
|
|
func TestSnapshotRecordsRunningVersions(t *testing.T) {
|
|
s := openTestStore(t)
|
|
ctx := context.Background()
|
|
id := newAgent(t, s, "node-2")
|
|
tagless := protocol.Service{Kind: protocol.KindSwarm, SwarmService: "g_g", Image: "gitea/gitea@" + digGitea, Digest: digGitea, Status: "running"}
|
|
bare := protocol.Service{Kind: protocol.KindContainer, ContainerName: "pg", Image: "postgres", Digest: digPg, Status: "running"}
|
|
nodigest := protocol.Service{Kind: protocol.KindContainer, ContainerName: "nd", Image: "nodigest:1", Status: "running"}
|
|
hello := protocol.Hello{Hostname: "node-2", SwarmManager: true, SwarmClusterID: "c1"}
|
|
svcs := []protocol.Service{slusk, tagless, bare, nodigest}
|
|
|
|
require.NoError(t, s.ApplySnapshot(ctx, id, hello, svcs, t0))
|
|
require.NoError(t, s.ApplySnapshot(ctx, id, hello, svcs, t0.Add(time.Hour)))
|
|
|
|
got := versionsOf(t, s, sluskRepo)
|
|
require.Len(t, got, 1)
|
|
assert.Equal(t, "1.4.2", got[0].Tag)
|
|
assert.Equal(t, digD1, got[0].Digest)
|
|
assert.Equal(t, model.SourceRunning, got[0].Source)
|
|
assert.True(t, t0.Equal(got[0].FirstSeenAt), "re-snapshot does not bump first_seen_at")
|
|
tl := versionsOf(t, s, "gitea/gitea")
|
|
require.Len(t, tl, 1)
|
|
assert.Equal(t, "", tl[0].Tag)
|
|
assert.Equal(t, digGitea, tl[0].Digest)
|
|
b := versionsOf(t, s, "postgres")
|
|
require.Len(t, b, 1)
|
|
assert.Equal(t, "latest", b[0].Tag, "a bare image runs latest, like the followed ref")
|
|
assert.Empty(t, versionsOf(t, s, "nodigest"))
|
|
}
|
|
|
|
func TestSnapshotRecordsOnlyWellFormedDigests(t *testing.T) {
|
|
s := openTestStore(t)
|
|
id := newAgent(t, s, "node-2")
|
|
bad := protocol.Service{Kind: protocol.KindContainer, ContainerName: "bad", Image: "bad:1", Digest: "sha256:short", Status: "running"}
|
|
upper := protocol.Service{Kind: protocol.KindContainer, ContainerName: "up", Image: "up:1", Digest: "sha256:" + strings.Repeat("A", 64), Status: "running"}
|
|
|
|
require.NoError(t, s.ApplySnapshot(context.Background(), id, protocol.Hello{Hostname: "node-2"}, []protocol.Service{bad, upper}, t0))
|
|
|
|
assert.Empty(t, versionsOf(t, s, "bad"))
|
|
assert.Empty(t, versionsOf(t, s, "up"))
|
|
}
|
|
|
|
func TestRecentVersions(t *testing.T) {
|
|
ctx := context.Background()
|
|
t.Run("order, limit and repo filter", func(t *testing.T) {
|
|
s := openTestStore(t)
|
|
_, svc := jobFixture(t, s, "a")
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: "other/repo:x", Digest: "sha256:ox", Source: model.SourcePoll, At: t0}))
|
|
// 12 versions of the service's repo, one per minute.
|
|
for i := 0; i < 12; i++ {
|
|
tag := "t" + string(rune('a'+i))
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: sluskRepo + ":" + tag, Digest: "sha256:" + tag, Source: model.SourcePoll, At: t0.Add(time.Duration(i+1) * time.Minute)}))
|
|
}
|
|
|
|
got, err := s.RecentVersions(ctx, sluskRepo, 10)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, got, 10)
|
|
assert.Equal(t, "tl", got[0].Tag, "newest first")
|
|
assert.Equal(t, "tc", got[9].Tag)
|
|
for _, v := range got {
|
|
assert.Equal(t, sluskRepo, v.Repo)
|
|
}
|
|
})
|
|
|
|
t.Run("digest-less row is hidden once its tag has a digest", func(t *testing.T) {
|
|
s := openTestStore(t)
|
|
_, svc := jobFixture(t, s, "a")
|
|
add := func(tag, digest string, at time.Time) {
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: sluskRepo + ":" + tag, Digest: digest, Source: model.SourceWebhook, At: at}))
|
|
}
|
|
add("main", "", t0.Add(time.Minute))
|
|
add("solo", "", t0.Add(2*time.Minute))
|
|
require.Len(t, versionsOf(t, s, sluskRepo), 3, "running 1.4.2 plus two digest-less rows")
|
|
|
|
add("main", "sha256:m1", t0.Add(3*time.Minute))
|
|
|
|
var refs []string
|
|
for _, v := range versionsOf(t, s, sluskRepo) {
|
|
refs = append(refs, v.Ref())
|
|
}
|
|
assert.Equal(t, []string{sluskRepo + ":main@sha256:m1", sluskRepo + ":solo", sluskRepo + ":1.4.2@" + digD1}, refs)
|
|
})
|
|
|
|
t.Run("digest-less row newer than the digest row of its tag is shown", func(t *testing.T) {
|
|
s := openTestStore(t)
|
|
_, svc := jobFixture(t, s, "a")
|
|
add := func(tag, digest string, at time.Time) {
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: sluskRepo + ":" + tag, Digest: digest, Source: model.SourceWebhook, At: at}))
|
|
}
|
|
add("main", "sha256:m1", t0.Add(time.Minute))
|
|
add("main", "", t0.Add(2*time.Minute))
|
|
|
|
var refs []string
|
|
for _, v := range versionsOf(t, s, sluskRepo) {
|
|
refs = append(refs, v.Ref())
|
|
}
|
|
assert.Equal(t, []string{sluskRepo + ":main", sluskRepo + ":main@sha256:m1", sluskRepo + ":1.4.2@" + digD1}, refs)
|
|
})
|
|
}
|
|
|
|
func TestGetImageVersion_NotFound(t *testing.T) {
|
|
s := openTestStore(t)
|
|
|
|
_, err := s.GetImageVersion(context.Background(), 99999)
|
|
|
|
assert.ErrorIs(t, err, model.ErrNotFound)
|
|
}
|
|
|
|
func TestSetPollError(t *testing.T) {
|
|
s := openTestStore(t)
|
|
ctx := context.Background()
|
|
agent, svc := jobFixture(t, s, "a")
|
|
require.NoError(t, s.SetAvailable(ctx, svc, model.Available{Ref: sluskRepo + ":1.5.0", Digest: "sha256:n1", Source: model.SourcePoll, At: t0}))
|
|
|
|
require.NoError(t, s.SetPollError(ctx, []int64{svc}, "boom"))
|
|
require.NoError(t, s.SetPollError(ctx, nil, "ignored"))
|
|
got, err := s.GetService(ctx, svc)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "boom", got.LastPollError)
|
|
|
|
require.NoError(t, s.UpsertService(ctx, agent, "", slusk, t0.Add(time.Hour)))
|
|
got, err = s.GetService(ctx, svc)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "boom", got.LastPollError, "inventory writes do not touch the poll error")
|
|
assert.Equal(t, "sha256:n1", got.AvailableDigest, "nor the suggestion")
|
|
|
|
require.NoError(t, s.SetPollError(ctx, []int64{svc}, ""))
|
|
got, err = s.GetService(ctx, svc)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, got.LastPollError)
|
|
}
|
|
|
|
func TestSetLastTagDigest(t *testing.T) {
|
|
s := openTestStore(t)
|
|
ctx := context.Background()
|
|
agent, svc := jobFixture(t, s, "a")
|
|
got, err := s.GetService(ctx, svc)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, got.LastTagDigest)
|
|
|
|
require.NoError(t, s.SetLastTagDigest(ctx, svc, "sha256:n1"))
|
|
require.NoError(t, s.UpsertService(ctx, agent, "", slusk, t0.Add(time.Hour)))
|
|
|
|
got, err = s.GetService(ctx, svc)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sha256:n1", got.LastTagDigest, "inventory writes do not touch it")
|
|
list, err := s.ListServices(ctx)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 1)
|
|
assert.Equal(t, "sha256:n1", list[0].LastTagDigest)
|
|
}
|