Skip to content

Commit

Permalink
Allow to add/switch components of a published local repository
Browse files Browse the repository at this point in the history
This commit modifies the behavior of the publish switch method in the way, that it can also be used to add/switch components of a published local repository. Furthermore, the commit ensures that the sources returned by a published repository are ordered by their component names.

Signed-off-by: Christoph Fiehe <[email protected]>
  • Loading branch information
Christoph Fiehe committed Oct 3, 2024
1 parent aff7b0d commit 1a9638b
Show file tree
Hide file tree
Showing 42 changed files with 452 additions and 104 deletions.
144 changes: 123 additions & 21 deletions api/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ func parseEscapedPath(path string) string {
return result
}

// @Summary Get publish points
// @Description Get list of available publish points. Each publish point is returned as in show API.
// @Summary Get published repositories
// @Description Get a list of published repositories. Each published repository is returned as in "show" API.
// @Tags Publish
// @Produce json
// @Produce json
// @Success 200 {array} deb.PublishedRepo
// @Router /api/publish [get]
func apiPublishList(c *gin.Context) {
Expand Down Expand Up @@ -83,7 +83,50 @@ func apiPublishList(c *gin.Context) {
c.JSON(200, result)
}

// POST /publish/:prefix
// @Summary Show published repository
// @Description Get published repository by name.
// @Tags Publish
// @Consume json
// @Produce json
// @Param prefix path string true "publishing prefix, use ':.' instead of '.' because it is ambigious in URLs"
// @Param distribution path string true "distribution name"
// @Success 200 {object} deb.RemoteRepo
// @Failure 404 {object} Error "Published repository not found"
// @Failure 500 {object} Error "Internal Error"
// @Router /api/publish/{prefix}/{distribution} [get]
func apiPublishShow(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := parseEscapedPath(c.Params.ByName("distribution"))

collectionFactory := context.NewCollectionFactory()
collection := collectionFactory.PublishedRepoCollection()

published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
AbortWithJSONError(c, 404, fmt.Errorf("unable to show: %s", err))
return
}
err = collection.LoadComplete(published, collectionFactory)
if err != nil {
AbortWithJSONError(c, 500, fmt.Errorf("unable to show: %s", err))
return
}

c.JSON(200, published)
}

// @Summary Create published repository
// @Description Create a published repository with specified parameters.
// @Tags Publish
// @Accept json
// @Produce json
// @Param prefix path string true "publishing prefix"
// @Success 200 {object} deb.RemoteRepo
// @Failure 400 {object} Error "Bad Request"
// @Failure 404 {object} Error "Source not found"
// @Failure 500 {object} Error "Internal Error"
// @Router /api/publish/{prefix} [post]
func apiPublishRepoOrSnapshot(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
Expand Down Expand Up @@ -190,7 +233,8 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
resources = append(resources, string(published.Key()))
collection := collectionFactory.PublishedRepoCollection()

taskName := fmt.Sprintf("Publish %s: %s", b.SourceKind, strings.Join(names, ", "))
taskName := fmt.Sprintf("Publish %s repository %s/%s with components \"%s\" and sources \"%s\"",
b.SourceKind, published.StoragePrefix(), published.Distribution, strings.Join(components, `", "`), strings.Join(names, `", "`))
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
taskDetail := task.PublishDetail{
Detail: detail,
Expand Down Expand Up @@ -245,11 +289,22 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
})
}

// PUT /publish/:prefix/:distribution
// @Summary Update published repository
// @Description Update a published repository.
// @Tags Publish
// @Accept json
// @Produce json
// @Param prefix path string true "publishing prefix"
// @Param distribution path string true "distribution name"
// @Success 200 {object} deb.RemoteRepo
// @Failure 400 {object} Error "Bad Request"
// @Failure 404 {object} Error "Published repository or source not found"
// @Failure 500 {object} Error "Internal Error"
// @Router /api/publish/{prefix}/{distribution} [put]
func apiPublishUpdateSwitch(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := c.Params.ByName("distribution")
distribution := parseEscapedPath(c.Params.ByName("distribution"))

var b struct {
ForceOverwrite bool
Expand All @@ -261,6 +316,10 @@ func apiPublishUpdateSwitch(c *gin.Context) {
Component string `binding:"required"`
Name string `binding:"required"`
}
Sources []struct {
Component string `binding:"required"`
Name string `binding:"required"`
}
AcquireByHash *bool
MultiDist bool
}
Expand Down Expand Up @@ -290,22 +349,50 @@ func apiPublishUpdateSwitch(c *gin.Context) {
}

var updatedComponents []string
var updatedSnapshots []string
var updatedSources []string
var resources []string

if published.SourceKind == deb.SourceLocalRepo {
if len(b.Snapshots) > 0 {
AbortWithJSONError(c, 400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
return
}
updatedComponents = published.Components()
for _, component := range updatedComponents {
published.UpdateLocalRepo(component)

localRepoCollection := collectionFactory.LocalRepoCollection()
if len(b.Sources) > 0 {
for _, source := range b.Sources {
localRepo, err2 := localRepoCollection.ByName(source.Name)
if err2 != nil {
AbortWithJSONError(c, 404, err2)
return
}

err2 = localRepoCollection.LoadComplete(localRepo)
if err2 != nil {
AbortWithJSONError(c, 500, err2)
return
}

published.SwitchLocalRepo(source.Component, localRepo)
updatedComponents = append(updatedComponents, source.Component)
updatedSources = append(updatedSources, localRepo.Name)
}
} else {
updatedComponents = published.Components()
for _, component := range updatedComponents {
published.UpdateLocalRepo(component)
}
}
} else if published.SourceKind == "snapshot" {
for _, snapshotInfo := range b.Snapshots {
snapshotCollection := collectionFactory.SnapshotCollection()
snapshot, err2 := snapshotCollection.ByName(snapshotInfo.Name)
sources := b.Sources
if len(sources) == 0 {
// For reasons of backward compatibility, resort to the former 'Snapshots' attribute
// if the newer 'Sources' attribute is not specified.
sources = b.Snapshots
}
snapshotCollection := collectionFactory.SnapshotCollection()
for _, source := range sources {
snapshot, err2 := snapshotCollection.ByName(source.Name)
if err2 != nil {
AbortWithJSONError(c, 404, err2)
return
Expand All @@ -317,9 +404,9 @@ func apiPublishUpdateSwitch(c *gin.Context) {
return
}

published.UpdateSnapshot(snapshotInfo.Component, snapshot)
updatedComponents = append(updatedComponents, snapshotInfo.Component)
updatedSnapshots = append(updatedSnapshots, snapshot.Name)
published.SwitchSnapshot(source.Component, snapshot)
updatedComponents = append(updatedComponents, source.Component)
updatedSources = append(updatedSources, snapshot.Name)
}
} else {
AbortWithJSONError(c, 500, fmt.Errorf("unknown published repository type"))
Expand All @@ -339,7 +426,9 @@ func apiPublishUpdateSwitch(c *gin.Context) {
}

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 repository %s/%s with components \"%s\" and sources \"%s\"",
published.SourceKind, published.StoragePrefix(), published.Distribution, strings.Join(updatedComponents, `", "`),
strings.Join(updatedSources, `", "`))
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
err := published.Publish(context.PackagePool(), context, collectionFactory, signer, out, b.ForceOverwrite, b.MultiDist)
if err != nil {
Expand All @@ -363,14 +452,27 @@ func apiPublishUpdateSwitch(c *gin.Context) {
})
}

// DELETE /publish/:prefix/:distribution
// @Summary Delete published repository
// @Description Delete a published repository.
// @Tags Publish
// @Accept json
// @Produce json
// @Param prefix path string true "publishing prefix"
// @Param distribution path string true "distribution name"
// @Param force query int true "force: 1 to enable"
// @Param skipCleanup query int true "skipCleanup: 1 to enable"
// @Success 200 {object} task.ProcessReturnValue
// @Failure 400 {object} Error "Bad Request"
// @Failure 404 {object} Error "Published repository not found"
// @Failure 500 {object} Error "Internal Error"
// @Router /api/publish/{prefix}/{distribution} [delete]
func apiPublishDrop(c *gin.Context) {
force := c.Request.URL.Query().Get("force") == "1"
skipCleanup := c.Request.URL.Query().Get("SkipCleanup") == "1"

param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
distribution := c.Params.ByName("distribution")
distribution := parseEscapedPath(c.Params.ByName("distribution"))

collectionFactory := context.NewCollectionFactory()
collection := collectionFactory.PublishedRepoCollection()
Expand All @@ -383,7 +485,7 @@ func apiPublishDrop(c *gin.Context) {

resources := []string{string(published.Key())}

taskName := fmt.Sprintf("Delete published %s (%s)", prefix, distribution)
taskName := fmt.Sprintf("Delete published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
err := collection.Remove(context, storage, prefix, distribution,
collectionFactory, out, force, skipCleanup)
Expand Down
1 change: 1 addition & 0 deletions api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ func Router(c *ctx.AptlyContext) http.Handler {

{
api.GET("/publish", apiPublishList)
api.GET("/publish/:prefix/:distribution", apiPublishShow)
api.POST("/publish", apiPublishRepoOrSnapshot)
api.POST("/publish/:prefix", apiPublishRepoOrSnapshot)
api.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch)
Expand Down
3 changes: 2 additions & 1 deletion cmd/publish_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ func aptlyPublishShowTxt(_ *commander.Command, args []string) error {
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, " "))

fmt.Printf("Sources:\n")
for component, sourceID := range repo.Sources {
for _, component := range repo.Components() {
sourceID := repo.Sources[component]
var name string
if repo.SourceKind == deb.SourceSnapshot {
source, e := collectionFactory.SnapshotCollection().ByUUID(sourceID)
Expand Down
72 changes: 45 additions & 27 deletions cmd/publish_switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (
)

func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
var err error
var (
err error
names []string
)

components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
multiDist := context.Flags().Lookup("multi-dist").Value.Get().(bool)
Expand All @@ -23,11 +26,6 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
distribution := args[0]
param := "."

var (
names []string
snapshot *deb.Snapshot
)

if len(args) == len(components)+2 {
param = args[1]
names = args[2:]
Expand All @@ -42,16 +40,12 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
collectionFactory := context.NewCollectionFactory()
published, err = collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}

if published.SourceKind != deb.SourceSnapshot {
return fmt.Errorf("unable to update: not a snapshot publish")
return fmt.Errorf("unable to switch: %s", err)
}

err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
return fmt.Errorf("unable to switch: %s", err)
}

publishedComponents := published.Components()
Expand All @@ -60,21 +54,45 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
}

if len(names) != len(components) {
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
return fmt.Errorf("mismatch in number of components (%d) and sources (%d)", len(components), len(names))
}

for i, component := range components {
snapshot, err = collectionFactory.SnapshotCollection().ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
if published.SourceKind == deb.SourceLocalRepo {
localRepoCollection := collectionFactory.LocalRepoCollection()
for i, component := range components {
localRepo, err := localRepoCollection.ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}

err = collectionFactory.SnapshotCollection().LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}
err = localRepoCollection.LoadComplete(localRepo)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}

published.UpdateSnapshot(component, snapshot)
context.Progress().Printf("Preparing to switch package source of component %q to local repository %q...\n",
component, localRepo.Name)
published.SwitchLocalRepo(component, localRepo)
}
} else if published.SourceKind == "snapshot" {
snapshotCollection := collectionFactory.SnapshotCollection()
for i, component := range components {
snapshot, err := snapshotCollection.ByName(names[i])
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}

err = snapshotCollection.LoadComplete(snapshot)
if err != nil {
return fmt.Errorf("unable to switch: %s", err)
}

context.Progress().Printf("Preparing to switch package source of component %q to snapshot %q...\n",
component, snapshot.Name)
published.SwitchSnapshot(component, snapshot)
}
} else {
return fmt.Errorf("unknown published repository type")
}

signer, err := getSigner(context.Flags())
Expand Down Expand Up @@ -115,18 +133,18 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
}
}

context.Progress().Printf("\nPublish for snapshot %s has been successfully switched to new snapshot.\n", published.String())
context.Progress().Printf("\nPublished %s repository %s has been successfully switched to new source.\n", published.SourceKind, published.String())

return err
}

func makeCmdPublishSwitch() *commander.Command {
cmd := &commander.Command{
Run: aptlyPublishSwitch,
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-snapshot>",
Short: "update published repository by switching to new snapshot",
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-source>",
Short: "update published repository by switching to new source",
Long: `
Command switches in-place published snapshots with new snapshot contents. All
Command switches in-place published snapshots with new source contents. All
publishing parameters are preserved (architecture list, distribution,
component).
Expand Down
2 changes: 1 addition & 1 deletion cmd/publish_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
}
}

context.Progress().Printf("\nPublish for local repo %s has been successfully updated.\n", published.String())
context.Progress().Printf("\nPublished local repository %s has been successfully updated.\n", published.String())

return err
}
Expand Down
Loading

0 comments on commit 1a9638b

Please sign in to comment.