From ffb36c00d0574fc15c050a7cbbd29c4ddf3eb47e Mon Sep 17 00:00:00 2001 From: Adnan Maolood Date: Tue, 15 Feb 2022 07:07:43 -0500 Subject: [PATCH] api/graph: Delete artifacts upon repository deletion Upon deleting a repository, gather a list of all the artifacts associated with it and schedule their deletion in a background task. Fixes: https://todo.sr.ht/~sircmpwn/git.sr.ht/356 --- api/graph/schema.resolvers.go | 25 ++++++++++++++++++-- api/repos/middleware.go | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 0008e03..ea51a5d 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -447,6 +447,23 @@ func (r *mutationResolver) DeleteRepository(ctx context.Context, id int) (*model var repo model.Repository if err := database.WithTx(ctx, nil, func(tx *sql.Tx) error { + rows, err := tx.QueryContext(ctx, + `DELETE FROM artifacts WHERE repo_id = $1 RETURNING filename;`, id) + if err != nil { + return err + } + var artifacts []string + for rows.Next() { + var filename string + if err := rows.Scan(&filename); err != nil { + return err + } + artifacts = append(artifacts, filename) + } + if err := rows.Err(); err != nil { + return err + } + row := tx.QueryRowContext(ctx, ` DELETE FROM repository WHERE id = $1 AND owner_id = $2 @@ -467,10 +484,14 @@ func (r *mutationResolver) DeleteRepository(ctx context.Context, id int) (*model webhooks.DeliverRepoEvent(ctx, model.WebhookEventRepoDeleted, &repo) webhooks.DeliverLegacyRepoDeleted(ctx, &repo) - err := os.RemoveAll(repo.Path) - if err != nil { + if err := os.RemoveAll(repo.Path); err != nil { return err } + + if len(artifacts) > 0 { + username := auth.ForContext(ctx).Username + repos.DeleteArtifacts(ctx, username, repo.Name, artifacts) + } return nil }); err != nil { return nil, err diff --git a/api/repos/middleware.go b/api/repos/middleware.go index 95a20e4..ef39832 100644 --- a/api/repos/middleware.go +++ b/api/repos/middleware.go @@ -3,13 +3,18 @@ package repos import ( "context" "database/sql" + "fmt" "log" "net/http" + "path" "time" + "git.sr.ht/~sircmpwn/core-go/config" "git.sr.ht/~sircmpwn/core-go/database" work "git.sr.ht/~sircmpwn/dowork" "github.com/go-git/go-git/v5" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" ) type contextKey struct { @@ -69,3 +74,41 @@ func Clone(ctx context.Context, repoID int, repo *git.Repository, cloneURL strin queue.Enqueue(task) log.Printf("Enqueued clone of %s", cloneURL) } + +// Schedules deletion of artifacts. +func DeleteArtifacts(ctx context.Context, username, repoName string, filenames []string) { + queue, ok := ctx.Value(ctxKey).(*work.Queue) + if !ok { + panic("No repos worker for this context") + } + task := work.NewTask(func(ctx context.Context) error { + conf := config.ForContext(ctx) + upstream, _ := conf.Get("objects", "s3-upstream") + accessKey, _ := conf.Get("objects", "s3-access-key") + secretKey, _ := conf.Get("objects", "s3-secret-key") + bucket, _ := conf.Get("git.sr.ht", "s3-bucket") + prefix, _ := conf.Get("git.sr.ht", "s3-prefix") + + if upstream == "" || accessKey == "" || secretKey == "" || bucket == "" { + return fmt.Errorf("Object storage is not enabled for this server") + } + + mc, err := minio.New(upstream, &minio.Options{ + Creds: credentials.NewStaticV4(accessKey, secretKey, ""), + Secure: true, + }) + if err != nil { + panic(err) + } + + for _, filename := range filenames { + s3path := path.Join(prefix, "artifacts", "~"+username, repoName, filename) + if err := mc.RemoveObject(ctx, bucket, s3path, minio.RemoveObjectOptions{}); err != nil { + return err + } + } + return nil + }) + queue.Enqueue(task) + log.Printf("Enqueued deletion of %d artifacts", len(filenames)) +} -- 2.38.4