worker: Add CleanupArtifacts function

This removes all artifact directories, and their contents, if there
isn't an associated Job. This is used to clean up local artifacts after
the compose job has been deleted.

Related: RHEL-60120
This commit is contained in:
Brian C. Lane 2024-05-01 18:02:31 -07:00 committed by Tomáš Hozza
parent c3394ae589
commit f1a2c24563
2 changed files with 113 additions and 2 deletions

View file

@ -353,6 +353,38 @@ func (s *Server) AllRootJobIDs(ctx context.Context) ([]uuid.UUID, error) {
return s.jobs.AllRootJobIDs(ctx)
}
// CleanupArtifacts removes worker artifact directories that do not have matching jobs
// The UUID used for the artifact directory is the same as for the job that created it
func (s *Server) CleanupArtifacts() error {
artifacts, err := os.ReadDir(s.config.ArtifactsDir)
if err != nil {
return err
}
for _, d := range artifacts {
if !d.IsDir() {
continue
}
id, err := uuid.Parse(d.Name())
if err != nil {
continue
}
// Is there a job with this UUID?
if _, _, _, _, err := s.jobs.Job(id); err != nil {
// No associated job, it is safe to remove the unused artifact directory
// and everything under it, and the ComposeRequest (if it exists)
_ = os.Remove(path.Join(s.config.ArtifactsDir, "ComposeRequest", id.String()+".json"))
err = os.RemoveAll(path.Join(s.config.ArtifactsDir, id.String()))
if err != nil {
return err
}
}
}
return nil
}
func (s *Server) OSBuildJobInfo(id uuid.UUID, result *OSBuildJobResult) (*JobInfo, error) {
jobInfo, err := s.jobInfo(id, result)
if err != nil {

View file

@ -3,6 +3,7 @@ package worker_test
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
@ -32,7 +33,14 @@ import (
)
func newTestServer(t *testing.T, tempdir string, config worker.Config, acceptArtifacts bool) *worker.Server {
q, err := fsjobqueue.New(tempdir)
// NOTE: jobs and artifacts directories need to be next to each other. artifacts inside the fsjobqueue
// directory makes it crash when trying to list all the jobs because it isn't a UUID.
jobsDir := path.Join(tempdir, "jobs")
err := os.Mkdir(jobsDir, 0755)
if err != nil && !os.IsExist(err) {
t.Fatalf("cannot create jobs directory %s: %v", jobsDir, err)
}
q, err := fsjobqueue.New(jobsDir)
if err != nil {
t.Fatalf("error creating fsjobqueue: %v", err)
}
@ -41,7 +49,7 @@ func newTestServer(t *testing.T, tempdir string, config worker.Config, acceptArt
artifactsDir := path.Join(tempdir, "artifacts")
err := os.Mkdir(artifactsDir, 0755)
if err != nil && !os.IsExist(err) {
t.Fatalf("cannot create state directory %s: %v", artifactsDir, err)
t.Fatalf("cannot create artifacts directory %s: %v", artifactsDir, err)
}
config.ArtifactsDir = artifactsDir
}
@ -1607,3 +1615,74 @@ func TestJobHeartbeats(t *testing.T) {
require.Equal(t, float64(0), promtest.ToFloat64(prometheus.PendingJobs))
require.Equal(t, float64(0), promtest.ToFloat64(prometheus.RunningJobs))
}
func makeFakeArtifact(tempdir string, id uuid.UUID, filename string) error {
d := path.Join(tempdir, "artifacts", id.String())
err := os.Mkdir(d, 0755)
if err != nil {
return err
}
if len(filename) > 0 {
p := path.Join(d, filename)
fp, err := os.Create(p)
if err != nil {
return err
}
return fp.Close()
}
return nil
}
func artifactUUIDExists(tempdir string, id uuid.UUID) bool {
dir := path.Join(tempdir, "artifacts", id.String())
_, err := os.Stat(dir)
return !errors.Is(err, os.ErrNotExist)
}
func TestCleanupArtifacts(t *testing.T) {
distroStruct := newTestDistro(t)
arch, err := distroStruct.GetArch(test_distro.TestArchName)
if err != nil {
t.Fatalf("error getting arch from distro: %v", err)
}
imageType, err := arch.GetImageType(test_distro.TestImageTypeName)
if err != nil {
t.Fatalf("error getting image type from arch: %v", err)
}
manifest, _, err := imageType.Manifest(nil, distro.ImageOptions{Size: imageType.Size(0)}, nil, nil)
if err != nil {
t.Fatalf("error creating osbuild manifest: %v", err)
}
tempdir := t.TempDir()
server := newTestServer(t, tempdir, defaultConfig, true)
mf, err := manifest.Serialize(nil, nil, nil, nil)
if err != nil {
t.Fatalf("error creating osbuild manifest: %v", err)
}
jobID, err := server.EnqueueOSBuild(arch.Name(), &worker.OSBuildJob{Manifest: mf}, "")
require.NoError(t, err)
// Make a fake artifact for the existing jobid
err = makeFakeArtifact(tempdir, jobID, "not-a-real.img")
require.Nil(t, err)
// Make an artifact directory for a job that is already gone
lostJobID := uuid.New()
err = makeFakeArtifact(tempdir, lostJobID, "not-a-real.img")
require.Nil(t, err)
// Make an artifact directory with no files
emptyJobID := uuid.New()
err = makeFakeArtifact(tempdir, emptyJobID, "")
require.Nil(t, err)
// This should remove lostJobID and not jobID
err = server.CleanupArtifacts()
require.Nil(t, err)
assert.True(t, artifactUUIDExists(tempdir, jobID))
assert.False(t, artifactUUIDExists(tempdir, lostJobID))
assert.False(t, artifactUUIDExists(tempdir, emptyJobID))
}