[lxc-devel] [distrobuilder/master] Add support form compat level overrides

monstermunchkin on Github lxc-bot at linuxcontainers.org
Wed Mar 7 14:53:44 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 309 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180307/96ad67a3/attachment.bin>
-------------- next part --------------
From f412fc349d543b65122575fc8861b68891f05adc Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 7 Mar 2018 13:38:55 +0100
Subject: [PATCH 1/2] *: Add support for compat level overrides

Resolves #30

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 image/lxc.go         | 99 +++++++++++++++++++++++++++++++++++++++++++---------
 shared/definition.go | 13 +++++--
 2 files changed, 93 insertions(+), 19 deletions(-)

diff --git a/image/lxc.go b/image/lxc.go
index d8467f1..3c5a9fb 100644
--- a/image/lxc.go
+++ b/image/lxc.go
@@ -13,6 +13,8 @@ import (
 	"github.com/lxc/distrobuilder/shared"
 )
 
+const maxLXCCompatLevel = 5
+
 // LXCImage represents a LXC image.
 type LXCImage struct {
 	sourceDir  string
@@ -85,23 +87,58 @@ func (l *LXCImage) Build() error {
 func (l *LXCImage) createMetadata() error {
 	metaDir := filepath.Join(l.cacheDir, "metadata")
 
-	err := l.writeMetadata(filepath.Join(metaDir, "config"), l.target.Config)
-	if err != nil {
-		return fmt.Errorf("Error writing 'config': %s", err)
-	}
+	for _, c := range l.target.Config {
+		// If not specified, create files up to ${maxLXCCompatLevel}
+		if c.Before == 0 {
+			c.Before = maxLXCCompatLevel + 1
+		}
+		for i := uint(1); i < maxLXCCompatLevel+1; i++ {
+			// Bound checking
+			if c.After < c.Before {
+				if i <= c.After || i >= c.Before {
+					continue
+				}
 
-	err = l.writeMetadata(filepath.Join(metaDir, "config-user"), l.target.ConfigUser)
-	if err != nil {
-		return fmt.Errorf("Error writing 'config-user': %s", err)
+			} else if c.After >= c.Before {
+				if i <= c.After && i >= c.Before {
+					continue
+				}
+			}
+
+			switch c.Type {
+			case "all":
+				err := l.writeConfig(i, filepath.Join(metaDir, "config"), c.Content)
+				if err != nil {
+					return err
+				}
+
+				err = l.writeConfig(i, filepath.Join(metaDir, "config.user"), c.Content)
+				if err != nil {
+					return err
+				}
+			case "system":
+				err := l.writeConfig(i, filepath.Join(metaDir, "config"), c.Content)
+				if err != nil {
+					return err
+				}
+			case "user":
+				err := l.writeConfig(i, filepath.Join(metaDir, "config.user"), c.Content)
+				if err != nil {
+					return err
+				}
+			}
+		}
 	}
 
-	err = l.writeMetadata(filepath.Join(metaDir, "create-message"), l.target.CreateMessage)
+	err := l.writeMetadata(filepath.Join(metaDir, "create-message"),
+		l.target.CreateMessage, false)
 	if err != nil {
 		return fmt.Errorf("Error writing 'create-message': %s", err)
 	}
 
 	err = l.writeMetadata(filepath.Join(metaDir, "expiry"),
-		fmt.Sprint(shared.GetExpiryDate(time.Now(), l.definition.Expiry).Unix()))
+		fmt.Sprint(shared.GetExpiryDate(time.Now(), l.definition.Expiry).Unix()),
+		false)
 	if err != nil {
 		return fmt.Errorf("Error writing 'expiry': %s", err)
 	}
@@ -127,7 +164,8 @@ func (l *LXCImage) createMetadata() error {
 		}
 	}
 
-	err = l.writeMetadata(filepath.Join(metaDir, "excludes-user"), excludesUser)
+	err = l.writeMetadata(filepath.Join(metaDir, "excludes-user"), excludesUser,
+		false)
 	if err != nil {
 		return fmt.Errorf("Error writing 'excludes-user': %s", err)
 	}
@@ -136,14 +174,23 @@ func (l *LXCImage) createMetadata() error {
 }
 
 func (l *LXCImage) packMetadata() error {
-	files := []string{"config", "config-user", "create-message", "expiry",
-		"excludes-user"}
+	files := []string{"create-message", "expiry", "excludes-user"}
+
+	// Get all config and config.user files
+	configs, err := filepath.Glob(filepath.Join(l.cacheDir, "metadata", "config*"))
+	if err != nil {
+		return err
+	}
+
+	for _, c := range configs {
+		files = append(files, filepath.Base(c))
+	}
 
 	if lxd.PathExists(filepath.Join(l.cacheDir, "metadata", "templates")) {
 		files = append(files, "templates")
 	}
 
-	err := shared.Pack(filepath.Join(l.targetDir, "meta.tar"), "xz",
+	err = shared.Pack(filepath.Join(l.targetDir, "meta.tar"), "xz",
 		filepath.Join(l.cacheDir, "metadata"), files...)
 	if err != nil {
 		return fmt.Errorf("Failed to create metadata: %s", err)
@@ -151,8 +198,15 @@ func (l *LXCImage) packMetadata() error {
 
 	return nil
 }
-func (l *LXCImage) writeMetadata(filename, content string) error {
-	file, err := os.Create(filename)
+func (l *LXCImage) writeMetadata(filename, content string, append bool) error {
+	var file *os.File
+	var err error
+
+	if append {
+		file, err = os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+	} else {
+		file, err = os.Create(filename)
+	}
 	if err != nil {
 		return err
 	}
@@ -167,10 +221,23 @@ func (l *LXCImage) writeMetadata(filename, content string) error {
 		return err
 	}
 
-	_, err = file.WriteString(out)
+	_, err = file.WriteString(out + "\n")
 	if err != nil {
 		return err
 	}
 
 	return nil
 }
+
+func (l *LXCImage) writeConfig(compatLevel uint, filename, content string) error {
+	// Only add suffix if it's not the latest compatLevel
+	if compatLevel != maxLXCCompatLevel {
+		filename = fmt.Sprintf("%s.%d", filename, compatLevel)
+	}
+	err := l.writeMetadata(filename, content, true)
+	if err != nil {
+		return fmt.Errorf("Error writing '%s': %s", filepath.Base(filename), err)
+	}
+
+	return nil
+}
diff --git a/shared/definition.go b/shared/definition.go
index 63cba8c..93950bc 100644
--- a/shared/definition.go
+++ b/shared/definition.go
@@ -40,11 +40,18 @@ type DefinitionSource struct {
 	AptSources string   `yaml:"apt_sources,omitempty"`
 }
 
+// A DefinitionTargetLXCConfig represents the config part of the metadata.
+type DefinitionTargetLXCConfig struct {
+	Type    string `yaml:"type"`
+	Before  uint   `yaml:"before,omitempty"`
+	After   uint   `yaml:"after,omitempty"`
+	Content string `yaml:"content"`
+}
+
 // A DefinitionTargetLXC represents LXC specific files as part of the metadata.
 type DefinitionTargetLXC struct {
-	CreateMessage string `yaml:"create-message,omitempty"`
-	Config        string `yaml:"config,omitempty"`
-	ConfigUser    string `yaml:"config-user,omitempty"`
+	CreateMessage string                      `yaml:"create-message,omitempty"`
+	Config        []DefinitionTargetLXCConfig `yaml:"config,omitempty"`
 }
 
 // A DefinitionTarget specifies target dependent files.

From 0c7e50de7c5c114cdb91d165a5ae47365de3a33c Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 7 Mar 2018 15:50:48 +0100
Subject: [PATCH 2/2] tests: Update lxc config tests

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 image/lxc_test.go | 152 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 134 insertions(+), 18 deletions(-)

diff --git a/image/lxc_test.go b/image/lxc_test.go
index 9271176..93de24f 100644
--- a/image/lxc_test.go
+++ b/image/lxc_test.go
@@ -25,11 +25,55 @@ var lxcImageDef = shared.DefinitionImage{
 
 var lxcTarget = shared.DefinitionTargetLXC{
 	CreateMessage: "Welcome to {{ image.Distribution|capfirst}} {{ image.Release }}",
-	Config: `lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.common.conf
-lxc.arch = x86_64`,
-	ConfigUser: `lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.common.conf
-lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.userns.conf
-lxc.arch = x86_64`,
+	Config: []shared.DefinitionTargetLXCConfig{
+		{
+			Type:    "all",
+			Before:  5,
+			Content: "all_before_5",
+		},
+		{
+			Type:    "user",
+			Before:  5,
+			Content: "user_before_5",
+		},
+		{
+			Type:    "all",
+			After:   4,
+			Content: "all_after_4",
+		},
+		{
+			Type:    "user",
+			After:   4,
+			Content: "user_after_4",
+		},
+		{
+			Type:    "all",
+			Content: "all",
+		},
+		{
+			Type:    "system",
+			Before:  2,
+			Content: "system_before_2",
+		},
+		{
+			Type:    "system",
+			Before:  2,
+			After:   4,
+			Content: "system_before_2_after_4",
+		},
+		{
+			Type:    "user",
+			Before:  3,
+			After:   3,
+			Content: "user_before_3_after_3",
+		},
+		{
+			Type:    "system",
+			Before:  4,
+			After:   2,
+			Content: "system_before_4_after_2",
+		},
+	},
 }
 
 func lxcCacheDir() string {
@@ -127,7 +171,7 @@ func TestLXCBuild(t *testing.T) {
 	}()
 }
 
-func TestLXCCreateMetadata(t *testing.T) {
+func TestLXCCreateMetadataBasic(t *testing.T) {
 	defaultImage := setupLXC()
 	defer teardownLXC()
 
@@ -148,16 +192,13 @@ func TestLXCCreateMetadata(t *testing.T) {
 			true,
 			"Error writing 'config': .+",
 			func(l LXCImage) *LXCImage {
-				l.target.Config = "{{ invalid }"
-				return &l
-			},
-		},
-		{
-			"invalid config-user template",
-			true,
-			"Error writing 'config-user': .+",
-			func(l LXCImage) *LXCImage {
-				l.target.ConfigUser = "{{ invalid }"
+				l.target.Config = []shared.DefinitionTargetLXCConfig{
+					{
+						Type:    "all",
+						After:   4,
+						Content: "{{ invalid }",
+					},
+				}
 				return &l
 			},
 		},
@@ -205,6 +246,81 @@ func TestLXCCreateMetadata(t *testing.T) {
 	}
 }
 
+func TestLXCCreateMetadataConfig(t *testing.T) {
+	image := setupLXC()
+	defer teardownLXC()
+
+	tests := []struct {
+		configFile string
+		expected   string
+	}{
+		{
+			"config",
+			"all_after_4\nall\nsystem_before_2_after_4\n",
+		},
+		{
+			"config.1",
+			"all_before_5\nall\nsystem_before_2\nsystem_before_2_after_4\n",
+		},
+		{
+			"config.2",
+			"all_before_5\nall\n",
+		},
+		{
+			"config.3",
+			"all_before_5\nall\nsystem_before_4_after_2\n",
+		},
+		{
+			"config.4",
+			"all_before_5\nall\n",
+		},
+		{
+			"config.user",
+			"all_after_4\nuser_after_4\nall\nuser_before_3_after_3\n",
+		},
+		{
+			"config.user.1",
+			"all_before_5\nuser_before_5\nall\nuser_before_3_after_3\n",
+		},
+		{
+			"config.user.2",
+			"all_before_5\nuser_before_5\nall\nuser_before_3_after_3\n",
+		},
+		{
+			"config.user.3",
+			"all_before_5\nuser_before_5\nall\n",
+		},
+		{
+			"config.user.4",
+			"all_before_5\nuser_before_5\nall\nuser_before_3_after_3\n",
+		},
+	}
+
+	err := image.createMetadata()
+	if err != nil {
+		t.Fatalf("Unexpected error: %s", err)
+	}
+
+	for _, tt := range tests {
+		log.Printf("Checking '%s'", tt.configFile)
+		file, err := os.Open(filepath.Join(lxcCacheDir(), "metadata", tt.configFile))
+		if err != nil {
+			t.Fatalf("Unexpected error: %s", err)
+		}
+
+		var buffer bytes.Buffer
+		_, err = io.Copy(&buffer, file)
+		file.Close()
+		if err != nil {
+			t.Fatalf("Unexpected error: %s", err)
+		}
+
+		if buffer.String() != tt.expected {
+			t.Fatalf("Expected '%s', got '%s'", tt.expected, buffer.String())
+		}
+	}
+}
+
 func TestLXCPackMetadata(t *testing.T) {
 	image := setupLXC()
 	defer func() {
@@ -243,13 +359,13 @@ func TestLXCWriteMetadata(t *testing.T) {
 	defer teardownLXC()
 
 	// Should fail due to invalid path
-	err := image.writeMetadata("/path/file", "")
+	err := image.writeMetadata("/path/file", "", false)
 	if err == nil {
 		t.Fatal("Expected failure")
 	}
 
 	// Should succeed
-	err = image.writeMetadata("test", "metadata")
+	err = image.writeMetadata("test", "metadata", false)
 	if err != nil {
 		t.Fatalf("Unexpected failure: %s", err)
 	}


More information about the lxc-devel mailing list