Merge branch 'snapshot-api' of https://github.com/lebauce/aptly into lebauce-snapshot-api

Conflicts:
	api/router.go
	system/t12_api/__init__.py
This commit is contained in:
Andrey Smirnov
2015-01-22 21:29:58 +03:00
10 changed files with 768 additions and 78 deletions
+19 -11
View File
@@ -178,6 +178,15 @@ func apiReposPackagesShow(c *gin.Context) {
return return
} }
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(500, err)
return
}
list.PrepareIndex()
result := []*deb.Package{}
queryS := c.Request.URL.Query().Get("q") queryS := c.Request.URL.Query().Get("q")
if queryS != "" { if queryS != "" {
q, err := query.Parse(queryS) q, err := query.Parse(queryS)
@@ -186,14 +195,6 @@ func apiReposPackagesShow(c *gin.Context) {
return return
} }
list, err := deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
if err != nil {
c.Fail(500, err)
return
}
list.PrepareIndex()
withDeps := c.Request.URL.Query().Get("withDeps") == "1" withDeps := c.Request.URL.Query().Get("withDeps") == "1"
architecturesList := []string{} architecturesList := []string{}
@@ -212,16 +213,23 @@ func apiReposPackagesShow(c *gin.Context) {
} }
} }
result, err := list.Filter([]deb.PackageQuery{q}, withDeps, list, err = list.Filter([]deb.PackageQuery{q}, withDeps,
nil, context.DependencyOptions(), architecturesList) nil, context.DependencyOptions(), architecturesList)
if err != nil { if err != nil {
c.Fail(500, err) c.Fail(500, err)
return return
} }
}
c.JSON(200, result.Strings()) 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 { } else {
c.JSON(200, repo.RefList().Strings()) c.JSON(200, list.Strings())
} }
} }
+16
View File
@@ -34,6 +34,12 @@ func Router(c *ctx.AptlyContext) http.Handler {
root.POST("/repos/:name/file/:dir/:file", apiReposPackageFromFile) root.POST("/repos/:name/file/:dir/:file", apiReposPackageFromFile)
root.POST("/repos/:name/file/:dir", apiReposPackageFromDir) root.POST("/repos/:name/file/:dir", apiReposPackageFromDir)
root.POST("/repos/:name/snapshots", apiSnapshotsCreateFromRepository)
}
{
root.POST("/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror)
} }
{ {
@@ -52,6 +58,16 @@ func Router(c *ctx.AptlyContext) http.Handler {
root.DELETE("/publish/:prefix/:distribution", apiPublishDrop) root.DELETE("/publish/:prefix/:distribution", apiPublishDrop)
} }
{
root.GET("/snapshots", apiSnapshotsList)
root.POST("/snapshots", apiSnapshotsCreate)
root.PUT("/snapshots/:name", apiSnapshotsUpdate)
root.GET("/snapshots/:name", apiSnapshotsShow)
root.GET("/snapshots/:name/packages", apiSnapshotsSearchPackages)
root.DELETE("/snapshots/:name", apiSnapshotsDrop)
root.POST("/snapshots/:name/diff/:withSnapshot", apiSnapshotsDiff)
}
{ {
root.GET("/graph.:ext", apiGraph) root.GET("/graph.:ext", apiGraph)
} }
+460
View File
@@ -0,0 +1,460 @@
package api
import (
"fmt"
"sort"
"github.com/gin-gonic/gin"
"github.com/smira/aptly/deb"
"github.com/smira/aptly/query"
)
// GET /api/snapshots
func apiSnapshotsList(c *gin.Context) {
SortMethodString := c.Request.URL.Query().Get("sort")
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
if SortMethodString != "" {
collection.Sort(SortMethodString)
}
result := []*deb.Snapshot{}
collection.ForEach(func(snapshot *deb.Snapshot) error {
result = append(result, snapshot)
return nil
})
c.JSON(200, result)
}
// POST /api/mirrors/:name/snapshots/
func apiSnapshotsCreateFromMirror(c *gin.Context) {
var (
err error
repo *deb.RemoteRepo
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().RemoteRepoCollection()
collection.Lock()
defer collection.Unlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
repo, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = repo.CheckLock()
if err != nil {
c.Fail(409, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
snapshot, err = deb.NewSnapshotFromRepository(b.Name, repo)
if err != nil {
c.Fail(400, err)
return
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(201, snapshot)
}
// POST /api/snapshots
func apiSnapshotsCreate(c *gin.Context) {
var (
err error
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
SourceIDs []string
PackageRefs []string
}
if !c.Bind(&b) {
return
}
if b.Description == "" {
if len(b.SourceIDs) + len(b.PackageRefs) == 0 {
b.Description = "Created as empty"
}
}
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
sources := make([]*deb.Snapshot, len(b.SourceIDs))
for i := 0; i < len(b.SourceIDs); i++ {
sources[i], err = snapshotCollection.ByUUID(b.SourceIDs[i])
if err != nil {
c.Fail(404, err)
return
}
err = snapshotCollection.LoadComplete(sources[i])
if err != nil {
c.Fail(500, err)
return
}
}
packageRefs := make([][]byte, len(b.PackageRefs))
for i, ref := range b.PackageRefs {
packageRefs[i] = []byte(ref)
}
packageRefList := &deb.PackageRefList{packageRefs}
snapshot = deb.NewSnapshotFromRefList(b.Name, sources, packageRefList, b.Description)
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(201, snapshot)
}
// POST /api/repos/:name/snapshots/:snapname
func apiSnapshotsCreateFromRepository(c *gin.Context) {
var (
err error
repo *deb.LocalRepo
snapshot *deb.Snapshot
)
var b struct {
Name string `binding:"required"`
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.Lock()
defer snapshotCollection.Unlock()
repo, err = collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(repo)
if err != nil {
c.Fail(500, err)
return
}
snapshot, err = deb.NewSnapshotFromLocalRepo(b.Name, repo)
if err != nil {
c.Fail(400, err)
return
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = snapshotCollection.Add(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(201, snapshot)
}
// PUT /api/snapshots/:name
func apiSnapshotsUpdate(c *gin.Context) {
var (
err error
snapshot *deb.Snapshot
)
var b struct {
Name string
Description string
}
if !c.Bind(&b) {
return
}
collection := context.CollectionFactory().SnapshotCollection()
collection.Lock()
defer collection.Unlock()
snapshot, err = context.CollectionFactory().SnapshotCollection().ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
_, err = context.CollectionFactory().SnapshotCollection().ByName(b.Name)
if err == nil {
c.Fail(409, fmt.Errorf("unable to rename: snapshot %s already exists", b.Name))
return
}
if b.Name != "" {
snapshot.Name = b.Name
}
if b.Description != "" {
snapshot.Description = b.Description
}
err = context.CollectionFactory().SnapshotCollection().Update(snapshot)
if err != nil {
c.Fail(403, err)
return
}
c.JSON(200, snapshot)
}
// GET /api/snapshots/:name
func apiSnapshotsShow(c *gin.Context) {
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshot, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, snapshot)
}
// DELETE /api/snapshots/:name
func apiSnapshotsDrop(c *gin.Context) {
name := c.Params.ByName("name")
force := c.Request.URL.Query().Get("force") == "1"
collection := context.CollectionFactory().LocalRepoCollection()
collection.Lock()
defer collection.Unlock()
snapshotCollection := context.CollectionFactory().SnapshotCollection()
snapshotCollection.RLock()
defer snapshotCollection.RUnlock()
publishedCollection := context.CollectionFactory().PublishedRepoCollection()
publishedCollection.RLock()
defer publishedCollection.RUnlock()
snapshot, err := snapshotCollection.ByName(name)
if err != nil {
c.Fail(404, err)
return
}
published := publishedCollection.BySnapshot(snapshot)
if len(published) > 0 {
for _, repo := range published {
err = publishedCollection.LoadComplete(repo, context.CollectionFactory())
if err != nil {
c.Fail(500, err)
return
}
}
c.Fail(409, fmt.Errorf("unable to drop: snapshot is published"))
return
}
if !force {
snapshots := snapshotCollection.BySnapshotSource(snapshot)
if len(snapshots) > 0 {
c.Fail(409, fmt.Errorf("won't delete snapshot that was used as source for other snapshots, use ?force=1 to override"))
return
}
}
err = context.CollectionFactory().SnapshotCollection().Drop(snapshot)
if err != nil {
c.Fail(500, err)
return
}
c.JSON(200, gin.H{})
}
// POST /api/snapshots/:name/diff/:name2
func apiSnapshotsDiff(c *gin.Context) {
onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshotA, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
snapshotB, err := collection.ByName(c.Params.ByName("withSnapshot"))
if err != nil {
c.Fail(404, err)
return
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotA)
if err != nil {
c.Fail(500, err)
return
}
err = context.CollectionFactory().SnapshotCollection().LoadComplete(snapshotB)
if err != nil {
c.Fail(500, err)
return
}
// Calculate diff
diff, err := snapshotA.RefList().Diff(snapshotB.RefList(), context.CollectionFactory().PackageCollection())
if err != nil {
c.Fail(500, err)
return
}
result := []deb.PackageDiff{}
for _, pdiff := range diff {
if onlyMatching && (pdiff.Left == nil || pdiff.Right == nil) {
continue
}
result = append(result, pdiff)
}
c.JSON(200, result)
}
// GET /api/snapshots/:name/packages
func apiSnapshotsSearchPackages(c *gin.Context) {
collection := context.CollectionFactory().SnapshotCollection()
collection.RLock()
defer collection.RUnlock()
snapshot, err := collection.ByName(c.Params.ByName("name"))
if err != nil {
c.Fail(404, err)
return
}
err = collection.LoadComplete(snapshot)
if err != nil {
c.Fail(500, err)
return
}
reflist := snapshot.RefList()
result := []*deb.Package{}
list, err := deb.NewPackageListFromRefList(reflist, context.CollectionFactory().PackageCollection(), context.Progress())
if err != nil {
c.Fail(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.Fail(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.Fail(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.Fail(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())
}
}
+10 -65
View File
@@ -4,49 +4,8 @@ import (
"fmt" "fmt"
"github.com/smira/aptly/deb" "github.com/smira/aptly/deb"
"github.com/smira/commander" "github.com/smira/commander"
"sort"
) )
// Snapshot sorting methods
const (
SortName = iota
SortTime
)
type snapshotListToSort struct {
list []*deb.Snapshot
sortMethod int
}
func parseSortMethod(sortMethod string) (int, error) {
switch sortMethod {
case "time", "Time":
return SortTime, nil
case "name", "Name":
return SortName, nil
}
return -1, fmt.Errorf("sorting method \"%s\" unknown", sortMethod)
}
func (s snapshotListToSort) Swap(i, j int) {
s.list[i], s.list[j] = s.list[j], s.list[i]
}
func (s snapshotListToSort) Less(i, j int) bool {
switch s.sortMethod {
case SortName:
return s.list[i].Name < s.list[j].Name
case SortTime:
return s.list[i].CreatedAt.Before(s.list[j].CreatedAt)
}
panic("unknown sort method")
}
func (s snapshotListToSort) Len() int {
return len(s.list)
}
func aptlySnapshotList(cmd *commander.Command, args []string) error { func aptlySnapshotList(cmd *commander.Command, args []string) error {
var err error var err error
if len(args) != 0 { if len(args) != 0 {
@@ -57,44 +16,30 @@ func aptlySnapshotList(cmd *commander.Command, args []string) error {
raw := cmd.Flag.Lookup("raw").Value.Get().(bool) raw := cmd.Flag.Lookup("raw").Value.Get().(bool)
sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string) sortMethodString := cmd.Flag.Lookup("sort").Value.Get().(string)
snapshotsToSort := &snapshotListToSort{} collection := context.CollectionFactory().SnapshotCollection()
snapshotsToSort.list = make([]*deb.Snapshot, context.CollectionFactory().SnapshotCollection().Len()) collection.Sort(sortMethodString)
snapshotsToSort.sortMethod, err = parseSortMethod(sortMethodString)
if err != nil {
return err
}
i := 0
context.CollectionFactory().SnapshotCollection().ForEach(func(snapshot *deb.Snapshot) error {
snapshotsToSort.list[i] = snapshot
i++
return nil
})
context.CloseDatabase()
sort.Sort(snapshotsToSort)
if raw { if raw {
for _, snapshot := range snapshotsToSort.list { collection.ForEach(func(snapshot *deb.Snapshot) error {
fmt.Printf("%s\n", snapshot.Name) fmt.Printf("%s\n", snapshot.Name)
} return nil
})
} else { } else {
if len(snapshotsToSort.list) > 0 { if collection.Len() > 0 {
fmt.Printf("List of snapshots:\n") fmt.Printf("List of snapshots:\n")
for _, snapshot := range snapshotsToSort.list { collection.ForEach(func(snapshot *deb.Snapshot) error {
fmt.Printf(" * %s\n", snapshot.String()) fmt.Printf(" * %s\n", snapshot.String())
} return nil
})
fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n") fmt.Printf("\nTo get more information about snapshot, run `aptly snapshot show <name>`.\n")
} else { } else {
fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n") fmt.Printf("\nNo snapshots found, create one with `aptly snapshot create...`.\n")
} }
} }
return err
return err
} }
func makeCmdSnapshotList() *commander.Command { func makeCmdSnapshotList() *commander.Command {
+8 -2
View File
@@ -9,6 +9,8 @@ import (
"strings" "strings"
) )
type Hash uint64
// Package is single instance of Debian package // Package is single instance of Debian package
type Package struct { type Package struct {
// Basic package properties // Basic package properties
@@ -27,7 +29,7 @@ type Package struct {
// Is this udeb package // Is this udeb package
IsUdeb bool IsUdeb bool
// Hash of files section // Hash of files section
FilesHash uint64 FilesHash Hash
// Is this >= 0.6 package? // Is this >= 0.6 package?
V06Plus bool V06Plus bool
// Offload fields // Offload fields
@@ -38,6 +40,10 @@ type Package struct {
collection *PackageCollection collection *PackageCollection
} }
func (h *Hash) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%08x\"", *h)), nil
}
// NewPackageFromControlFile creates Package from parsed Debian control file // NewPackageFromControlFile creates Package from parsed Debian control file
func NewPackageFromControlFile(input Stanza) *Package { func NewPackageFromControlFile(input Stanza) *Package {
result := &Package{ result := &Package{
@@ -398,7 +404,7 @@ func (p *Package) Files() PackageFiles {
// UpdateFiles saves new state of files // UpdateFiles saves new state of files
func (p *Package) UpdateFiles(files PackageFiles) { func (p *Package) UpdateFiles(files PackageFiles) {
p.files = &files p.files = &files
p.FilesHash = files.Hash() p.FilesHash = Hash(files.Hash())
} }
// Stanza creates original stanza from package // Stanza creates original stanza from package
+56
View File
@@ -9,6 +9,7 @@ import (
"github.com/smira/aptly/utils" "github.com/smira/aptly/utils"
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
"log" "log"
"sort"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -327,3 +328,58 @@ func (collection *SnapshotCollection) Drop(snapshot *Snapshot) error {
return collection.db.Delete(snapshot.RefKey()) return collection.db.Delete(snapshot.RefKey())
} }
// Snapshot sorting methods
const (
SortName = iota
SortTime
)
type snapshotListToSort struct {
list []*Snapshot
sortMethod int
}
func parseSortMethod(sortMethod string) (int, error) {
switch sortMethod {
case "time", "Time":
return SortTime, nil
case "name", "Name":
return SortName, nil
}
return -1, fmt.Errorf("sorting method \"%s\" unknown", sortMethod)
}
func (s snapshotListToSort) Swap(i, j int) {
s.list[i], s.list[j] = s.list[j], s.list[i]
}
func (s snapshotListToSort) Less(i, j int) bool {
switch s.sortMethod {
case SortName:
return s.list[i].Name < s.list[j].Name
case SortTime:
return s.list[i].CreatedAt.Before(s.list[j].CreatedAt)
}
panic("unknown sort method")
}
func (s snapshotListToSort) Len() int {
return len(s.list)
}
func (collection *SnapshotCollection) Sort(sortMethodString string) error {
var err error
snapshotsToSort := &snapshotListToSort{}
snapshotsToSort.list = collection.list
snapshotsToSort.sortMethod, err = parseSortMethod(sortMethodString)
if err != nil {
return err
}
sort.Sort(snapshotsToSort)
collection.list = snapshotsToSort.list
return err
}
+8
View File
@@ -47,6 +47,14 @@ class APITest(BaseTest):
kwargs["headers"]["Content-Type"] = "application/json" kwargs["headers"]["Content-Type"] = "application/json"
return requests.post("http://%s%s" % (self.base_url, uri), *args, **kwargs) return requests.post("http://%s%s" % (self.base_url, uri), *args, **kwargs)
def put(self, uri, *args, **kwargs):
if "json" in kwargs:
kwargs["data"] = json.dumps(kwargs.pop("json"))
if not "headers" in kwargs:
kwargs["headers"] = {}
kwargs["headers"]["Content-Type"] = "application/json"
return requests.put("http://%s%s" % (self.base_url, uri), *args, **kwargs)
def delete(self, uri, *args, **kwargs): def delete(self, uri, *args, **kwargs):
if "json" in kwargs: if "json" in kwargs:
kwargs["data"] = json.dumps(kwargs.pop("json")) kwargs["data"] = json.dumps(kwargs.pop("json"))
+11
View File
@@ -267,6 +267,17 @@ class BaseTest(object):
if a != b: if a != b:
self.verify_match(a, b, match_prepare=pprint.pformat) self.verify_match(a, b, match_prepare=pprint.pformat)
def check_subset(self, a, b):
diff = ''
for k, v in a.items():
if k not in b:
diff += "unexpected key '%s'\n" % (k,)
elif b[k] != v:
diff += "wrong value '%s' for key '%s', expected '%s'\n" % (v, k, b[k])
if diff:
raise Exception("content doesn't match:\n" + diff)
def verify_match(self, a, b, match_prepare=None): def verify_match(self, a, b, match_prepare=None):
if match_prepare is not None: if match_prepare is not None:
a = match_prepare(a) a = match_prepare(a)
+1
View File
@@ -7,3 +7,4 @@ from .files import *
from .publish import * from .publish import *
from .version import * from .version import *
from .graph import * from .graph import *
from .snapshots import *
+179
View File
@@ -0,0 +1,179 @@
from api_lib import APITest
class SnapshotsAPITestCreateShow(APITest):
"""
GET /api/snapshots/:name, POST /api/snapshots, GET /api/snapshots/:name/packages
"""
def check(self):
snapshot_name = self.random_name()
snapshot_desc = {u'Description': u'fun snapshot',
u'Name': snapshot_name}
resp = self.post("/api/snapshots", json=snapshot_desc)
self.check_subset(snapshot_desc, resp.json())
self.check_equal(resp.status_code, 201)
self.check_subset(snapshot_desc, self.get("/api/snapshots/" + snapshot_name).json())
self.check_equal(self.get("/api/snapshots/" + snapshot_name).status_code, 200)
self.check_equal(self.get("/api/snapshots/" + self.random_name()).status_code, 404)
class SnapshotsAPITestCreateFromRefs(APITest):
"""
GET /api/snapshots/:name, POST /api/snapshots, GET /api/snapshots/:name/packages
"""
def check(self):
snapshot_name = self.random_name()
snapshot_desc = {u'Description': u'fun snapshot',
u'Name': snapshot_name,
u'SourceIDs': ['123']}
resp = self.post("/api/snapshots", json=snapshot_desc)
self.check_equal(resp.status_code, 404)
resp = self.post("/api/snapshots", json={"Name": self.random_name()})
self.check_equal(resp.status_code, 201)
snapshot_desc['SourceIDs'] = [resp.json()["UUID"]]
snapshot = snapshot_desc.copy()
snapshot['PackageRefs'] = ["Pi386 libboost-program-options-dev 1.49.0.1 918d2f433384e378"]
resp = self.post("/api/snapshots", json=snapshot)
self.check_equal(resp.status_code, 201)
self.check_subset(snapshot_desc, resp.json())
self.check_subset(snapshot_desc, self.get("/api/snapshots/" + snapshot_name).json())
self.check_equal(self.get("/api/snapshots/" + snapshot_name).status_code, 200)
self.check_equal(self.get("/api/snapshots/" + self.random_name()).status_code, 404)
class SnapshotsAPITestCreateFromRepo(APITest):
"""
POST /api/repos, POST /api/repos/:name/snapshots, GET /api/snapshots/:name
"""
def check(self):
repo_name = self.random_name()
snapshot_name = self.random_name()
self.check_equal(self.post("/api/repos", json={"Name": repo_name}).status_code, 201)
resp = self.post("/api/repos/" + repo_name + '/snapshots', json={'Name': snapshot_name})
self.check_equal(resp.status_code, 400)
d = self.random_name()
self.check_equal(self.upload("/api/files/" + d,
"libboost-program-options-dev_1.49.0.1_i386.deb").status_code, 200)
self.check_equal(self.post("/api/repos/" + repo_name + "/file/" + d).status_code, 200)
resp = self.post("/api/repos/" + repo_name + '/snapshots', json={'Name': snapshot_name})
self.check_equal(self.get("/api/snapshots/" + snapshot_name).status_code, 200)
self.check_subset({u'Architecture': 'i386', u'Name': 'libboost-program-options-dev', u'Version': '1.49.0.1', 'FilesHash': '918d2f433384e378'},
self.get("/api/snapshots/" + snapshot_name + "/packages?format=details").json()[0])
self.check_subset({u'Architecture': 'i386', u'Name': 'libboost-program-options-dev', u'Version': '1.49.0.1', 'FilesHash': '918d2f433384e378'},
self.get("/api/snapshots/" + snapshot_name + "/packages?format=details", params={"q": "Version (> 0.6.1-1.4)"}).json()[0])
class SnapshotsAPITestCreateUpdate(APITest):
"""
POST /api/snapshots, PUT /api/snapshots/:name, GET /api/snapshots/:name
"""
def check(self):
snapshot_name = self.random_name()
snapshot_desc = {u'Description': u'fun snapshot',
u'Name': snapshot_name}
resp = self.post("/api/snapshots", json=snapshot_desc)
self.check_equal(resp.status_code, 201)
new_snapshot_name = self.random_name()
resp = self.put("/api/snapshots/" + snapshot_name, json={'Name': new_snapshot_name,
'Description': 'New description'})
self.check_equal(resp.status_code, 200)
resp = self.get("/api/snapshots/" + new_snapshot_name)
self.check_equal(resp.status_code, 200)
self.check_subset({"Name": new_snapshot_name,
"Description": "New description"}, resp.json())
class SnapshotsAPITestCreateDelete(APITest):
"""
POST /api/snapshots, DELETE /api/snapshots/:name, GET /api/snapshots/:name
"""
def check(self):
snapshot_name = self.random_name()
snapshot_desc = {u'Description': u'fun snapshot',
u'Name': snapshot_name}
resp = self.post("/api/snapshots", json=snapshot_desc)
self.check_equal(resp.status_code, 201)
self.check_equal(self.delete("/api/snapshots/" + snapshot_name).status_code, 200)
self.check_equal(self.get("/api/snapshots/" + snapshot_name).status_code, 404)
class SnapshotsAPITestSearch(APITest):
"""
POST /api/snapshots, GET /api/snapshots?sort=name, GET /api/snapshots/:name
"""
def check(self):
repo_name = self.random_name()
self.check_equal(self.post("/api/repos", json={"Name": repo_name}).status_code, 201)
d = self.random_name()
snapshot_name = self.random_name()
self.check_equal(self.upload("/api/files/" + d,
"libboost-program-options-dev_1.49.0.1_i386.deb").status_code, 200)
self.check_equal(self.post("/api/repos/" + repo_name + "/file/" + d).status_code, 200)
resp = self.post("/api/repos/" + repo_name + '/snapshots', json={'Name': snapshot_name})
self.check_equal(resp.status_code, 201)
resp = self.get("/api/snapshots/" + snapshot_name + "/packages?q=libboost-program-options-dev&format=details")
self.check_equal(resp.status_code, 200)
self.check_equal(len(resp.json()), 1)
self.check_equal(resp.json()[0]["Name"], "libboost-program-options-dev")
resp = self.get("/api/snapshots/" + snapshot_name + "/packages")
self.check_equal(resp.status_code, 200)
self.check_equal(len(resp.json()), 1)
self.check_equal(resp.json(), ["Pi386 libboost-program-options-dev 1.49.0.1 918d2f433384e378"])
class SnapshotsAPITestDiff(APITest):
"""
POST /api/snapshot/:name/diff/:name2
"""
def check(self):
repos = [ self.random_name() for x in xrange(2) ]
snapshots = [ self.random_name() for x in xrange(2) ]
for repo_name in repos:
self.check_equal(self.post("/api/repos", json={"Name": repo_name}).status_code, 201)
d = self.random_name()
self.check_equal(self.upload("/api/files/" + d,
"libboost-program-options-dev_1.49.0.1_i386.deb").status_code, 200)
self.check_equal(self.post("/api/repos/" + repo_name + "/file/" + d).status_code, 200)
resp = self.post("/api/repos/" + repo_name + '/snapshots', json={'Name': snapshots[0]})
self.check_equal(resp.status_code, 201)
resp = self.post("/api/snapshots", json={'Name': snapshots[1]})
self.check_equal(resp.status_code, 201)
resp = self.post("/api/snapshots/" + snapshots[0] + "/diff/" + snapshots[1])
self.check_equal(resp.status_code, 200)
self.check_subset({"Right": None}, resp.json()[0])
self.check_subset({"Name": "libboost-program-options-dev"}, resp.json()[0]["Left"])