mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-01-12 03:21:33 +00:00
snapshot merge: use proper REST api
- this breaks the existing api, which is only available in CI builds - improve swagger doc
This commit is contained in:
@@ -200,7 +200,7 @@ func Router(c *ctx.AptlyContext) http.Handler {
|
||||
api.GET("/snapshots/:name/packages", apiSnapshotsSearchPackages)
|
||||
api.DELETE("/snapshots/:name", apiSnapshotsDrop)
|
||||
api.GET("/snapshots/:name/diff/:withSnapshot", apiSnapshotsDiff)
|
||||
api.POST("/snapshots/merge", apiSnapshotsMerge)
|
||||
api.POST("/snapshots/:name/merge", apiSnapshotsMerge)
|
||||
api.POST("/snapshots/:name/pull", apiSnapshotsPull)
|
||||
}
|
||||
|
||||
|
||||
@@ -406,17 +406,37 @@ func apiSnapshotsSearchPackages(c *gin.Context) {
|
||||
showPackages(c, snapshot.RefList(), collectionFactory)
|
||||
}
|
||||
|
||||
// POST /api/snapshots/merge
|
||||
type snapshotsMergeParams struct {
|
||||
// List of snapshot names to be merged
|
||||
Sources []string `binding:"required"`
|
||||
}
|
||||
|
||||
// @Summary Snapshot Merge
|
||||
// @Description **Merge several source snapshots into a new snapshot**
|
||||
// @Description
|
||||
// @Description Merge happens from left to right. By default, packages with the same name-architecture pair are replaced during merge (package from latest snapshot on the list wins).
|
||||
// @Description
|
||||
// @Description If only one snapshot is specified, merge copies source into destination.
|
||||
// @Tags Snapshots
|
||||
// @Param latest query int false "merge only the latest version of each package"
|
||||
// @Param no-remove query int false "all versions of packages are preserved during merge"
|
||||
// @Accept json
|
||||
// @Param name path string true "Name of the snapshot to be created"
|
||||
// @Param request body snapshotsMergeParams true "json parameters"
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Failure 400 {object} Error "Bad Request"
|
||||
// @Failure 404 {object} Error "Not Found"
|
||||
// @Failure 500 {object} Error "Internal Error"
|
||||
// @Router /api/snapshots/{name}/merge [post]
|
||||
func apiSnapshotsMerge(c *gin.Context) {
|
||||
var (
|
||||
err error
|
||||
snapshot *deb.Snapshot
|
||||
body snapshotsMergeParams
|
||||
)
|
||||
|
||||
var body struct {
|
||||
Destination string `binding:"required"`
|
||||
Sources []string `binding:"required"`
|
||||
}
|
||||
name := c.Params.ByName("name")
|
||||
|
||||
if c.Bind(&body) != nil {
|
||||
return
|
||||
@@ -456,7 +476,7 @@ func apiSnapshotsMerge(c *gin.Context) {
|
||||
resources[i] = string(sources[i].ResourceKey())
|
||||
}
|
||||
|
||||
maybeRunTaskInBackground(c, "Merge snapshot "+body.Destination, resources, func(_ aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||
maybeRunTaskInBackground(c, "Merge snapshot "+name, resources, func(_ aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||
result := sources[0].RefList()
|
||||
for i := 1; i < len(sources); i++ {
|
||||
result = result.Merge(sources[i].RefList(), overrideMatching, false)
|
||||
@@ -471,7 +491,7 @@ func apiSnapshotsMerge(c *gin.Context) {
|
||||
sourceDescription[i] = fmt.Sprintf("'%s'", s.Name)
|
||||
}
|
||||
|
||||
snapshot = deb.NewSnapshotFromRefList(body.Destination, sources, result,
|
||||
snapshot = deb.NewSnapshotFromRefList(name, sources, result,
|
||||
fmt.Sprintf("Merged from sources: %s", strings.Join(sourceDescription, ", ")))
|
||||
|
||||
err = collectionFactory.SnapshotCollection().Add(snapshot)
|
||||
@@ -483,27 +503,36 @@ func apiSnapshotsMerge(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
type snapshotsPullBody struct {
|
||||
// Source name where packages and dependencies will be searched
|
||||
type snapshotsPullParams struct {
|
||||
// Source name to be searched for packages and dependencies
|
||||
Source string `binding:"required" json:"Source" example:"source-snapshot"`
|
||||
// Name of the snapshot that will be created
|
||||
// Name of the snapshot to be created
|
||||
Destination string `binding:"required" json:"Destination" example:"idestination-snapshot"`
|
||||
// List of package queries, in the simplest form, name of package to be pulled from
|
||||
// List of package queries (i.e. name of package to be pulled from `Source`)
|
||||
Queries []string `binding:"required" json:"Queries" example:"xserver-xorg"`
|
||||
// List of architectures (optional)
|
||||
Architectures []string ` json:"Architectures" example:"amd64, armhf"`
|
||||
}
|
||||
|
||||
// @Summary Snapshot Pull
|
||||
// @Description Pulls new packages (along with its dependencies) to name snapshot from source snapshot. Also pull command can upgrade package versions if name snapshot already contains packages being pulled. New snapshot destination is created as result of this process.
|
||||
// @Description **Pulls new packages and dependencies from a source snapshot into a new snapshot**
|
||||
// @Description
|
||||
// @Description May also upgrade package versions if name snapshot already contains packages being pulled. New snapshot `Destination` is created as result of this process.
|
||||
// @Description If architectures are limited (with config architectures or parameter `Architectures`, only mentioned architectures are processed, otherwise aptly will process all architectures in the snapshot.
|
||||
// @Description If following dependencies by source is enabled (using dependencyFollowSource config), pulling binary packages would also pull corresponding source packages as well.
|
||||
// @Description By default aptly would remove packages matching name and architecture while importing: e.g. when importing software_1.3_amd64, package software_1.2.9_amd64 would be removed.
|
||||
// @Description
|
||||
// @Description With flag `no-remove` both package versions would stay in the snapshot.
|
||||
// @Description
|
||||
// @Description Aptly pulls first package matching each of package queries, but with flag -all-matches all matching packages would be pulled.
|
||||
// @Tags Snapshots
|
||||
// @Param all-matches query int false "all-matches: 1 to enable"
|
||||
// @Param dry-run query int false "dry-run: 1 to enable"
|
||||
// @Param no-deps query int false "no-deps: 1 to enable"
|
||||
// @Param no-remove query int false "no-remove: 1 to enable"
|
||||
// @Param all-matches query int false "pull all the packages that satisfy the dependency version requirements (default is to pull first matching package): 1 to enable"
|
||||
// @Param dry-run query int false "don’t create destination snapshot, just show what would be pulled: 1 to enable"
|
||||
// @Param no-deps query int false "don’t process dependencies, just pull listed packages: 1 to enable"
|
||||
// @Param no-remove query int false "don’t remove other package versions when pulling package: 1 to enable"
|
||||
// @Accept json
|
||||
// @Param name path string true "Snapshot where packages and dependencies will be pulled to"
|
||||
// @Param request body snapshotsPullBody true "See api.snapshotsPullBody"
|
||||
// @Param name path string true "Name of the snapshot to be created"
|
||||
// @Param request body snapshotsPullParams true "json parameters"
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Failure 400 {object} Error "Bad Request"
|
||||
@@ -514,7 +543,7 @@ func apiSnapshotsPull(c *gin.Context) {
|
||||
var (
|
||||
err error
|
||||
destinationSnapshot *deb.Snapshot
|
||||
body snapshotsPullBody
|
||||
body snapshotsPullParams
|
||||
)
|
||||
|
||||
name := c.Params.ByName("name")
|
||||
|
||||
@@ -308,7 +308,7 @@ class SnapshotsAPITestDiff(APITest):
|
||||
|
||||
class SnapshotsAPITestMerge(APITest):
|
||||
"""
|
||||
POST /api/snapshots, POST /api/snapshots/merge, GET /api/snapshots/:name, DELETE /api/snapshots/:name
|
||||
POST /api/snapshots, POST /api/snapshots/:name/merge, GET /api/snapshots/:name, DELETE /api/snapshots/:name
|
||||
"""
|
||||
|
||||
def check(self):
|
||||
@@ -325,9 +325,8 @@ class SnapshotsAPITestMerge(APITest):
|
||||
# create merge snapshot
|
||||
merged_name = self.random_name()
|
||||
task = self.post_task(
|
||||
"/api/snapshots/merge",
|
||||
f"/api/snapshots/{merged_name}/merge",
|
||||
json={
|
||||
"Destination": merged_name,
|
||||
"Sources": [source["Name"] for source in sources],
|
||||
},
|
||||
)
|
||||
@@ -352,7 +351,7 @@ class SnapshotsAPITestMerge(APITest):
|
||||
# create merge snapshot without sources
|
||||
merged_name = self.random_name()
|
||||
resp = self.post(
|
||||
"/api/snapshots/merge", json={"Destination": merged_name, "Sources": []}
|
||||
f"/api/snapshots/{merged_name}/merge", json={"Sources": []}
|
||||
)
|
||||
self.check_equal(resp.status_code, 400)
|
||||
self.check_equal(
|
||||
@@ -364,8 +363,8 @@ class SnapshotsAPITestMerge(APITest):
|
||||
merged_name = self.random_name()
|
||||
non_existing_source = self.random_name()
|
||||
resp = self.post(
|
||||
"/api/snapshots/merge",
|
||||
json={"Destination": merged_name, "Sources": [non_existing_source]},
|
||||
f"/api/snapshots/{merged_name}/merge",
|
||||
json={"Sources": [non_existing_source]},
|
||||
)
|
||||
self.check_equal(
|
||||
resp.json()["error"], f"snapshot with name {non_existing_source} not found"
|
||||
@@ -377,8 +376,8 @@ class SnapshotsAPITestMerge(APITest):
|
||||
# create merge snapshot with used name
|
||||
merged_name = sources[0]["Name"]
|
||||
resp = self.post(
|
||||
"/api/snapshots/merge",
|
||||
json={"Destination": merged_name, "Sources": [source["Name"] for source in sources]},
|
||||
f"/api/snapshots/{merged_name}/merge",
|
||||
json={"Sources": [source["Name"] for source in sources]},
|
||||
)
|
||||
self.check_equal(
|
||||
resp.json()["error"],
|
||||
@@ -389,9 +388,8 @@ class SnapshotsAPITestMerge(APITest):
|
||||
# create merge snapshot with "latest" and "no-remove" flags (should fail)
|
||||
merged_name = self.random_name()
|
||||
resp = self.post(
|
||||
"/api/snapshots/merge",
|
||||
f"/api/snapshots/{merged_name}/merge",
|
||||
json={
|
||||
"Destination": merged_name,
|
||||
"Sources": [source["Name"] for source in sources],
|
||||
},
|
||||
params={"latest": "1", "no-remove": "1"},
|
||||
@@ -404,7 +402,7 @@ class SnapshotsAPITestMerge(APITest):
|
||||
|
||||
class SnapshotsAPITestPull(APITest):
|
||||
"""
|
||||
POST /api/snapshots/pull, POST /api/snapshots, GET /api/snapshots/:name/packages?name=:package_name
|
||||
POST /api/snapshots/:name/pull, POST /api/snapshots, GET /api/snapshots/:name/packages?name=:package_name
|
||||
"""
|
||||
|
||||
def check(self):
|
||||
@@ -448,7 +446,7 @@ class SnapshotsAPITestPull(APITest):
|
||||
self.check_equal(resp.status_code, 200)
|
||||
|
||||
# dry run, all-matches
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull?dry-run=1&all-matches=1", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull?dry-run=1&all-matches=1", json={
|
||||
'Source': snapshot_repo_with_libboost,
|
||||
'Destination': snapshot_pull_libboost,
|
||||
'Queries': [
|
||||
@@ -462,14 +460,14 @@ class SnapshotsAPITestPull(APITest):
|
||||
self.check_equal(resp.status_code, 200)
|
||||
|
||||
# missing argument
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
'Source': snapshot_repo_with_libboost,
|
||||
'Destination': snapshot_pull_libboost,
|
||||
})
|
||||
self.check_equal(resp.status_code, 400)
|
||||
|
||||
# dry run, emtpy architectures
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull?dry-run=1", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull?dry-run=1", json={
|
||||
'Source': snapshot_repo_with_libboost,
|
||||
'Destination': snapshot_pull_libboost,
|
||||
'Queries': [
|
||||
@@ -490,7 +488,7 @@ class SnapshotsAPITestPull(APITest):
|
||||
self.check_equal(resp.status_code, 404)
|
||||
|
||||
# dry run, non-existing source
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull?dry-run=1", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull?dry-run=1", json={
|
||||
'Source': "asd123",
|
||||
'Destination': snapshot_pull_libboost,
|
||||
'Queries': [
|
||||
@@ -500,7 +498,7 @@ class SnapshotsAPITestPull(APITest):
|
||||
self.check_equal(resp.status_code, 404)
|
||||
|
||||
# snapshot pull
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
'Source': snapshot_repo_with_libboost,
|
||||
'Destination': snapshot_pull_libboost,
|
||||
'Queries': [
|
||||
@@ -525,7 +523,7 @@ class SnapshotsAPITestPull(APITest):
|
||||
# pull from non-existing source
|
||||
non_existing_source = self.random_name()
|
||||
destination = self.random_name()
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
'Source': non_existing_source,
|
||||
'Destination': destination,
|
||||
'Queries': [
|
||||
@@ -541,7 +539,7 @@ class SnapshotsAPITestPull(APITest):
|
||||
# pull to non-existing snapshot
|
||||
non_existing_snapshot = self.random_name()
|
||||
destination = self.random_name()
|
||||
resp = self.post("/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
resp = self.post(f"/api/snapshots/{snapshot_empty_repo}/pull", json={
|
||||
'Source': non_existing_snapshot,
|
||||
'Destination': destination,
|
||||
'Queries': [
|
||||
|
||||
Reference in New Issue
Block a user