[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