[lxc-devel] [lxd/master] Update image aliases when they already exist

albertodonato on Github lxc-bot at linuxcontainers.org
Tue Jul 18 08:28:29 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 453 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170718/dca04465/attachment.bin>
-------------- next part --------------
From e8e0b4be39f609231e5f5f3894f688f203605c09 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Mon, 17 Jul 2017 15:17:24 +0200
Subject: [PATCH] Update image aliases when they already exist

Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
 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
+}


More information about the lxc-devel mailing list