mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-08 05:50:47 +00:00
Added mirror api based on task list
This commit is contained in:
committed by
Lorenzo Bolla
parent
6ab5e60833
commit
d7ccf95499
+561
@@ -0,0 +1,561 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/aptly-dev/aptly/aptly"
|
||||
"github.com/aptly-dev/aptly/deb"
|
||||
"github.com/aptly-dev/aptly/pgp"
|
||||
"github.com/aptly-dev/aptly/query"
|
||||
"github.com/aptly-dev/aptly/task"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func getVerifier(ignoreSignatures bool, keyRings []string) (pgp.Verifier, error) {
|
||||
if ignoreSignatures {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
verifier := &pgp.GpgVerifier{}
|
||||
for _, keyRing := range keyRings {
|
||||
verifier.AddKeyring(keyRing)
|
||||
}
|
||||
|
||||
err := verifier.InitKeyring()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return verifier, nil
|
||||
}
|
||||
|
||||
// GET /api/mirrors
|
||||
func apiMirrorsList(c *gin.Context) {
|
||||
collectionFactory := context.NewCollectionFactory()
|
||||
collection := collectionFactory.RemoteRepoCollection()
|
||||
|
||||
result := []*deb.RemoteRepo{}
|
||||
collection.ForEach(func(repo *deb.RemoteRepo) error {
|
||||
result = append(result, repo)
|
||||
return nil
|
||||
})
|
||||
|
||||
c.JSON(200, result)
|
||||
}
|
||||
|
||||
// POST /api/mirrors
|
||||
func apiMirrorsCreate(c *gin.Context) {
|
||||
var err error
|
||||
var b struct {
|
||||
Name string `binding:"required"`
|
||||
ArchiveURL string `binding:"required"`
|
||||
Distribution string
|
||||
Filter string
|
||||
Components []string
|
||||
Architectures []string
|
||||
Keyrings []string
|
||||
DownloadSources bool
|
||||
DownloadUdebs bool
|
||||
FilterWithDeps bool
|
||||
SkipComponentCheck bool
|
||||
IgnoreSignatures bool
|
||||
}
|
||||
|
||||
b.DownloadSources = context.Config().DownloadSourcePackages
|
||||
b.IgnoreSignatures = context.Config().GpgDisableVerify
|
||||
b.Architectures = context.ArchitecturesList()
|
||||
|
||||
if c.Bind(&b) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
collectionFactory := context.NewCollectionFactory()
|
||||
collection := collectionFactory.RemoteRepoCollection()
|
||||
|
||||
if strings.HasPrefix(b.ArchiveURL, "ppa:") {
|
||||
b.ArchiveURL, b.Distribution, b.Components, err = deb.ParsePPA(b.ArchiveURL, context.Config())
|
||||
if err != nil {
|
||||
c.AbortWithError(400, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if b.Filter != "" {
|
||||
_, err = query.Parse(b.Filter)
|
||||
if err != nil {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to create mirror: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
repo, err := deb.NewRemoteRepo(b.Name, b.ArchiveURL, b.Distribution, b.Components, b.Architectures,
|
||||
b.DownloadSources, b.DownloadUdebs)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to create mirror: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
repo.Filter = b.Filter
|
||||
repo.FilterWithDeps = b.FilterWithDeps
|
||||
repo.SkipComponentCheck = b.SkipComponentCheck
|
||||
repo.DownloadSources = b.DownloadSources
|
||||
repo.DownloadUdebs = b.DownloadUdebs
|
||||
|
||||
verifier, err := getVerifier(b.IgnoreSignatures, b.Keyrings)
|
||||
if err != nil {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to initialize GPG verifier: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
downloader := context.NewDownloader(nil)
|
||||
err = repo.Fetch(downloader, verifier)
|
||||
if err != nil {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to fetch mirror: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = collection.Add(repo)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, fmt.Errorf("unable to add mirror: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(201, repo)
|
||||
}
|
||||
|
||||
// DELETE /api/mirrors/:name
|
||||
func apiMirrorsDrop(c *gin.Context) {
|
||||
name := c.Params.ByName("name")
|
||||
force := c.Request.URL.Query().Get("force") == "1"
|
||||
|
||||
collectionFactory := context.NewCollectionFactory()
|
||||
mirrorCollection := collectionFactory.RemoteRepoCollection()
|
||||
snapshotCollection := collectionFactory.SnapshotCollection()
|
||||
|
||||
repo, err := mirrorCollection.ByName(name)
|
||||
if err != nil {
|
||||
c.AbortWithError(404, fmt.Errorf("unable to drop: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
resources := []string{string(repo.Key())}
|
||||
taskName := fmt.Sprintf("Delete mirror %s", name)
|
||||
task, conflictErr := runTaskInBackground(taskName, resources, func(out *task.Output, detail *task.Detail) error {
|
||||
err := repo.CheckLock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to drop: %s", err)
|
||||
}
|
||||
|
||||
if !force {
|
||||
snapshots := snapshotCollection.ByRemoteRepoSource(repo)
|
||||
|
||||
if len(snapshots) > 0 {
|
||||
return fmt.Errorf("won't delete mirror with snapshots, use 'force=1' to override")
|
||||
}
|
||||
}
|
||||
|
||||
return mirrorCollection.Drop(repo)
|
||||
})
|
||||
|
||||
if conflictErr != nil {
|
||||
c.AbortWithError(409, conflictErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(202, task)
|
||||
}
|
||||
|
||||
// GET /api/mirrors/:name
|
||||
func apiMirrorsShow(c *gin.Context) {
|
||||
collectionFactory := context.NewCollectionFactory()
|
||||
collection := collectionFactory.RemoteRepoCollection()
|
||||
|
||||
name := c.Params.ByName("name")
|
||||
repo, err := collection.ByName(name)
|
||||
if err != nil {
|
||||
c.AbortWithError(404, fmt.Errorf("unable to show: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = collection.LoadComplete(repo)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, fmt.Errorf("unable to show: %s", err))
|
||||
}
|
||||
|
||||
c.JSON(200, repo)
|
||||
}
|
||||
|
||||
// GET /api/mirrors/:name/packages
|
||||
func apiMirrorsPackages(c *gin.Context) {
|
||||
collectionFactory := context.NewCollectionFactory()
|
||||
collection := collectionFactory.RemoteRepoCollection()
|
||||
|
||||
name := c.Params.ByName("name")
|
||||
repo, err := collection.ByName(name)
|
||||
if err != nil {
|
||||
c.AbortWithError(404, fmt.Errorf("unable to show: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = collection.LoadComplete(repo)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, fmt.Errorf("unable to show: %s", err))
|
||||
}
|
||||
|
||||
if repo.LastDownloadDate.IsZero() {
|
||||
c.AbortWithError(404, fmt.Errorf("unable to show package list, mirror hasn't been downloaded yet"))
|
||||
return
|
||||
}
|
||||
|
||||
reflist := repo.RefList()
|
||||
result := []*deb.Package{}
|
||||
|
||||
list, err := deb.NewPackageListFromRefList(reflist, collectionFactory.PackageCollection(), nil)
|
||||
if err != nil {
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
queryS := c.Request.URL.Query().Get("q")
|
||||
if queryS != "" {
|
||||
q, err := query.Parse(c.Request.URL.Query().Get("q"))
|
||||
if err != nil {
|
||||
c.AbortWithError(400, err)
|
||||
return
|
||||
}
|
||||
|
||||
withDeps := c.Request.URL.Query().Get("withDeps") == "1"
|
||||
architecturesList := []string{}
|
||||
|
||||
if withDeps {
|
||||
if len(context.ArchitecturesList()) > 0 {
|
||||
architecturesList = context.ArchitecturesList()
|
||||
} else {
|
||||
architecturesList = list.Architectures(false)
|
||||
}
|
||||
|
||||
sort.Strings(architecturesList)
|
||||
|
||||
if len(architecturesList) == 0 {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to determine list of architectures, please specify explicitly"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
list.PrepareIndex()
|
||||
|
||||
list, err = list.Filter([]deb.PackageQuery{q}, withDeps,
|
||||
nil, context.DependencyOptions(), architecturesList)
|
||||
if err != nil {
|
||||
c.AbortWithError(500, fmt.Errorf("unable to search: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if c.Request.URL.Query().Get("format") == "details" {
|
||||
list.ForEach(func(p *deb.Package) error {
|
||||
result = append(result, p)
|
||||
return nil
|
||||
})
|
||||
|
||||
c.JSON(200, result)
|
||||
} else {
|
||||
c.JSON(200, list.Strings())
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/mirrors/:name
|
||||
func apiMirrorsUpdate(c *gin.Context) {
|
||||
var (
|
||||
err error
|
||||
remote *deb.RemoteRepo
|
||||
)
|
||||
|
||||
var b struct {
|
||||
Name string
|
||||
ArchiveURL string
|
||||
Filter string
|
||||
Architectures []string
|
||||
Components []string
|
||||
Keyrings []string
|
||||
FilterWithDeps bool
|
||||
DownloadSources bool
|
||||
DownloadUdebs bool
|
||||
SkipComponentCheck bool
|
||||
IgnoreChecksums bool
|
||||
IgnoreSignatures bool
|
||||
ForceUpdate bool
|
||||
SkipExistingPackages bool
|
||||
MaxTries int
|
||||
}
|
||||
|
||||
collectionFactory := context.NewCollectionFactory()
|
||||
collection := collectionFactory.RemoteRepoCollection()
|
||||
|
||||
remote, err = collection.ByName(c.Params.ByName("name"))
|
||||
if err != nil {
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
b.Name = remote.Name
|
||||
b.DownloadUdebs = remote.DownloadUdebs
|
||||
b.DownloadSources = remote.DownloadSources
|
||||
b.SkipComponentCheck = remote.SkipComponentCheck
|
||||
b.FilterWithDeps = remote.FilterWithDeps
|
||||
b.Filter = remote.Filter
|
||||
b.Architectures = remote.Architectures
|
||||
b.Components = remote.Components
|
||||
|
||||
if c.Bind(&b) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if b.Name != remote.Name {
|
||||
_, err = collection.ByName(b.Name)
|
||||
if err == nil {
|
||||
c.AbortWithError(409, fmt.Errorf("unable to rename: mirror %s already exists", b.Name))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if b.DownloadUdebs != remote.DownloadUdebs {
|
||||
if remote.IsFlat() && b.DownloadUdebs {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to update: flat mirrors don't support udebs"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if b.ArchiveURL != "" {
|
||||
remote.SetArchiveRoot(b.ArchiveURL)
|
||||
}
|
||||
|
||||
remote.Name = b.Name
|
||||
remote.DownloadUdebs = b.DownloadUdebs
|
||||
remote.DownloadSources = b.DownloadSources
|
||||
remote.SkipComponentCheck = b.SkipComponentCheck
|
||||
remote.FilterWithDeps = b.FilterWithDeps
|
||||
remote.Filter = b.Filter
|
||||
remote.Architectures = b.Architectures
|
||||
remote.Components = b.Components
|
||||
|
||||
verifier, err := getVerifier(b.IgnoreSignatures, b.Keyrings)
|
||||
if err != nil {
|
||||
c.AbortWithError(400, fmt.Errorf("unable to initialize GPG verifier: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
resources := []string{string(remote.Key())}
|
||||
currTask, conflictErr := runTaskInBackground("Update mirror "+b.Name, resources, func(out *task.Output, detail *task.Detail) error {
|
||||
|
||||
downloader := context.NewDownloader(out)
|
||||
err := remote.Fetch(downloader, verifier)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
|
||||
if !b.ForceUpdate {
|
||||
err = remote.CheckLock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if b.MaxTries <= 0 {
|
||||
b.MaxTries = 1
|
||||
}
|
||||
|
||||
err = remote.DownloadPackageIndexes(out, downloader, collectionFactory, b.SkipComponentCheck, b.MaxTries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
|
||||
if remote.Filter != "" {
|
||||
var filterQuery deb.PackageQuery
|
||||
|
||||
filterQuery, err = query.Parse(remote.Filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
|
||||
_, _, err = remote.ApplyFilter(context.DependencyOptions(), filterQuery, out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
queue, downloadSize, err := remote.BuildDownloadQueue(context.PackagePool(), collectionFactory.PackageCollection(),
|
||||
collectionFactory.ChecksumCollection(), b.SkipExistingPackages)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// on any interruption, unlock the mirror
|
||||
e := context.ReOpenDatabase()
|
||||
if e == nil {
|
||||
remote.MarkAsIdle()
|
||||
collection.Update(remote)
|
||||
}
|
||||
}()
|
||||
|
||||
remote.MarkAsUpdating()
|
||||
err = collection.Update(remote)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
|
||||
context.GoContextHandleSignals()
|
||||
|
||||
count := len(queue)
|
||||
taskDetail := struct {
|
||||
TotalDownloadSize int64
|
||||
RemainingDownloadSize int64
|
||||
TotalNumberOfPackages int
|
||||
RemainingNumberOfPackages int
|
||||
}{
|
||||
downloadSize, downloadSize, count, count,
|
||||
}
|
||||
detail.Store(taskDetail)
|
||||
|
||||
downloadQueue := make(chan int)
|
||||
taskFinished := make(chan *deb.PackageDownloadTask)
|
||||
|
||||
var (
|
||||
errors []string
|
||||
errLock sync.Mutex
|
||||
)
|
||||
|
||||
pushError := func(err error) {
|
||||
errLock.Lock()
|
||||
errors = append(errors, err.Error())
|
||||
errLock.Unlock()
|
||||
}
|
||||
|
||||
go func() {
|
||||
for idx := range queue {
|
||||
select {
|
||||
case downloadQueue <- idx:
|
||||
case <-context.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
close(downloadQueue)
|
||||
}()
|
||||
|
||||
// update of task details need to be done in order
|
||||
go func() {
|
||||
for {
|
||||
task, ok := <-taskFinished
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
taskDetail.RemainingDownloadSize -= task.File.Checksums.Size
|
||||
taskDetail.RemainingNumberOfPackages--
|
||||
detail.Store(taskDetail)
|
||||
}
|
||||
}()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < context.Config().DownloadConcurrency; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case idx, ok := <-downloadQueue:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
task := &queue[idx]
|
||||
|
||||
var e error
|
||||
|
||||
// provision download location
|
||||
task.TempDownPath, e = context.PackagePool().(aptly.LocalPackagePool).GenerateTempPath(task.File.Filename)
|
||||
if e != nil {
|
||||
pushError(e)
|
||||
continue
|
||||
}
|
||||
|
||||
// download file...
|
||||
e = context.Downloader().DownloadWithChecksum(
|
||||
context,
|
||||
remote.PackageURL(task.File.DownloadURL()).String(),
|
||||
task.TempDownPath,
|
||||
&task.File.Checksums,
|
||||
b.IgnoreChecksums,
|
||||
b.MaxTries)
|
||||
if e != nil {
|
||||
pushError(e)
|
||||
continue
|
||||
}
|
||||
|
||||
task.Done = true
|
||||
taskFinished <- task
|
||||
case <-context.Done():
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait for all download goroutines to finish
|
||||
wg.Wait()
|
||||
close(taskFinished)
|
||||
|
||||
for idx := range queue {
|
||||
|
||||
task := &queue[idx]
|
||||
|
||||
if !task.Done {
|
||||
// download not finished yet
|
||||
continue
|
||||
}
|
||||
|
||||
// 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())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to import file: %s", err)
|
||||
}
|
||||
|
||||
// update "attached" files if any
|
||||
for _, additionalTask := range task.Additional {
|
||||
additionalTask.File.PoolPath = task.File.PoolPath
|
||||
additionalTask.File.Checksums = task.File.Checksums
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-context.Done():
|
||||
return fmt.Errorf("unable to update: interrupted")
|
||||
default:
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("unable to update: download errors:\n %s", strings.Join(errors, "\n "))
|
||||
}
|
||||
|
||||
remote.FinalizeDownload(collectionFactory, out)
|
||||
err = collectionFactory.RemoteRepoCollection().Update(remote)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if conflictErr != nil {
|
||||
c.AbortWithError(409, conflictErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(202, currTask)
|
||||
}
|
||||
@@ -78,6 +78,15 @@ func Router(c *ctx.AptlyContext) http.Handler {
|
||||
root.POST("/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror)
|
||||
}
|
||||
|
||||
{
|
||||
root.GET("/mirrors", apiMirrorsList)
|
||||
root.GET("/mirrors/:name", apiMirrorsShow)
|
||||
root.GET("/mirrors/:name/packages", apiMirrorsPackages)
|
||||
root.POST("/mirrors", apiMirrorsCreate)
|
||||
root.PUT("/mirrors/:name", apiMirrorsUpdate)
|
||||
root.DELETE("/mirrors/:name", apiMirrorsDrop)
|
||||
}
|
||||
|
||||
{
|
||||
root.GET("/files", apiFilesListDirs)
|
||||
root.POST("/files/:dir", apiFilesUpload)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
from api_lib import APITest
|
||||
|
||||
|
||||
class MirrorsAPITestCreateShow(APITest):
|
||||
"""
|
||||
POST /api/mirrors, GET /api/mirrors/:name/packages
|
||||
"""
|
||||
def check(self):
|
||||
mirror_name = self.random_name()
|
||||
mirror_desc = {u'Name': mirror_name,
|
||||
u'ArchiveURL': 'http://security.debian.org/',
|
||||
u'Architectures': ['amd64'],
|
||||
u'Components': ['main'],
|
||||
u'Distribution': 'wheezy/updates'}
|
||||
|
||||
resp = self.post("/api/mirrors", json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 400)
|
||||
self.check_equal({
|
||||
'error': 'unable to fetch mirror: verification of detached signature failed: exit status 2',
|
||||
}, resp.json())
|
||||
|
||||
mirror_desc[u'IgnoreSignatures'] = True
|
||||
resp = self.post("/api/mirrors", json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 201)
|
||||
|
||||
resp = self.get("/api/mirrors/" + mirror_name)
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_subset({u'Name': mirror_name,
|
||||
u'ArchiveRoot': 'http://security.debian.org/',
|
||||
u'Architectures': ['amd64'],
|
||||
u'Components': ['main'],
|
||||
u'Distribution': 'wheezy/updates'}, resp.json())
|
||||
|
||||
resp = self.get("/api/mirrors/" + mirror_desc["Name"] + "/packages")
|
||||
self.check_equal(resp.status_code, 404)
|
||||
|
||||
|
||||
class MirrorsAPITestCreateUpdate(APITest):
|
||||
"""
|
||||
POST /api/mirrors, PUT /api/mirrors/:name, GET /api/mirrors/:name/packages
|
||||
"""
|
||||
def check(self):
|
||||
mirror_name = self.random_name()
|
||||
mirror_desc = {u'Name': mirror_name,
|
||||
u'ArchiveURL': 'https://packagecloud.io/varnishcache/varnish30/debian/',
|
||||
u'Distribution': 'wheezy',
|
||||
u'Components': ['main']}
|
||||
|
||||
mirror_desc[u'IgnoreSignatures'] = True
|
||||
resp = self.post("/api/mirrors", json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 201)
|
||||
|
||||
resp = self.get("/api/mirrors/" + mirror_name + "/packages")
|
||||
self.check_equal(resp.status_code, 404)
|
||||
|
||||
mirror_desc["Name"] = self.random_name()
|
||||
resp = self.put_task("/api/mirrors/" + mirror_name, json=mirror_desc)
|
||||
self.check_equal(resp.json()["State"], 2)
|
||||
|
||||
_id = resp.json()['ID']
|
||||
resp = self.get("/api/tasks/" + str(_id) + "/detail")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(resp.json()['RemainingDownloadSize'], 0)
|
||||
self.check_equal(resp.json()['RemainingNumberOfPackages'], 0)
|
||||
|
||||
resp = self.get("/api/mirrors/" + mirror_desc["Name"])
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_subset({u'Name': mirror_desc["Name"],
|
||||
u'ArchiveRoot': 'https://packagecloud.io/varnishcache/varnish30/debian/',
|
||||
u'Distribution': 'wheezy'}, resp.json())
|
||||
|
||||
resp = self.get("/api/mirrors/" + mirror_desc["Name"] + "/packages")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
|
||||
|
||||
class MirrorsAPITestCreateDelete(APITest):
|
||||
"""
|
||||
POST /api/mirrors, DELETE /api/mirrors/:name
|
||||
"""
|
||||
def check(self):
|
||||
mirror_name = self.random_name()
|
||||
mirror_desc = {u'Name': mirror_name,
|
||||
u'ArchiveURL': 'https://packagecloud.io/varnishcache/varnish30/debian/',
|
||||
u'IgnoreSignatures': True,
|
||||
u'Distribution': 'wheezy',
|
||||
u'Components': ['main']}
|
||||
|
||||
resp = self.post("/api/mirrors", json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 201)
|
||||
|
||||
resp = self.delete_task("/api/mirrors/" + mirror_name)
|
||||
self.check_equal(resp.json()['State'], 2)
|
||||
|
||||
|
||||
class MirrorsAPITestCreateList(APITest):
|
||||
"""
|
||||
GET /api/mirrors, POST /api/mirrors, GET /api/mirrors
|
||||
"""
|
||||
def check(self):
|
||||
resp = self.get("/api/mirrors")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
count = len(resp.json())
|
||||
|
||||
mirror_name = self.random_name()
|
||||
mirror_desc = {u'Name': mirror_name,
|
||||
u'ArchiveURL': 'https://packagecloud.io/varnishcache/varnish30/debian/',
|
||||
u'IgnoreSignatures': True,
|
||||
u'Distribution': 'wheezy',
|
||||
u'Components': ['main']}
|
||||
|
||||
resp = self.post("/api/mirrors", json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 201)
|
||||
|
||||
resp = self.get("/api/mirrors")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(len(resp.json()), count + 1)
|
||||
@@ -0,0 +1,106 @@
|
||||
from api_lib import APITest
|
||||
from publish import DefaultSigningOptions
|
||||
|
||||
|
||||
class TaskAPITestParallelTasks(APITest):
|
||||
"""
|
||||
GET /api/tasks, GET /api/tasks/:id/wait, GET /api/tasks-wait
|
||||
"""
|
||||
def _create_mirror(self, dist):
|
||||
mirror_name = self.random_name()
|
||||
mirror_desc = {u'Name': mirror_name,
|
||||
u'ArchiveURL': 'https://packagecloud.io/varnishcache/varnish30/debian/',
|
||||
u'Distribution': dist,
|
||||
u'Components': ['main']}
|
||||
mirror_desc[u'IgnoreSignatures'] = True
|
||||
resp = self.post("/api/mirrors", json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 201)
|
||||
resp = self.put("/api/mirrors/" + mirror_name, json=mirror_desc)
|
||||
self.check_equal(resp.status_code, 202)
|
||||
|
||||
# check that two mirror updates cannot run at the same time
|
||||
resp2 = self.put("/api/mirrors/" + mirror_name, json=mirror_desc)
|
||||
self.check_equal(resp2.status_code, 409)
|
||||
|
||||
return resp.json()['ID'], mirror_name
|
||||
|
||||
def _create_repo(self):
|
||||
repo_name = self.random_name()
|
||||
distribution = self.random_name()
|
||||
|
||||
self.check_equal(self.post("/api/repos",
|
||||
json={
|
||||
"Name": repo_name,
|
||||
"Comment": "fun repo",
|
||||
"DefaultDistribution": distribution
|
||||
}).status_code, 201)
|
||||
d = self.random_name()
|
||||
self.check_equal(
|
||||
self.upload("/api/files/" + d, "pyspi_0.6.1-1.3.dsc",
|
||||
"pyspi_0.6.1-1.3.diff.gz",
|
||||
"pyspi_0.6.1.orig.tar.gz").status_code, 200)
|
||||
|
||||
resp = self.post("/api/repos/" + repo_name + "/file/" + d)
|
||||
self.check_equal(resp.status_code, 202)
|
||||
|
||||
return resp.json()['ID'], repo_name
|
||||
|
||||
def _wait_for_task(self, task_id):
|
||||
uri = "/api/tasks/%d/wait" % int(task_id)
|
||||
resp = self.get(uri)
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(resp.json()['State'], 2)
|
||||
|
||||
def _wait_for_all_tasks(self):
|
||||
resp = self.get("/api/tasks-wait")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
|
||||
def _snapshot(self, res_type, name):
|
||||
uri = "/api/%s/%s/snapshots" % (res_type, name)
|
||||
resp = self.post(uri, json={"Name": name})
|
||||
self.check_equal(resp.status_code, 202)
|
||||
|
||||
return resp.json()['ID']
|
||||
|
||||
def _publish(self, source_kind, name):
|
||||
resp = self.post("/api/publish",
|
||||
json={
|
||||
"SourceKind": source_kind,
|
||||
"Sources": [{"Name": name}],
|
||||
"Signing": DefaultSigningOptions,
|
||||
})
|
||||
self.check_equal(resp.status_code, 202)
|
||||
return resp.json()['ID']
|
||||
|
||||
def check(self):
|
||||
publish_task_ids = []
|
||||
mirror_task_list = []
|
||||
for mirror_dist in ['squeeze', 'jessie']:
|
||||
mirror_task_id, mirror_name = self._create_mirror(mirror_dist)
|
||||
mirror_task_list.append((mirror_task_id, mirror_name))
|
||||
repo_task_id, repo_name = self._create_repo()
|
||||
|
||||
self._wait_for_task(repo_task_id)
|
||||
|
||||
resp = self.delete("/api/tasks/%d" % repo_task_id)
|
||||
self.check_equal(resp.status_code, 200)
|
||||
resp = self.get("/api/tasks/%d" % repo_task_id)
|
||||
self.check_equal(resp.status_code, 404)
|
||||
|
||||
repo_snap_task_id = self._snapshot('repos', repo_name)
|
||||
self._wait_for_task(repo_snap_task_id)
|
||||
publish_task_ids.append(self._publish('snapshot', repo_name))
|
||||
|
||||
for mirror_task_id, mirror_name in mirror_task_list:
|
||||
self._wait_for_task(mirror_task_id)
|
||||
mirror_snap_task_id = self._snapshot('mirrors', mirror_name)
|
||||
|
||||
self._wait_for_task(mirror_snap_task_id)
|
||||
publish_task_ids.append(self._publish('snapshot', mirror_name))
|
||||
|
||||
self._wait_for_all_tasks()
|
||||
|
||||
for publish_task_id in publish_task_ids:
|
||||
resp = self.get("/api/tasks/%d" % publish_task_id)
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(resp.json()['State'], 2)
|
||||
Reference in New Issue
Block a user