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

Reply via email to