The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3563
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === This makes the CLI explicitly remove existing aliases that conflict with the ones of the image being copied/imported, and recreate them. Closes #3560
From e8e0b4be39f609231e5f5f3894f688f203605c09 Mon Sep 17 00:00:00 2001 From: Alberto Donato <[email protected]> Date: Mon, 17 Jul 2017 15:17:24 +0200 Subject: [PATCH] Update image aliases when they already exist Signed-off-by: Alberto Donato <[email protected]> --- lxc/image.go | 75 ++++++++++++++++++++++++++++++++++++------------ lxc/publish.go | 8 ++++-- shared/api/image.go | 14 +++++++++ shared/api/image_test.go | 40 ++++++++++++++++++++++++++ test/suites/basic.sh | 7 +++++ test/suites/image.sh | 12 ++++++++ 6 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 shared/api/image_test.go diff --git a/lxc/image.go b/lxc/image.go index 6c0558504..7de983dfb 100644 --- a/lxc/image.go +++ b/lxc/image.go @@ -398,18 +398,8 @@ func (c *imageCmd) run(conf *config.Config, args []string) error { return err } - // Setup the copy arguments - aliases := []api.ImageAlias{} - for _, entry := range c.addAliases { - alias := api.ImageAlias{} - alias.Name = entry - aliases = append(aliases, alias) - } - args := lxd.ImageCopyArgs{ - Aliases: aliases, AutoUpdate: c.autoUpdate, - CopyAliases: c.copyAliases, Public: c.publicImage, } @@ -435,7 +425,20 @@ func (c *imageCmd) run(conf *config.Config, args []string) error { } progress.Done(i18n.G("Image copied successfully!")) - return nil + + // Ensure aliases + aliases := make([]api.ImageAlias, len(c.addAliases)) + for i, entry := range c.addAliases { + aliases[i].Name = entry + } + if c.copyAliases { + // Also add the original aliases + for _, alias := range image.Aliases { + aliases = append(aliases, alias) + } + } + err = ensureImageAliases(dest, aliases, image.Fingerprint) + return err case "delete": /* delete [<remote>:]<image> [<remote>:][<image>...] */ @@ -722,17 +725,16 @@ func (c *imageCmd) run(conf *config.Config, args []string) error { progress.Done(fmt.Sprintf(i18n.G("Image imported with fingerprint: %s"), fingerprint)) // Add the aliases - for _, entry := range c.addAliases { - alias := api.ImageAliasesPost{} - alias.Name = entry - alias.Target = fingerprint - - err = d.CreateImageAlias(alias) + if len(c.addAliases) > 0 { + aliases := make([]api.ImageAlias, len(c.addAliases)) + for i, entry := range c.addAliases { + aliases[i].Name = entry + } + err = ensureImageAliases(d, aliases, fingerprint) if err != nil { return err } } - return nil case "list": @@ -1236,3 +1238,40 @@ func packImageDir(path string) (string, error) { shared.RunCommand("tar", "-C", path, "--numeric-owner", "-cJf", outFileName, "rootfs", "templates", "metadata.yaml") return outFileName, nil } + +// Create the specified image alises, updating those that already exist +func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error { + if len(aliases) == 0 { + return nil + } + + names := make([]string, len(aliases)) + for i, alias := range aliases { + names[i] = alias.Name + } + sort.Strings(names) + + resp, err := client.GetImageAliases() + if err != nil { + return err + } + + // Delete existing aliases that match provided ones + for _, alias := range api.GetExistingAliases(names, resp) { + err := client.DeleteImageAlias(alias.Name) + if err != nil { + fmt.Println(i18n.G("Failed to remove alias %s"), alias.Name) + } + } + // Create new aliases + for _, alias := range aliases { + aliasPost := api.ImageAliasesPost{} + aliasPost.Name = alias.Name + aliasPost.Target = fingerprint + err := client.CreateImageAlias(aliasPost) + if err != nil { + fmt.Println(i18n.G("Failed to create alias %s"), alias.Name) + } + } + return nil +} diff --git a/lxc/publish.go b/lxc/publish.go index 2d4a9e121..24fa8ab28 100644 --- a/lxc/publish.go +++ b/lxc/publish.go @@ -200,7 +200,6 @@ func (c *publishCmd) run(conf *config.Config, args []string) error { } if cRemote == iRemote { - req.Aliases = aliases req.Public = c.makePublic } @@ -229,8 +228,7 @@ func (c *publishCmd) run(conf *config.Config, args []string) error { // Image copy arguments args := lxd.ImageCopyArgs{ - Aliases: aliases, - Public: c.makePublic, + Public: c.makePublic, } // Copy the image to the destination host @@ -245,6 +243,10 @@ func (c *publishCmd) run(conf *config.Config, args []string) error { } } + err = ensureImageAliases(d, aliases, fingerprint) + if err != nil { + return err + } fmt.Printf(i18n.G("Container published with fingerprint: %s")+"\n", fingerprint) return nil diff --git a/shared/api/image.go b/shared/api/image.go index 814c0981d..aeeecc17b 100644 --- a/shared/api/image.go +++ b/shared/api/image.go @@ -1,6 +1,7 @@ package api import ( + "sort" "time" ) @@ -102,3 +103,16 @@ type ImageAliasesEntry struct { Name string `json:"name" yaml:"name"` } + +// GetExistingAliases returns the intersection between a list of aliases and all the existing ones. +func GetExistingAliases(aliases []string, allAliases []ImageAliasesEntry) []ImageAliasesEntry { + existing := []ImageAliasesEntry{} + for _, alias := range allAliases { + name := alias.Name + pos := sort.SearchStrings(aliases, name) + if pos < len(aliases) && aliases[pos] == name { + existing = append(existing, alias) + } + } + return existing +} diff --git a/shared/api/image_test.go b/shared/api/image_test.go new file mode 100644 index 000000000..9bc8a59f7 --- /dev/null +++ b/shared/api/image_test.go @@ -0,0 +1,40 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type imageTestSuite struct { + suite.Suite + images *[]ImageAliasesEntry +} + +func TestImageTestSuite(t *testing.T) { + suite.Run(t, new(imageTestSuite)) +} + +func (s *imageTestSuite) SetupTest() { + s.images = &[]ImageAliasesEntry{ + ImageAliasesEntry{ + Name: "foo", + }, + ImageAliasesEntry{ + Name: "bar", + }, + ImageAliasesEntry{ + Name: "baz", + }, + } +} + +func (s *imageTestSuite) TestGetExistingAliases() { + aliases := GetExistingAliases([]string{"bar", "foo", "other"}, *s.images) + s.Exactly([]ImageAliasesEntry{(*s.images)[0], (*s.images)[1]}, aliases) +} + +func (s *imageTestSuite) TestGetExistingAliasesEmpty() { + aliases := GetExistingAliases([]string{"other1", "other2"}, *s.images) + s.Exactly([]ImageAliasesEntry{}, aliases) +} diff --git a/test/suites/basic.sh b/test/suites/basic.sh index e3ad787a5..4993ffa72 100644 --- a/test/suites/basic.sh +++ b/test/suites/basic.sh @@ -115,6 +115,13 @@ test_basic_usage() { curl -k -s --cert "${LXD_CONF}/client3.crt" --key "${LXD_CONF}/client3.key" -X GET "https://${LXD_ADDR}/1.0/images" | grep "/1.0/images/" && false lxc image delete foo-image + # Test container publish with existing alias + lxc init testimage baz + lxc publish bar --alias=foo-image --alias=foo-image2 + # publishing another image with same alias doesn't fail + lxc publish baz --alias=foo-image + lxc image delete foo-image foo-image2 + # Test image compression on publish lxc publish bar --alias=foo-image-compressed --compression=bzip2 prop=val1 lxc image show foo-image-compressed | grep val1 diff --git a/test/suites/image.sh b/test/suites/image.sh index 89007d4f3..a28913abc 100644 --- a/test/suites/image.sh +++ b/test/suites/image.sh @@ -69,3 +69,15 @@ test_image_import_dir() { tar tvf "$exported" | fgrep -q metadata.yaml rm "$exported" } + +test_image_import_existing_alias() { + ensure_import_testimage + lxc init testimage c + lxc publish c --alias newimage --alias image2 + lxc delete c + lxc image export testimage testimage.file + lxc image delete testimage + # the image can be imported with an existing alias + lxc image import testimage.file --alias newimage + lxc image delete newimage image2 +}
_______________________________________________ lxc-devel mailing list [email protected] http://lists.linuxcontainers.org/listinfo/lxc-devel
