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 <c.fiehe@eurodata.de>
  • Loading branch information
Christoph Fiehe committed Oct 2, 2024
1 parent aff7b0d commit eb6acfd
Show file tree
Hide file tree
Showing 24 changed files with 411 additions and 86 deletions.
137 changes: 118 additions & 19 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 @@ -245,11 +288,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 +315,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 +348,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 +403,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 +425,7 @@ 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 (%s): %s", published.SourceKind, 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 +449,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 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: 43 additions & 29 deletions cmd/publish_switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,23 @@ 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)

if len(args) < len(components)+1 || len(args) > len(components)+2 {
if len(args) < len(components) + 1 || len(args) > len(components) + 2 {
cmd.Usage()
return commander.ErrCommandError
}

distribution := args[0]
param := "."

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

if len(args) == len(components)+2 {
if len(args) == len(components) + 2 {
param = args[1]
names = args[2:]
} else {
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,41 @@ 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)
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)
}

published.SwitchSnapshot(component, snapshot)
}
} else {
return fmt.Errorf("unknown published repository type")
}

signer, err := getSigner(context.Flags())
Expand Down Expand Up @@ -115,18 +129,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("\nPublish %s has been successfully switched to new source.\n", 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
25 changes: 22 additions & 3 deletions deb/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ func (p *PublishedRepo) MarshalJSON() ([]byte, error) {
}

sources := []sourceInfo{}
for component, item := range p.sourceItems {
for _, component := range p.Components() {
item := p.sourceItems[component]
name := ""
if item.snapshot != nil {
name = item.snapshot.Name
Expand Down Expand Up @@ -452,8 +453,26 @@ func (p *PublishedRepo) UpdateLocalRepo(component string) {
p.rePublishing = true
}

// UpdateSnapshot switches snapshot for component
func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
// SwitchLocalRepo switches local repo for component
func (p *PublishedRepo) SwitchLocalRepo(component string, localRepo *LocalRepo) {
if p.SourceKind != SourceLocalRepo {
panic("not local repo publish")
}

item, exists := p.sourceItems[component]
if !exists {
item = repoSourceItem{}
}
item.localRepo = localRepo
item.packageRefs = localRepo.RefList()
p.sourceItems[component] = item

p.Sources[component] = localRepo.UUID
p.rePublishing = true
}

// SwitchSnapshot switches snapshot for component
func (p *PublishedRepo) SwitchSnapshot(component string, snapshot *Snapshot) {
if p.SourceKind != SourceSnapshot {
panic("not snapshot publish")
}
Expand Down
Loading

0 comments on commit eb6acfd

Please sign in to comment.