Skip to content

Commit

Permalink
Merge pull request #1428 from presztak/image_import_replace
Browse files Browse the repository at this point in the history
Add --reuse to incus image import
  • Loading branch information
stgraber authored Nov 28, 2024
2 parents afa874b + cb8e7da commit bded1e4
Show file tree
Hide file tree
Showing 18 changed files with 917 additions and 862 deletions.
23 changes: 18 additions & 5 deletions cmd/incus/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ type cmdImageImport struct {
image *cmdImage

flagPublic bool
flagReuse bool
flagAliases []string
}

Expand All @@ -669,6 +670,7 @@ func (c *cmdImageImport) Command() *cobra.Command {
Directory import is only available on Linux and must be performed as root.`))

cmd.Flags().BoolVar(&c.flagPublic, "public", false, i18n.G("Make image public"))
cmd.Flags().BoolVar(&c.flagReuse, "reuse", false, i18n.G("If the image alias already exists, delete and create a new one"))
cmd.Flags().StringArrayVar(&c.flagAliases, "alias", nil, i18n.G("New aliases to add to the image")+"``")
cmd.RunE = c.Run

Expand Down Expand Up @@ -880,13 +882,24 @@ func (c *cmdImageImport) Run(cmd *cobra.Command, args []string) error {
fingerprint := opAPI.Metadata["fingerprint"].(string)
progress.Done(fmt.Sprintf(i18n.G("Image imported with fingerprint: %s"), fingerprint))

// Add the aliases
if len(c.flagAliases) > 0 {
aliases := make([]api.ImageAlias, len(c.flagAliases))
for i, entry := range c.flagAliases {
aliases[i].Name = entry
// Reformat aliases
aliases := []api.ImageAlias{}
for _, entry := range c.flagAliases {
alias := api.ImageAlias{}
alias.Name = entry
aliases = append(aliases, alias)
}

// Delete images if necessary
if c.flagReuse {
err = deleteImagesByAliases(d, aliases)
if err != nil {
return err
}
}

// Add the aliases
if len(c.flagAliases) > 0 {
err = ensureImageAliases(d, aliases, fingerprint)
if err != nil {
return err
Expand Down
38 changes: 4 additions & 34 deletions cmd/incus/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,40 +313,10 @@ func (c *cmdPublish) Run(cmd *cobra.Command, args []string) error {
}

// Delete images if necessary
if c.flagReuse && len(existingAliases) > 0 {
visitedImages := make(map[string]interface{})
for _, alias := range existingAliases {
image, _, _ := d.GetImage(alias.Target)

// If the image has already been visited then continue
if image != nil {
_, found := visitedImages[image.Fingerprint]
if found {
continue
}

visitedImages[image.Fingerprint] = nil
}

// An image can have multiple aliases. If an image being published
// reuses all the aliases from an existing image then that existing image is removed.
// In other case only specific aliases should be removed. E.g.
// 1. If image with 'foo' and 'bar' aliases already exists and new image is published
// with aliases 'foo' and 'bar' (and flag '--reuse'). Old image should be removed.
// 2. If image with 'foo' and 'bar' aliases already exists and new image is published
// with alias 'foo' (and flag '--reuse'). Old image should be kept with alias 'bar'
// and new image will have 'foo' alias.
if image != nil && IsAliasesSubset(image.Aliases, aliases) {
op, err := d.DeleteImage(alias.Target)
if err != nil {
return err
}

err = op.Wait()
if err != nil {
return err
}
}
if c.flagReuse {
err = deleteImagesByAliases(d, aliases)
if err != nil {
return err
}
}

Expand Down
54 changes: 54 additions & 0 deletions cmd/incus/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,60 @@ func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) []
return existing
}

// deleteImagesByAliases deletes images based on provided aliases. E.g.
// aliases=[a1], image aliases=[a1] - image will be deleted
// aliases=[a1, a2], image aliases=[a1] - image will be deleted
// aliases=[a1], image aliases=[a1, a2] - image will be preserved.
func deleteImagesByAliases(client incus.InstanceServer, aliases []api.ImageAlias) error {
existingAliases, err := GetCommonAliases(client, aliases...)
if err != nil {
return fmt.Errorf(i18n.G("Error retrieving aliases: %w"), err)
}

// Nothing to do. Just return.
if len(existingAliases) == 0 {
return nil
}

// Delete images if necessary
visitedImages := make(map[string]any)
for _, alias := range existingAliases {
image, _, _ := client.GetImage(alias.Target)

// If the image has already been visited then continue
if image != nil {
_, found := visitedImages[image.Fingerprint]
if found {
continue
}

visitedImages[image.Fingerprint] = nil
}

// An image can have multiple aliases. If an image being published
// reuses all the aliases from an existing image then that existing image is removed.
// In other case only specific aliases should be removed. E.g.
// 1. If image with 'foo' and 'bar' aliases already exists and new image is published
// with aliases 'foo' and 'bar'. Old image should be removed.
// 2. If image with 'foo' and 'bar' aliases already exists and new image is published
// with alias 'foo'. Old image should be kept with alias 'bar'
// and new image will have 'foo' alias.
if image != nil && IsAliasesSubset(image.Aliases, aliases) {
op, err := client.DeleteImage(alias.Target)
if err != nil {
return err
}

err = op.Wait()
if err != nil {
return err
}
}
}

return nil
}

func getConfig(args ...string) (map[string]string, error) {
if len(args) == 2 && !strings.Contains(args[0], "=") {
if args[1] == "-" && !termios.IsTerminal(getStdinFd()) {
Expand Down
Loading

0 comments on commit bded1e4

Please sign in to comment.