Files
threshold/internal/scheduler/job/additional_verification_test.go
T
Hugo 27c432a748 feat(scheduler): Drop completed job runs as they complete
Before this change, upon the completion of a job run, the scheduler
would update a job_run entry with the completed status as well as the
number of objects affected by the run. Additionally, we then had a
cleaner job whose scope was deleting these completed entries from
job_run.

This commit changes this design to drop the cleaner job the and rolls
its functionality directly into the scheduler. Now, when any given job
run is complete, the scheduler will automatically delete it from the
job_run table itself.

In addition, the completed status has been removed as a valid status as
it is not set anymore.

This change follows a decision that given the data in these job_run
entries is already being deleted by the cleaner job, updating its state
when a job completes just to have it deleted is not efficient.
Additionally, no use-case for this data was surfaced.
2024-11-05 17:10:39 +00:00

213 lines
6.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package job
import (
"context"
"database/sql"
"testing"
"time"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestJobWorkflow(t *testing.T) {
t.Parallel()
ctx := context.Background()
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
iam.TestRepo(t, conn, wrapper)
server := testController(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
require.NoError(err)
job, err := repo.UpsertJob(ctx, "job1", "description")
require.NoError(err)
require.NotNil(job)
assert.Equal(defaultPluginId, job.PluginId)
runs, err := repo.RunJobs(ctx, server.PrivateId)
require.NoError(err)
assert.Len(runs, 1)
run := runs[0]
assert.NotEmpty(run.PrivateId)
assert.Equal(job.Name, run.JobName)
run, err = repo.UpdateProgress(ctx, run.PrivateId, 100, 110, 0)
require.NoError(err)
assert.Equal(uint32(100), run.CompletedCount)
assert.Equal(uint32(110), run.TotalCount)
// The only available job is already running, a request for work should return nil
newRuns, err := repo.RunJobs(ctx, server.PrivateId)
require.NoError(err)
assert.Nil(newRuns)
err = repo.CompleteRun(ctx, run.PrivateId, time.Hour)
require.NoError(err)
job, err = repo.LookupJob(ctx, job.Name)
require.NoError(err)
assert.NotNil(job)
// The only available job has a next run in the future, a request for work should return nil
newRuns, err = repo.RunJobs(ctx, server.PrivateId)
require.NoError(err)
assert.Nil(newRuns)
// Update job next run to time in past
job, err = repo.UpdateJobNextRunInAtLeast(ctx, job.Name, 0)
require.NoError(err)
// Now that next scheduled time is in past, a request for work should return a Run
newRuns, err = repo.RunJobs(ctx, server.PrivateId)
require.NoError(err)
require.Len(newRuns, 1)
newRun := newRuns[0]
require.NotEmpty(newRun.PrivateId)
assert.Equal(job.Name, newRun.JobName)
assert.NotEqual(run.PrivateId, newRun.PrivateId)
// The only available job is already running, a request for work should return nil
newRuns, err = repo.RunJobs(ctx, server.PrivateId)
require.NoError(err)
assert.Nil(newRuns)
newRun, err = repo.FailRun(ctx, newRun.PrivateId, 0, 0, 0)
require.NoError(err)
assert.Equal(Failed.string(), newRun.Status)
// Run failed so the job should be available for work immediately
newRuns, err = repo.RunJobs(ctx, server.PrivateId)
require.NoError(err)
assert.Len(newRuns, 1)
}
// TODO (LCR): This test can be dropped once plugin support is plumbed through job repo and scheduler
func TestPlugin(t *testing.T) {
t.Parallel()
t.Run("plugin-unique", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
rows, err := rw.Query(context.Background(), upsertJobQuery, []any{
sql.Named("plugin_id", defaultPluginId),
sql.Named("name", "same-job-name"),
sql.Named("description", "description"),
sql.Named("next_scheduled_run", 0),
})
require.NoError(err)
_ = rows.Close()
rows, err = rw.Query(context.Background(), "select * from job;", nil)
require.NoError(err)
var numRows int
for rows.Next() {
numRows++
}
require.NoError(rows.Err())
_ = rows.Close()
require.Equal(1, numRows)
// Calling upsertJob with same name and pluginId should not insert a new job
rows, err = rw.Query(context.Background(), upsertJobQuery, []any{
sql.Named("plugin_id", defaultPluginId),
sql.Named("name", "same-job-name"),
sql.Named("description", "description"),
sql.Named("next_scheduled_run", 0),
})
require.NoError(err)
_ = rows.Close()
// Validate there is still only 1 job in the database
rows, err = rw.Query(context.Background(), "select * from job;", nil)
require.NoError(err)
numRows = 0
for rows.Next() {
numRows++
}
require.NoError(rows.Err())
_ = rows.Close()
require.Equal(1, numRows)
// Create test plugin id
testPluginId := "pi_test1234"
numRows, err = rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []any{testPluginId})
require.NoError(err)
assert.Equal(1, numRows)
// Calling upsertJob with the same name and different pluginId should create a new job
rows, err = rw.Query(context.Background(), upsertJobQuery, []any{
sql.Named("plugin_id", testPluginId),
sql.Named("name", "same-job-name"),
sql.Named("description", "description"),
sql.Named("next_scheduled_run", 0),
})
assert.NoError(err)
_ = rows.Close()
// Validate there are 2 jobs in the database
rows, err = rw.Query(context.Background(), "select * from job;", nil)
require.NoError(err)
numRows = 0
for rows.Next() {
numRows++
}
require.NoError(rows.Err())
_ = rows.Close()
require.Equal(2, numRows)
})
t.Run("plugin-id-immutable", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
// Create test plugin id
testPluginId := "pi_test1234"
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []any{testPluginId})
assert.NoError(err)
assert.Equal(1, numRows)
newPluginId := "pi_newtest1234"
numUpdated, err := rw.Exec(context.Background(), "update plugin set public_id = ? where public_id = ?", []any{newPluginId, testPluginId})
require.Error(err)
assert.Equal("db.Exec: immutable column: plugin.public_id: integrity violation: error #1003", err.Error())
assert.Equal(0, numUpdated)
})
t.Run("default-plugin-deletion-disallowed", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
numDeleted, err := rw.Exec(context.Background(), "delete from plugin where public_id = ?", []any{defaultPluginId})
require.Error(err)
assert.Equal("db.Exec: deletion of system plugin not allowed: integrity violation: error #1104", err.Error())
assert.Equal(0, numDeleted)
// Create test plugin id
testPluginId := "pi_test1234"
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []any{testPluginId})
assert.NoError(err)
assert.Equal(1, numRows)
numDeleted, err = rw.Exec(context.Background(), "delete from plugin where public_id = ?", []any{testPluginId})
assert.NoError(err)
assert.Equal(1, numDeleted)
})
}