The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/distrobuilder/pull/215
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) === Make sure the cloud-init generator doesn't leave any files behind when building an lxc image. This depends on https://github.com/lxc/lxd/pull/6084 Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
From 859a1030e080298608707297e251945240aac69a Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@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.h...@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 }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel