Channels are a concept similar to job types. Callers must specify a channel name when queueing a new job. A list of channels is also specified when dequeueing a job. The dequeued job's channel will always be from one of the specified channel. Of course, the job types are also respected. The dequeued job will also always be from one of the specified type. Currently, all calls to jobqueue were changed so all queue operations use an empty channel name and all dequeue operations use a list containing an empty channel. Thus, this is a non-functional change. Signed-off-by: Ondřej Budai <ondrej@budai.cz>
220 lines
6.8 KiB
Go
220 lines
6.8 KiB
Go
// +build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v4"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/jobqueue"
|
|
"github.com/osbuild/osbuild-composer/internal/jobqueue/dbjobqueue"
|
|
"github.com/osbuild/osbuild-composer/internal/jobqueue/jobqueuetest"
|
|
)
|
|
|
|
const url = "postgres://postgres:foobar@localhost:5432/osbuildcomposer"
|
|
|
|
func TestJobQueueInterface(t *testing.T) {
|
|
makeJobQueue := func() (jobqueue.JobQueue, func(), error) {
|
|
// clear db before each run
|
|
conn, err := pgx.Connect(context.Background(), url)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer conn.Close(context.Background())
|
|
for _, table := range []string{"job_dependencies", "heartbeats", "jobs"} {
|
|
_, err = conn.Exec(context.Background(), fmt.Sprintf("DELETE FROM %s", table))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
err = conn.Close(context.Background())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
q, err := dbjobqueue.New(url)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
stop := func() {
|
|
q.Close()
|
|
}
|
|
return q, stop, nil
|
|
}
|
|
|
|
jobqueuetest.TestJobQueue(t, makeJobQueue)
|
|
|
|
wrap := func(f func(t *testing.T, q *dbjobqueue.DBJobQueue)) func(*testing.T) {
|
|
q, stop, err := makeJobQueue()
|
|
require.NoError(t, err)
|
|
return func(t *testing.T) {
|
|
defer stop() // use defer because f() might call testing.T.FailNow()
|
|
dbq, ok := q.(*dbjobqueue.DBJobQueue)
|
|
require.True(t, ok)
|
|
f(t, dbq)
|
|
}
|
|
}
|
|
|
|
t.Run("maintenance-query-jobs-before", wrap(testJobsUptoByType))
|
|
t.Run("maintenance-delete-job-and-dependencies", wrap(testDeleteJobAndDependencies))
|
|
}
|
|
|
|
func setFinishedAt(t *testing.T, q *dbjobqueue.DBJobQueue, id uuid.UUID, finished time.Time) {
|
|
conn, err := pgx.Connect(context.Background(), url)
|
|
require.NoError(t, err)
|
|
defer conn.Close(context.Background())
|
|
|
|
started := finished.Add(-time.Second)
|
|
queued := started.Add(-time.Second)
|
|
|
|
_, err = conn.Exec(context.Background(), "UPDATE jobs SET queued_at = $1, started_at = $2, finished_at = $3 WHERE id = $4", queued, started, finished, id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testJobsUptoByType(t *testing.T, q *dbjobqueue.DBJobQueue) {
|
|
date80 := time.Date(1980, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
date85 := time.Date(1985, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
date90 := time.Date(1990, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
id80, err := q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id80)
|
|
_, _, _, _, _, err = q.Dequeue(context.Background(), []string{"octopus"}, []string{""})
|
|
require.NoError(t, err)
|
|
err = q.FinishJob(id80, nil)
|
|
require.NoError(t, err)
|
|
setFinishedAt(t, q, id80, date80)
|
|
|
|
id85, err := q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id85)
|
|
_, _, _, _, _, err = q.Dequeue(context.Background(), []string{"octopus"}, []string{""})
|
|
require.NoError(t, err)
|
|
err = q.FinishJob(id85, nil)
|
|
require.NoError(t, err)
|
|
setFinishedAt(t, q, id85, date85)
|
|
|
|
ids, err := q.JobsUptoByType([]string{"octopus"}, date85)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []uuid.UUID{id80}, ids["octopus"])
|
|
|
|
ids, err = q.JobsUptoByType([]string{"octopus"}, date90)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []uuid.UUID{id80, id85}, ids["octopus"])
|
|
}
|
|
|
|
func testDeleteJobAndDependencies(t *testing.T, q *dbjobqueue.DBJobQueue) {
|
|
// id1 -> id2 -> id3
|
|
id1, err := q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id1)
|
|
id2, err := q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id2)
|
|
id3, err := q.Enqueue("octopus", nil, []uuid.UUID{id2}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id3)
|
|
|
|
c1, err := q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, c1)
|
|
c2, err := q.Enqueue("octopus", nil, []uuid.UUID{c1}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, c2)
|
|
c3, err := q.Enqueue("octopus", nil, []uuid.UUID{c2}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, c3)
|
|
controls := []uuid.UUID{c1, c2, c3}
|
|
|
|
_, _, _, _, err = q.Job(c1)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, q.DeleteJobIncludingDependencies(id3))
|
|
for _, id := range []uuid.UUID{id1, id2, id3} {
|
|
_, _, _, _, err = q.Job(id)
|
|
require.ErrorIs(t, err, jobqueue.ErrNotExist)
|
|
}
|
|
|
|
// controls should still exist
|
|
for _, c := range controls {
|
|
_, _, _, _, err = q.Job(c)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// id1 -> id2 -> id4 && id3 -> id4
|
|
id1, err = q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id1)
|
|
id2, err = q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id2)
|
|
id3, err = q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id3)
|
|
id4, err := q.Enqueue("octopus", nil, []uuid.UUID{id2, id3}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id4)
|
|
|
|
require.NoError(t, q.DeleteJobIncludingDependencies(id4))
|
|
for _, id := range []uuid.UUID{id1, id2, id3, id4} {
|
|
_, _, _, _, err = q.Job(id)
|
|
require.ErrorIs(t, err, jobqueue.ErrNotExist)
|
|
}
|
|
|
|
// controls should still exist
|
|
for _, c := range controls {
|
|
_, _, _, _, err = q.Job(c)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// id1 has 2 dependants, and the maintenance queries currently do not account for this
|
|
// situation as it does not occur in the service. This should be changed once we allow
|
|
// multiple build job per depsolve job, and the depsolve job should only be removed once all
|
|
// the build jobs have been dealt with.
|
|
id1, err = q.Enqueue("octopus", nil, nil, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id1)
|
|
id2a, err := q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id2a)
|
|
id2b, err := q.Enqueue("octopus", nil, []uuid.UUID{id1}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id2b)
|
|
id3, err = q.Enqueue("octopus", nil, []uuid.UUID{id2a}, "")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, uuid.Nil, id3)
|
|
|
|
require.NoError(t, q.DeleteJobIncludingDependencies(id3))
|
|
for _, id := range []uuid.UUID{id1, id2a, id3} {
|
|
_, _, _, _, err = q.Job(id)
|
|
require.ErrorIs(t, err, jobqueue.ErrNotExist)
|
|
}
|
|
|
|
// id2b still exists
|
|
_, _, _, _, err = q.Job(id2b)
|
|
require.NoError(t, err)
|
|
|
|
// id2b can still be deleted with it's dependencies missing
|
|
require.NoError(t, q.DeleteJobIncludingDependencies(id2b))
|
|
_, _, _, _, err = q.Job(id2b)
|
|
require.ErrorIs(t, err, jobqueue.ErrNotExist)
|
|
|
|
// controls should still exist
|
|
for _, c := range controls {
|
|
_, _, _, _, err = q.Job(c)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
require.NoError(t, q.DeleteJobIncludingDependencies(uuid.Nil))
|
|
// controls should still exist
|
|
for _, c := range controls {
|
|
_, _, _, _, err = q.Job(c)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|