More informative return value for task.Process

This commit is contained in:
Lorenzo Bolla
2021-09-20 09:43:44 +02:00
parent 0914cd16af
commit ff51c46915
11 changed files with 156 additions and 127 deletions
+10 -6
View File
@@ -4,6 +4,7 @@ package api
import ( import (
"fmt" "fmt"
"log" "log"
"net/http"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -100,11 +101,11 @@ func releaseDatabaseConnection() error {
// runs tasks in background. Acquires database connection first. // runs tasks in background. Acquires database connection first.
func runTaskInBackground(name string, resources []string, proc task.Process) (task.Task, *task.ResourceConflictError) { func runTaskInBackground(name string, resources []string, proc task.Process) (task.Task, *task.ResourceConflictError) {
return context.TaskList().RunTaskInBackground(name, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { return context.TaskList().RunTaskInBackground(name, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
err := acquireDatabaseConnection() err := acquireDatabaseConnection()
if err != nil { if err != nil {
return -1, err return nil, err
} }
defer releaseDatabaseConnection() defer releaseDatabaseConnection()
@@ -142,13 +143,16 @@ func maybeRunTaskInBackground(c *gin.Context, name string, resources []string, p
log.Println("Executing task synchronously") log.Println("Executing task synchronously")
out := context.Progress() out := context.Progress()
detail := task.Detail{} detail := task.Detail{}
retCode, err := proc(out, &detail) retValue, err := proc(out, &detail)
if err != nil { if err != nil {
c.AbortWithError(retCode, err) c.AbortWithError(retValue.Code, err)
return return
} }
response := detail.Load() if retValue != nil {
c.JSON(retCode, response) c.JSON(retValue.Code, retValue.Value)
} else {
c.JSON(http.StatusOK, nil)
}
} }
} }
+10 -10
View File
@@ -15,7 +15,7 @@ import (
func apiDbCleanup(c *gin.Context) { func apiDbCleanup(c *gin.Context) {
resources := []string{string(task.AllResourcesKey)} resources := []string{string(task.AllResourcesKey)}
maybeRunTaskInBackground(c, "Clean up db", resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, "Clean up db", resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
var err error var err error
collectionFactory := context.NewCollectionFactory() collectionFactory := context.NewCollectionFactory()
@@ -36,7 +36,7 @@ func apiDbCleanup(c *gin.Context) {
return nil return nil
}) })
if err != nil { if err != nil {
return -1, err return nil, err
} }
err = collectionFactory.LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error { err = collectionFactory.LocalRepoCollection().ForEach(func(repo *deb.LocalRepo) error {
@@ -52,7 +52,7 @@ func apiDbCleanup(c *gin.Context) {
return nil return nil
}) })
if err != nil { if err != nil {
return -1, err return nil, err
} }
err = collectionFactory.SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error { err = collectionFactory.SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
@@ -66,7 +66,7 @@ func apiDbCleanup(c *gin.Context) {
return nil return nil
}) })
if err != nil { if err != nil {
return -1, err return nil, err
} }
err = collectionFactory.PublishedRepoCollection().ForEach(func(published *deb.PublishedRepo) error { err = collectionFactory.PublishedRepoCollection().ForEach(func(published *deb.PublishedRepo) error {
@@ -84,7 +84,7 @@ func apiDbCleanup(c *gin.Context) {
return nil return nil
}) })
if err != nil { if err != nil {
return -1, err return nil, err
} }
// ... and compare it to the list of all packages // ... and compare it to the list of all packages
@@ -108,7 +108,7 @@ func apiDbCleanup(c *gin.Context) {
err = batch.Write() err = batch.Write()
if err != nil { if err != nil {
return -1, fmt.Errorf("unable to write to DB: %s", err) return nil, fmt.Errorf("unable to write to DB: %s", err)
} }
} }
@@ -131,7 +131,7 @@ func apiDbCleanup(c *gin.Context) {
return nil return nil
}) })
if err != nil { if err != nil {
return -1, err return nil, err
} }
sort.Strings(referencedFiles) sort.Strings(referencedFiles)
@@ -140,7 +140,7 @@ func apiDbCleanup(c *gin.Context) {
out.Printf("Building list of files in package pool...") out.Printf("Building list of files in package pool...")
existingFiles, err := context.PackagePool().FilepathList(out) existingFiles, err := context.PackagePool().FilepathList(out)
if err != nil { if err != nil {
return -1, fmt.Errorf("unable to collect file paths: %s", err) return nil, fmt.Errorf("unable to collect file paths: %s", err)
} }
// find files which are in the pool but not referenced by packages // find files which are in the pool but not referenced by packages
@@ -163,7 +163,7 @@ func apiDbCleanup(c *gin.Context) {
for _, file := range filesToDelete { for _, file := range filesToDelete {
size, err = context.PackagePool().Remove(file) size, err = context.PackagePool().Remove(file)
if err != nil { if err != nil {
return -1, err return nil, err
} }
taskDetail.RemainingNumberOfPackagesToDelete-- taskDetail.RemainingNumberOfPackagesToDelete--
@@ -175,6 +175,6 @@ func apiDbCleanup(c *gin.Context) {
} }
out.Printf("Compacting database...") out.Printf("Compacting database...")
return -1, db.CompactDB() return nil, db.CompactDB()
}) })
} }
+24 -24
View File
@@ -147,25 +147,25 @@ func apiMirrorsDrop(c *gin.Context) {
resources := []string{string(repo.Key())} resources := []string{string(repo.Key())}
taskName := fmt.Sprintf("Delete mirror %s", name) taskName := fmt.Sprintf("Delete mirror %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
err := repo.CheckLock() err := repo.CheckLock()
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to drop: %v", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to drop: %v", err)
} }
if !force { if !force {
snapshots := snapshotCollection.ByRemoteRepoSource(repo) snapshots := snapshotCollection.ByRemoteRepoSource(repo)
if len(snapshots) > 0 { if len(snapshots) > 0 {
return http.StatusInternalServerError, fmt.Errorf("won't delete mirror with snapshots, use 'force=1' to override") return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("won't delete mirror with snapshots, use 'force=1' to override")
} }
} }
err = mirrorCollection.Drop(repo) err = mirrorCollection.Drop(repo)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to drop: %v", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to drop: %v", err)
} }
return http.StatusNoContent, nil return &task.ProcessReturnValue{http.StatusNoContent, nil}, nil
}) })
} }
@@ -350,24 +350,24 @@ func apiMirrorsUpdate(c *gin.Context) {
} }
resources := []string{string(remote.Key())} resources := []string{string(remote.Key())}
maybeRunTaskInBackground(c, "Update mirror "+b.Name, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, "Update mirror "+b.Name, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
downloader := context.NewDownloader(out) downloader := context.NewDownloader(out)
err := remote.Fetch(downloader, verifier) err := remote.Fetch(downloader, verifier)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
if !b.ForceUpdate { if !b.ForceUpdate {
err = remote.CheckLock() err = remote.CheckLock()
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
} }
err = remote.DownloadPackageIndexes(out, downloader, verifier, collectionFactory, b.SkipComponentCheck) err = remote.DownloadPackageIndexes(out, downloader, verifier, collectionFactory, b.SkipComponentCheck)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
if remote.Filter != "" { if remote.Filter != "" {
@@ -375,19 +375,19 @@ func apiMirrorsUpdate(c *gin.Context) {
filterQuery, err = query.Parse(remote.Filter) filterQuery, err = query.Parse(remote.Filter)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
_, _, err = remote.ApplyFilter(context.DependencyOptions(), filterQuery, out) _, _, err = remote.ApplyFilter(context.DependencyOptions(), filterQuery, out)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
} }
queue, downloadSize, err := remote.BuildDownloadQueue(context.PackagePool(), collectionFactory.PackageCollection(), queue, downloadSize, err := remote.BuildDownloadQueue(context.PackagePool(), collectionFactory.PackageCollection(),
collectionFactory.ChecksumCollection(nil), b.SkipExistingPackages) collectionFactory.ChecksumCollection(nil), b.SkipExistingPackages)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
defer func() { defer func() {
@@ -402,7 +402,7 @@ func apiMirrorsUpdate(c *gin.Context) {
remote.MarkAsUpdating() remote.MarkAsUpdating()
err = collection.Update(remote) err = collection.Update(remote)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
context.GoContextHandleSignals() context.GoContextHandleSignals()
@@ -512,45 +512,45 @@ func apiMirrorsUpdate(c *gin.Context) {
for idx := range queue { for idx := range queue {
task := &queue[idx] atask := &queue[idx]
if !task.Done { if !atask.Done {
// download not finished yet // download not finished yet
continue continue
} }
// and import it back to the pool // and import it back to the pool
task.File.PoolPath, err = context.PackagePool().Import(task.TempDownPath, task.File.Filename, &task.File.Checksums, true, collectionFactory.ChecksumCollection(nil)) atask.File.PoolPath, err = context.PackagePool().Import(atask.TempDownPath, atask.File.Filename, &atask.File.Checksums, true, collectionFactory.ChecksumCollection(nil))
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to import file: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to import file: %s", err)
} }
// update "attached" files if any // update "attached" files if any
for _, additionalTask := range task.Additional { for _, additionalAtask := range atask.Additional {
additionalTask.File.PoolPath = task.File.PoolPath additionalAtask.File.PoolPath = atask.File.PoolPath
additionalTask.File.Checksums = task.File.Checksums additionalAtask.File.Checksums = atask.File.Checksums
} }
} }
select { select {
case <-context.Done(): case <-context.Done():
return http.StatusInternalServerError, fmt.Errorf("unable to update: interrupted") return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: interrupted")
default: default:
} }
if len(errors) > 0 { if len(errors) > 0 {
log.Printf("%s: Unable to update because of previous errors\n", b.Name) log.Printf("%s: Unable to update because of previous errors\n", b.Name)
return http.StatusInternalServerError, fmt.Errorf("unable to update: download errors:\n %s", strings.Join(errors, "\n ")) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: download errors:\n %s", strings.Join(errors, "\n "))
} }
log.Printf("%s: Finalizing download\n", b.Name) log.Printf("%s: Finalizing download\n", b.Name)
remote.FinalizeDownload(collectionFactory, out) remote.FinalizeDownload(collectionFactory, out)
err = collectionFactory.RemoteRepoCollection().Update(remote) err = collectionFactory.RemoteRepoCollection().Update(remote)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
log.Printf("%s: Mirror updated successfully!\n", b.Name) log.Printf("%s: Mirror updated successfully!\n", b.Name)
return http.StatusNoContent, nil return &task.ProcessReturnValue{http.StatusNoContent, nil}, nil
}) })
} }
+13 -16
View File
@@ -184,7 +184,7 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
collection := collectionFactory.PublishedRepoCollection() collection := collectionFactory.PublishedRepoCollection()
taskName := fmt.Sprintf("Publish %s: %s", b.SourceKind, strings.Join(names, ", ")) taskName := fmt.Sprintf("Publish %s: %s", b.SourceKind, strings.Join(names, ", "))
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
taskDetail := task.PublishDetail{ taskDetail := task.PublishDetail{
Detail: detail, Detail: detail,
} }
@@ -216,21 +216,20 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
duplicate := collection.CheckDuplicate(published) duplicate := collection.CheckDuplicate(published)
if duplicate != nil { if duplicate != nil {
collectionFactory.PublishedRepoCollection().LoadComplete(duplicate, collectionFactory) collectionFactory.PublishedRepoCollection().LoadComplete(duplicate, collectionFactory)
return 400, fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate) return &task.ProcessReturnValue{http.StatusBadRequest, nil}, fmt.Errorf("prefix/distribution already used by another published repo: %s", duplicate)
} }
err := published.Publish(context.PackagePool(), context, collectionFactory, signer, publishOutput, b.ForceOverwrite) err := published.Publish(context.PackagePool(), context, collectionFactory, signer, publishOutput, b.ForceOverwrite)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to publish: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to publish: %s", err)
} }
err = collection.Add(published) err = collection.Add(published)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to save to DB: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to save to DB: %s", err)
} }
detail.Store(published) return &task.ProcessReturnValue{http.StatusCreated, published}, nil
return http.StatusCreated, nil
}) })
} }
@@ -329,27 +328,26 @@ func apiPublishUpdateSwitch(c *gin.Context) {
resources = append(resources, string(published.Key())) resources = append(resources, string(published.Key()))
taskName := fmt.Sprintf("Update published %s (%s): %s", published.SourceKind, strings.Join(updatedComponents, " "), strings.Join(updatedSnapshots, ", ")) taskName := fmt.Sprintf("Update published %s (%s): %s", published.SourceKind, strings.Join(updatedComponents, " "), strings.Join(updatedSnapshots, ", "))
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
err := published.Publish(context.PackagePool(), context, collectionFactory, signer, out, b.ForceOverwrite) err := published.Publish(context.PackagePool(), context, collectionFactory, signer, out, b.ForceOverwrite)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
err = collection.Update(published) err = collection.Update(published)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to save to DB: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to save to DB: %s", err)
} }
if b.SkipCleanup == nil || !*b.SkipCleanup { if b.SkipCleanup == nil || !*b.SkipCleanup {
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents, err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents,
context.GetPublishedStorage(storage), collectionFactory, out) context.GetPublishedStorage(storage), collectionFactory, out)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to update: %s", err)
} }
} }
detail.Store(published) return &task.ProcessReturnValue{http.StatusOK, published}, nil
return http.StatusOK, nil
}) })
} }
@@ -374,14 +372,13 @@ func apiPublishDrop(c *gin.Context) {
resources := []string{string(published.Key())} resources := []string{string(published.Key())}
taskName := fmt.Sprintf("Delete published %s (%s)", prefix, distribution) taskName := fmt.Sprintf("Delete published %s (%s)", prefix, distribution)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
err := collection.Remove(context, storage, prefix, distribution, err := collection.Remove(context, storage, prefix, distribution,
collectionFactory, out, force, skipCleanup) collectionFactory, out, force, skipCleanup)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to drop: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to drop: %s", err)
} }
detail.Store(gin.H{}) return &task.ProcessReturnValue{http.StatusOK, gin.H{}}, nil
return http.StatusOK, nil
}) })
} }
+21 -25
View File
@@ -141,21 +141,20 @@ func apiReposDrop(c *gin.Context) {
resources := []string{string(repo.Key())} resources := []string{string(repo.Key())}
taskName := fmt.Sprintf("Delete repo %s", name) taskName := fmt.Sprintf("Delete repo %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
published := publishedCollection.ByLocalRepo(repo) published := publishedCollection.ByLocalRepo(repo)
if len(published) > 0 { if len(published) > 0 {
return http.StatusConflict, fmt.Errorf("unable to drop, local repo is published") return &task.ProcessReturnValue{http.StatusConflict, nil}, fmt.Errorf("unable to drop, local repo is published")
} }
if !force { if !force {
snapshots := snapshotCollection.ByLocalRepoSource(repo) snapshots := snapshotCollection.ByLocalRepoSource(repo)
if len(snapshots) > 0 { if len(snapshots) > 0 {
return http.StatusConflict, fmt.Errorf("unable to drop, local repo has snapshots, use ?force=1 to override") return &task.ProcessReturnValue{http.StatusConflict, nil}, fmt.Errorf("unable to drop, local repo has snapshots, use ?force=1 to override")
} }
} }
detail.Store(gin.H{}) return &task.ProcessReturnValue{http.StatusOK, gin.H{}}, collection.Drop(repo)
return http.StatusOK, collection.Drop(repo)
}) })
} }
@@ -205,11 +204,11 @@ func apiReposPackagesAddDelete(c *gin.Context, taskNamePrefix string, cb func(li
} }
resources := []string{string(repo.Key())} resources := []string{string(repo.Key())}
maybeRunTaskInBackground(c, taskNamePrefix+repo.Name, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskNamePrefix+repo.Name, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
out.Printf("Loading packages...\n") out.Printf("Loading packages...\n")
list, err := deb.NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), nil) list, err := deb.NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), nil)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
// verify package refs and build package list // verify package refs and build package list
@@ -219,14 +218,14 @@ func apiReposPackagesAddDelete(c *gin.Context, taskNamePrefix string, cb func(li
p, err = collectionFactory.PackageCollection().ByKey([]byte(ref)) p, err = collectionFactory.PackageCollection().ByKey([]byte(ref))
if err != nil { if err != nil {
if err == database.ErrNotFound { if err == database.ErrNotFound {
return http.StatusNotFound, fmt.Errorf("packages %s: %s", ref, err) return &task.ProcessReturnValue{http.StatusNotFound, nil}, fmt.Errorf("packages %s: %s", ref, err)
} }
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
err = cb(list, p, out) err = cb(list, p, out)
if err != nil { if err != nil {
return http.StatusBadRequest, err return &task.ProcessReturnValue{http.StatusBadRequest, nil}, err
} }
} }
@@ -234,10 +233,9 @@ func apiReposPackagesAddDelete(c *gin.Context, taskNamePrefix string, cb func(li
err = collectionFactory.LocalRepoCollection().Update(repo) err = collectionFactory.LocalRepoCollection().Update(repo)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to save: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to save: %s", err)
} }
detail.Store(repo) return &task.ProcessReturnValue{http.StatusOK, repo}, nil
return http.StatusOK, nil
}) })
} }
@@ -308,7 +306,7 @@ func apiReposPackageFromDir(c *gin.Context) {
resources := []string{string(repo.Key())} resources := []string{string(repo.Key())}
resources = append(resources, sources...) resources = append(resources, sources...)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
verifier := context.GetVerifier() verifier := context.GetVerifier()
var ( var (
@@ -327,7 +325,7 @@ func apiReposPackageFromDir(c *gin.Context) {
list, err := deb.NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), nil) list, err := deb.NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), nil)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to load packages: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to load packages: %s", err)
} }
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(), processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
@@ -336,14 +334,14 @@ func apiReposPackageFromDir(c *gin.Context) {
processedFiles = append(processedFiles, otherFiles...) processedFiles = append(processedFiles, otherFiles...)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to import package files: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to import package files: %s", err)
} }
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list)) repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
err = collectionFactory.LocalRepoCollection().Update(repo) err = collectionFactory.LocalRepoCollection().Update(repo)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to save: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to save: %s", err)
} }
if !noRemove { if !noRemove {
@@ -377,11 +375,10 @@ func apiReposPackageFromDir(c *gin.Context) {
out.Printf("Failed files: %s\n", strings.Join(failedFiles, ", ")) out.Printf("Failed files: %s\n", strings.Join(failedFiles, ", "))
} }
detail.Store(gin.H{ return &task.ProcessReturnValue{http.StatusOK, gin.H{
"Report": reporter, "Report": reporter,
"FailedFiles": failedFiles, "FailedFiles": failedFiles,
}) }}, nil
return http.StatusOK, nil
}) })
} }
@@ -443,7 +440,7 @@ func apiReposIncludePackageFromDir(c *gin.Context) {
} }
resources = append(resources, sources...) resources = append(resources, sources...)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
var ( var (
err error err error
verifier = context.GetVerifier() verifier = context.GetVerifier()
@@ -464,7 +461,7 @@ func apiReposIncludePackageFromDir(c *gin.Context) {
failedFiles = append(failedFiles, failedFiles2...) failedFiles = append(failedFiles, failedFiles2...)
if err != nil { if err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to import changes files: %s", err) return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, fmt.Errorf("unable to import changes files: %s", err)
} }
if !noRemoveFiles { if !noRemoveFiles {
@@ -489,11 +486,10 @@ func apiReposIncludePackageFromDir(c *gin.Context) {
out.Printf("Failed files: %s\n", strings.Join(failedFiles, ", ")) out.Printf("Failed files: %s\n", strings.Join(failedFiles, ", "))
} }
detail.Store(gin.H{ return &task.ProcessReturnValue{http.StatusOK, gin.H{
"Report": reporter, "Report": reporter,
"FailedFiles": failedFiles, "FailedFiles": failedFiles,
}) }}, nil
return http.StatusOK, nil
}) })
} }
+1
View File
@@ -134,6 +134,7 @@ func Router(c *ctx.AptlyContext) http.Handler {
root.GET("/tasks/:id/wait", apiTasksWaitForTaskByID) root.GET("/tasks/:id/wait", apiTasksWaitForTaskByID)
root.GET("/tasks/:id/output", apiTasksOutputShow) root.GET("/tasks/:id/output", apiTasksOutputShow)
root.GET("/tasks/:id/detail", apiTasksDetailShow) root.GET("/tasks/:id/detail", apiTasksDetailShow)
root.GET("/tasks/:id/return_value", apiTasksReturnValueShow)
root.GET("/tasks/:id", apiTasksShow) root.GET("/tasks/:id", apiTasksShow)
root.DELETE("/tasks/:id", apiTasksDelete) root.DELETE("/tasks/:id", apiTasksDelete)
root.POST("/tasks-dummy", apiTasksDummy) root.POST("/tasks-dummy", apiTasksDummy)
+26 -30
View File
@@ -62,20 +62,20 @@ func apiSnapshotsCreateFromMirror(c *gin.Context) {
// including snapshot resource key // including snapshot resource key
resources := []string{string(repo.Key()), "S" + b.Name} resources := []string{string(repo.Key()), "S" + b.Name}
taskName := fmt.Sprintf("Create snapshot of mirror %s", name) taskName := fmt.Sprintf("Create snapshot of mirror %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
err := repo.CheckLock() err := repo.CheckLock()
if err != nil { if err != nil {
return http.StatusConflict, err return &task.ProcessReturnValue{http.StatusConflict, nil}, err
} }
err = collection.LoadComplete(repo) err = collection.LoadComplete(repo)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo) snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
if err != nil { if err != nil {
return http.StatusBadRequest, err return &task.ProcessReturnValue{http.StatusBadRequest, nil}, err
} }
if b.Description != "" { if b.Description != "" {
@@ -84,10 +84,9 @@ func apiSnapshotsCreateFromMirror(c *gin.Context) {
err = snapshotCollection.Add(snapshot) err = snapshotCollection.Add(snapshot)
if err != nil { if err != nil {
return http.StatusBadRequest, err return &task.ProcessReturnValue{http.StatusBadRequest, nil}, err
} }
detail.Store(snapshot) return &task.ProcessReturnValue{http.StatusCreated, snapshot}, nil
return http.StatusCreated, nil
}) })
} }
@@ -137,7 +136,7 @@ func apiSnapshotsCreate(c *gin.Context) {
resources = append(resources, string(sources[i].ResourceKey())) resources = append(resources, string(sources[i].ResourceKey()))
} }
maybeRunTaskInBackground(c, "Create snapshot "+b.Name, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, "Create snapshot "+b.Name, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
list := deb.NewPackageList() list := deb.NewPackageList()
// verify package refs and build package list // verify package refs and build package list
@@ -145,13 +144,13 @@ func apiSnapshotsCreate(c *gin.Context) {
p, err := collectionFactory.PackageCollection().ByKey([]byte(ref)) p, err := collectionFactory.PackageCollection().ByKey([]byte(ref))
if err != nil { if err != nil {
if err == database.ErrNotFound { if err == database.ErrNotFound {
return http.StatusNotFound, fmt.Errorf("package %s: %s", ref, err) return &task.ProcessReturnValue{http.StatusNotFound, nil}, fmt.Errorf("package %s: %s", ref, err)
} }
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
err = list.Add(p) err = list.Add(p)
if err != nil { if err != nil {
return http.StatusBadRequest, err return &task.ProcessReturnValue{http.StatusBadRequest, nil}, err
} }
} }
@@ -159,9 +158,9 @@ func apiSnapshotsCreate(c *gin.Context) {
err = snapshotCollection.Add(snapshot) err = snapshotCollection.Add(snapshot)
if err != nil { if err != nil {
return http.StatusBadRequest, err return &task.ProcessReturnValue{http.StatusBadRequest, nil}, err
} }
return http.StatusCreated, nil return &task.ProcessReturnValue{http.StatusCreated, nil}, nil
}) })
} }
@@ -196,15 +195,15 @@ func apiSnapshotsCreateFromRepository(c *gin.Context) {
// including snapshot resource key // including snapshot resource key
resources := []string{string(repo.Key()), "S" + b.Name} resources := []string{string(repo.Key()), "S" + b.Name}
taskName := fmt.Sprintf("Create snapshot of repo %s", name) taskName := fmt.Sprintf("Create snapshot of repo %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
err := collection.LoadComplete(repo) err := collection.LoadComplete(repo)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo) snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
if err != nil { if err != nil {
return http.StatusNotFound, err return &task.ProcessReturnValue{http.StatusNotFound, nil}, err
} }
if b.Description != "" { if b.Description != "" {
@@ -213,10 +212,9 @@ func apiSnapshotsCreateFromRepository(c *gin.Context) {
err = snapshotCollection.Add(snapshot) err = snapshotCollection.Add(snapshot)
if err != nil { if err != nil {
return http.StatusBadRequest, err return &task.ProcessReturnValue{http.StatusBadRequest, nil}, err
} }
detail.Store(snapshot) return &task.ProcessReturnValue{http.StatusCreated, snapshot}, nil
return http.StatusCreated, nil
}) })
} }
@@ -248,10 +246,10 @@ func apiSnapshotsUpdate(c *gin.Context) {
resources := []string{string(snapshot.ResourceKey()), "S" + b.Name} resources := []string{string(snapshot.ResourceKey()), "S" + b.Name}
taskName := fmt.Sprintf("Update snapshot %s", name) taskName := fmt.Sprintf("Update snapshot %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
_, err := collection.ByName(b.Name) _, err := collection.ByName(b.Name)
if err == nil { if err == nil {
return http.StatusConflict, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name) return &task.ProcessReturnValue{http.StatusConflict, nil}, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name)
} }
if b.Name != "" { if b.Name != "" {
@@ -264,10 +262,9 @@ func apiSnapshotsUpdate(c *gin.Context) {
err = collectionFactory.SnapshotCollection().Update(snapshot) err = collectionFactory.SnapshotCollection().Update(snapshot)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
detail.Store(snapshot) return &task.ProcessReturnValue{http.StatusOK, snapshot}, nil
return http.StatusOK, nil
}) })
} }
@@ -308,26 +305,25 @@ func apiSnapshotsDrop(c *gin.Context) {
resources := []string{string(snapshot.ResourceKey())} resources := []string{string(snapshot.ResourceKey())}
taskName := fmt.Sprintf("Delete snapshot %s", name) taskName := fmt.Sprintf("Delete snapshot %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
published := publishedCollection.BySnapshot(snapshot) published := publishedCollection.BySnapshot(snapshot)
if len(published) > 0 { if len(published) > 0 {
return http.StatusConflict, fmt.Errorf("unable to drop: snapshot is published") return &task.ProcessReturnValue{http.StatusConflict, nil}, fmt.Errorf("unable to drop: snapshot is published")
} }
if !force { if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot) snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 { if len(snapshots) > 0 {
return http.StatusConflict, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override") return &task.ProcessReturnValue{http.StatusConflict, nil}, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override")
} }
} }
err = snapshotCollection.Drop(snapshot) err = snapshotCollection.Drop(snapshot)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return &task.ProcessReturnValue{http.StatusInternalServerError, nil}, err
} }
detail.Store(gin.H{}) return &task.ProcessReturnValue{http.StatusOK, gin.H{}}, nil
return http.StatusOK, nil
}) })
} }
+20 -2
View File
@@ -105,6 +105,24 @@ func apiTasksDetailShow(c *gin.Context) {
c.JSON(200, detail) c.JSON(200, detail)
} }
// GET /tasks/:id/return_value
func apiTasksReturnValueShow(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
if err != nil {
c.AbortWithError(500, err)
return
}
output, err := list.GetTaskReturnValueByID(int(id))
if err != nil {
c.AbortWithError(404, err)
return
}
c.JSON(200, output)
}
// DELETE /tasks/:id // DELETE /tasks/:id
func apiTasksDelete(c *gin.Context) { func apiTasksDelete(c *gin.Context) {
list := context.TaskList() list := context.TaskList()
@@ -128,10 +146,10 @@ func apiTasksDelete(c *gin.Context) {
func apiTasksDummy(c *gin.Context) { func apiTasksDummy(c *gin.Context) {
resources := []string{"dummy"} resources := []string{"dummy"}
taskName := fmt.Sprintf("Dummy task") taskName := fmt.Sprintf("Dummy task")
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (int, error) { maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
out.Printf("Dummy task started\n") out.Printf("Dummy task started\n")
detail.Store([]int{1, 2, 3}) detail.Store([]int{1, 2, 3})
out.Printf("Dummy task finished\n") out.Printf("Dummy task finished\n")
return http.StatusTeapot, nil return &task.ProcessReturnValue{Code: http.StatusTeapot, Value: []int{1, 2, 3}}, nil
}) })
} }
+13 -2
View File
@@ -105,6 +105,17 @@ func (list *List) GetTaskDetailByID(ID int) (interface{}, error) {
return detail, nil return detail, nil
} }
// GetTaskReturnValueByID returns process return value of task with given id
func (list *List) GetTaskReturnValueByID(ID int) (*ProcessReturnValue, error) {
task, err := list.GetTaskByID(ID)
if err != nil {
return nil, err
}
return task.processReturnValue, nil
}
// RunTaskInBackground creates task and runs it in background. It won't be run and an error // RunTaskInBackground creates task and runs it in background. It won't be run and an error
// returned if there are running tasks which are using needed resources already. // returned if there are running tasks which are using needed resources already.
func (list *List) RunTaskInBackground(name string, resources []string, process Process) (Task, *ResourceConflictError) { func (list *List) RunTaskInBackground(name string, resources []string, process Process) (Task, *ResourceConflictError) {
@@ -139,11 +150,11 @@ func (list *List) RunTaskInBackground(name string, resources []string, process P
} }
list.Unlock() list.Unlock()
retCode, err := process(aptly.Progress(task.output), task.detail) retValue, err := process(aptly.Progress(task.output), task.detail)
list.Lock() list.Lock()
{ {
task.processReturnCode = retCode task.processReturnValue = retValue
if err != nil { if err != nil {
task.output.Printf("Task failed with error: %v", err) task.output.Printf("Task failed with error: %v", err)
task.State = FAILED task.State = FAILED
+5 -4
View File
@@ -2,6 +2,7 @@ package task
import ( import (
"errors" "errors"
"github.com/aptly-dev/aptly/aptly" "github.com/aptly-dev/aptly/aptly"
// need to import as check as otherwise List is redeclared // need to import as check as otherwise List is redeclared
@@ -16,8 +17,8 @@ func (s *ListSuite) TestList(c *check.C) {
list := NewList() list := NewList()
c.Assert(len(list.GetTasks()), check.Equals, 0) c.Assert(len(list.GetTasks()), check.Equals, 0)
task, err := list.RunTaskInBackground("Successful task", nil, func(out aptly.Progress, detail *Detail) (int, error) { task, err := list.RunTaskInBackground("Successful task", nil, func(out aptly.Progress, detail *Detail) (*ProcessReturnValue, error) {
return -1, nil return nil, nil
}) })
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
list.WaitForTaskByID(task.ID) list.WaitForTaskByID(task.ID)
@@ -31,10 +32,10 @@ func (s *ListSuite) TestList(c *check.C) {
detail, _ := list.GetTaskDetailByID(task.ID) detail, _ := list.GetTaskDetailByID(task.ID)
c.Check(detail, check.Equals, struct{}{}) c.Check(detail, check.Equals, struct{}{})
task, err = list.RunTaskInBackground("Faulty task", nil, func(out aptly.Progress, detail *Detail) (int, error) { task, err = list.RunTaskInBackground("Faulty task", nil, func(out aptly.Progress, detail *Detail) (*ProcessReturnValue, error) {
detail.Store("Details") detail.Store("Details")
out.Printf("Test Progress\n") out.Printf("Test Progress\n")
return -1, errors.New("Task failed") return nil, errors.New("Task failed")
}) })
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
list.WaitForTaskByID(task.ID) list.WaitForTaskByID(task.ID)
+13 -8
View File
@@ -20,8 +20,13 @@ type PublishDetail struct {
RemainingNumberOfPackages int64 RemainingNumberOfPackages int64
} }
type ProcessReturnValue struct {
Code int
Value interface{}
}
// Process is a function implementing the actual task logic // Process is a function implementing the actual task logic
type Process func(out aptly.Progress, detail *Detail) (int, error) type Process func(out aptly.Progress, detail *Detail) (*ProcessReturnValue, error)
const ( const (
// IDLE when task is waiting // IDLE when task is waiting
@@ -36,13 +41,13 @@ const (
// Task represents as task in a queue encapsulates process code // Task represents as task in a queue encapsulates process code
type Task struct { type Task struct {
output *Output output *Output
detail *Detail detail *Detail
process Process process Process
processReturnCode int processReturnValue *ProcessReturnValue
Name string Name string
ID int ID int
State State State State
} }
// NewTask creates new task // NewTask creates new task