[lxc-devel] [distrobuilder/master] generators: Fix cloud-init
monstermunchkin on Github
lxc-bot at linuxcontainers.org
Wed Aug 14 16:14:08 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 518 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190814/46f65a81/attachment.bin>
-------------- next part --------------
From 859a1030e080298608707297e251945240aac69a Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 14 Aug 2019 15:42:00 +0200
Subject: [PATCH] generators: Fix cloud-init
Make sure the cloud-init generator doesn't leave any files behind when
building an lxc image.
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
generators/cloud-init.go | 27 ++++++++---
generators/cloud-init_test.go | 85 ++++++++++++++++++++++++++++++++++-
generators/generators.go | 62 ++++++++++++++++++++-----
3 files changed, 155 insertions(+), 19 deletions(-)
diff --git a/generators/cloud-init.go b/generators/cloud-init.go
index ed89e36..1e16780 100644
--- a/generators/cloud-init.go
+++ b/generators/cloud-init.go
@@ -22,16 +22,16 @@ func (g CloudInitGenerator) RunLXC(cacheDir, sourceDir string, img *image.LXCIma
defFile shared.DefinitionFile) error {
// With OpenRC:
// Remove all symlinks to /etc/init.d/cloud-{init-local,config,init,final} in /etc/runlevels/*
- fullPath := filepath.Join(sourceDir, "/etc/runlevels")
+ fullPath := filepath.Join(sourceDir, "etc", "runlevels")
if lxd.PathExists(fullPath) {
- filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
+ err := filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if lxd.StringInSlice(info.Name(), []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"}) {
- err := os.Remove(path)
+ err := StoreFile(cacheDir, sourceDir, strings.TrimPrefix(path, sourceDir))
if err != nil {
return err
}
@@ -39,6 +39,9 @@ func (g CloudInitGenerator) RunLXC(cacheDir, sourceDir string, img *image.LXCIma
return nil
})
+ if err != nil {
+ return err
+ }
}
// With upstart:
@@ -58,7 +61,7 @@ func (g CloudInitGenerator) RunLXC(cacheDir, sourceDir string, img *image.LXCIma
}
if re.MatchString(info.Name()) {
- err := os.Remove(path)
+ err := StoreFile(cacheDir, sourceDir, strings.TrimPrefix(path, sourceDir))
if err != nil {
return err
}
@@ -69,12 +72,24 @@ func (g CloudInitGenerator) RunLXC(cacheDir, sourceDir string, img *image.LXCIma
}
// With systemd:
- // Create file /etc/cloud/cloud-init.disabled
- err := os.MkdirAll(filepath.Join(sourceDir, "/etc/cloud"), 0755)
+ if !lxd.PathExists(filepath.Join(sourceDir, "/etc/cloud")) {
+ err := StoreFile(cacheDir, sourceDir, "/etc/cloud")
+ if err != nil {
+ return err
+ }
+
+ err = os.MkdirAll(filepath.Join(sourceDir, "/etc/cloud"), 0755)
+ if err != nil {
+ return err
+ }
+ }
+
+ err := StoreFile(cacheDir, sourceDir, "/etc/cloud/cloud-init.disabled")
if err != nil {
return err
}
+ // Create file /etc/cloud/cloud-init.disabled
f, err := os.Create(filepath.Join(sourceDir, "/etc/cloud/cloud-init.disabled"))
if err != nil {
return err
diff --git a/generators/cloud-init_test.go b/generators/cloud-init_test.go
index c6b341f..e07916d 100644
--- a/generators/cloud-init_test.go
+++ b/generators/cloud-init_test.go
@@ -7,13 +7,94 @@ import (
"path/filepath"
"testing"
+ lxd "github.com/lxc/lxd/shared"
+ "github.com/stretchr/testify/require"
+
"github.com/lxc/distrobuilder/image"
"github.com/lxc/distrobuilder/shared"
- "github.com/stretchr/testify/require"
)
-func TestCloudInitGeneratorRunLXD(t *testing.T) {
+func TestCloudInitGeneratorRunLXC(t *testing.T) {
+ cacheDir := filepath.Join(os.TempDir(), "distrobuilder-test")
+ rootfsDir := filepath.Join(cacheDir, "rootfs")
+
+ setup(t, cacheDir)
+ defer teardown(cacheDir)
+
+ generator := Get("cloud-init")
+ require.Equal(t, CloudInitGenerator{}, generator)
+
+ // Prepare rootfs
+ err := os.MkdirAll(filepath.Join(rootfsDir, "etc", "runlevels"), 0755)
+ require.NoError(t, err)
+
+ err = os.MkdirAll(filepath.Join(rootfsDir, "etc", "cloud"), 0755)
+ require.NoError(t, err)
+
+ for _, f := range []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"} {
+ fullPath := filepath.Join(rootfsDir, "etc", "runlevels", f)
+ err = os.Symlink("/dev/null", fullPath)
+ require.NoError(t, err)
+ require.FileExists(t, fullPath)
+ }
+
+ for i := 0; i <= 6; i++ {
+ dir := filepath.Join(rootfsDir, "etc", "rc.d", fmt.Sprintf("rc%d.d", i))
+
+ err = os.MkdirAll(dir, 0755)
+ require.NoError(t, err)
+
+ for _, f := range []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"} {
+ fullPath := filepath.Join(dir, fmt.Sprintf("S99%s", f))
+ err = os.Symlink("/dev/null", fullPath)
+ require.NoError(t, err)
+ require.FileExists(t, fullPath)
+ }
+ }
+
+ // Disable cloud-init
+ generator.RunLXC(cacheDir, rootfsDir, nil, shared.DefinitionFile{})
+
+ // Check whether the generator has altered the rootfs
+ for _, f := range []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"} {
+ fullPath := filepath.Join(rootfsDir, "etc", "runlevels", f)
+ require.Falsef(t, lxd.PathExists(fullPath), "File '%s' exists but shouldn't", fullPath)
+ }
+
+ for i := 0; i <= 6; i++ {
+ dir := filepath.Join(rootfsDir, "etc", "rc.d", fmt.Sprintf("rc%d.d", i))
+
+ for _, f := range []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"} {
+ fullPath := filepath.Join(dir, fmt.Sprintf("S99%s", f))
+ require.Falsef(t, lxd.PathExists(fullPath), "File '%s' exists but shouldn't", fullPath)
+ }
+ }
+
+ require.FileExists(t, filepath.Join(rootfsDir, "etc", "cloud", "cloud-init.disabled"))
+ err = RestoreFiles(cacheDir, rootfsDir)
+ require.NoError(t, err)
+
+ // Check whether the files have been restored
+ for _, f := range []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"} {
+ fullPath := filepath.Join(rootfsDir, "etc", "runlevels", f)
+ require.FileExists(t, fullPath)
+ }
+
+ for i := 0; i <= 6; i++ {
+ dir := filepath.Join(rootfsDir, "etc", "rc.d", fmt.Sprintf("rc%d.d", i))
+
+ for _, f := range []string{"cloud-init-local", "cloud-config", "cloud-init", "cloud-final"} {
+ fullPath := filepath.Join(dir, fmt.Sprintf("S99%s", f))
+ require.FileExists(t, fullPath)
+ }
+ }
+
+ fullPath := filepath.Join(rootfsDir, "etc", "cloud", "cloud-init.disabled")
+ require.Falsef(t, lxd.PathExists(fullPath), "File '%s' exists but shouldn't", fullPath)
+}
+
+func TestCloudInitGeneratorRunLXD(t *testing.T) {
cacheDir := filepath.Join(os.TempDir(), "distrobuilder-test")
rootfsDir := filepath.Join(cacheDir, "rootfs")
diff --git a/generators/generators.go b/generators/generators.go
index f4f1ef6..3842852 100644
--- a/generators/generators.go
+++ b/generators/generators.go
@@ -4,6 +4,7 @@ import (
"os"
p "path"
"path/filepath"
+ "strings"
lxd "github.com/lxc/lxd/shared"
@@ -40,13 +41,21 @@ func Get(generator string) Generator {
return nil
}
-var storedFiles = map[string]string{}
+var storedFiles = map[string]os.FileInfo{}
// StoreFile caches a file which can be restored with the RestoreFiles function.
func StoreFile(cacheDir, sourceDir, path string) error {
+ fullPath := filepath.Join(sourceDir, path)
+
+ _, ok := storedFiles[fullPath]
+ if ok {
+ // This file or directory has already been recorded
+ return nil
+ }
+
// Record newly created files
- if !lxd.PathExists(filepath.Join(sourceDir, path)) {
- storedFiles[filepath.Join(sourceDir, path)] = ""
+ if !lxd.PathExists(fullPath) {
+ storedFiles[fullPath] = nil
return nil
}
@@ -56,18 +65,39 @@ func StoreFile(cacheDir, sourceDir, path string) error {
return err
}
- storedFiles[filepath.Join(sourceDir, path)] = filepath.Join(cacheDir, "tmp", path)
+ info, err := os.Lstat(fullPath)
+ if err != nil {
+ return err
+ }
+
+ storedFiles[fullPath] = info
+
+ err = os.Rename(fullPath, filepath.Join(cacheDir, "tmp", path))
+ if err == nil {
+ return nil
+ }
- return lxd.FileCopy(filepath.Join(sourceDir, path),
- filepath.Join(cacheDir, "tmp", path))
+ // Try copying the file since renaming it failed
+ if info.IsDir() {
+ err = lxd.DirCopy(fullPath, filepath.Join(cacheDir, "tmp", path))
+ } else {
+ err = lxd.FileCopy(fullPath, filepath.Join(cacheDir, "tmp", path))
+ }
+ if err != nil {
+ return err
+ }
+
+ return os.RemoveAll(fullPath)
}
// RestoreFiles restores original files which were cached by StoreFile.
func RestoreFiles(cacheDir, sourceDir string) error {
- for origPath, tmpPath := range storedFiles {
+ var err error
+
+ for origPath, fi := range storedFiles {
// Deal with newly created files
- if tmpPath == "" {
- err := os.Remove(origPath)
+ if fi == nil {
+ err := os.RemoveAll(origPath)
if err != nil {
return err
}
@@ -75,14 +105,24 @@ func RestoreFiles(cacheDir, sourceDir string) error {
continue
}
- err := lxd.FileCopy(tmpPath, origPath)
+ err = os.Rename(filepath.Join(cacheDir, "tmp", strings.TrimPrefix(origPath, sourceDir)), origPath)
+ if err == nil {
+ continue
+ }
+
+ // Try copying the file or directory since renaming it failed
+ if fi.IsDir() {
+ err = lxd.DirCopy(filepath.Join(cacheDir, "tmp", strings.TrimPrefix(origPath, sourceDir)), origPath)
+ } else {
+ err = lxd.FileCopy(filepath.Join(cacheDir, "tmp", strings.TrimPrefix(origPath, sourceDir)), origPath)
+ }
if err != nil {
return err
}
}
// Reset the list of stored files
- storedFiles = map[string]string{}
+ storedFiles = map[string]os.FileInfo{}
return nil
}
More information about the lxc-devel
mailing list