Skip to content

Commit

Permalink
fix: make reset work with bind mounts (#1025)
Browse files Browse the repository at this point in the history
  • Loading branch information
sgalsaleh authored Aug 22, 2024
1 parent 4a23eaf commit cfa55c1
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 46 deletions.
60 changes: 21 additions & 39 deletions cmd/embedded-cluster/uninstall.go → cmd/embedded-cluster/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,70 +453,52 @@ var resetCommand = &cli.Command{
logrus.Infof("Node has been reset. Please reboot to ensure transient configuration is also reset.")
}

if _, err := os.Stat(defaults.PathToK0sConfig()); err == nil {
if err := os.Remove(defaults.PathToK0sConfig()); err != nil {
return err
}
if err := helpers.RemoveAll(defaults.PathToK0sConfig()); err != nil {
return fmt.Errorf("failed to remove k0s config: %w", err)
}

lamPath := "/etc/systemd/system/local-artifact-mirror.service"
if _, err := os.Stat(lamPath); err == nil {
if _, err := helpers.RunCommand("systemctl", "stop", "local-artifact-mirror"); err != nil {
return err
}
if err := os.RemoveAll(lamPath); err != nil {
return err
}
}
if err := helpers.RemoveAll(lamPath); err != nil {
return fmt.Errorf("failed to remove local-artifact-mirror path: %w", err)
}

proxyControllerPath := "/etc/systemd/system/k0scontroller.service.d"
if _, err := os.Stat(proxyControllerPath); err == nil {
if err := os.RemoveAll(proxyControllerPath); err != nil {
return err
}
if err := helpers.RemoveAll(proxyControllerPath); err != nil {
return fmt.Errorf("failed to remove proxy controller path: %w", err)
}

proxyWorkerPath := "/etc/systemd/system/k0sworker.service.d"
if _, err := os.Stat(proxyWorkerPath); err == nil {
if err := os.RemoveAll(proxyWorkerPath); err != nil {
return err
}
if err := helpers.RemoveAll(proxyWorkerPath); err != nil {
return fmt.Errorf("failed to remove proxy worker path: %w", err)
}

if _, err := os.Stat(defaults.EmbeddedClusterHomeDirectory()); err == nil {
if err := os.RemoveAll(defaults.EmbeddedClusterHomeDirectory()); err != nil {
return fmt.Errorf("failed to remove embedded cluster home directory: %w", err)
}
if err := helpers.RemoveAll(defaults.EmbeddedClusterHomeDirectory()); err != nil {
return fmt.Errorf("failed to remove embedded cluster directory: %w", err)
}

if _, err := os.Stat(defaults.PathToK0sContainerdConfig()); err == nil {
if err := os.RemoveAll(defaults.PathToK0sContainerdConfig()); err != nil {
return fmt.Errorf("failed to remove containerd config: %w", err)
}
if err := helpers.RemoveAll(defaults.PathToK0sContainerdConfig()); err != nil {
return fmt.Errorf("failed to remove containerd config: %w", err)
}

if _, err := os.Lstat(systemdUnitFileName()); err == nil {
if err := os.Remove(systemdUnitFileName()); err != nil {
return fmt.Errorf("failed to remove systemd unit file: %w", err)
}
if err := helpers.RemoveAll(systemdUnitFileName()); err != nil {
return fmt.Errorf("failed to remove systemd unit file: %w", err)
}

if _, err := os.Stat("/var/openebs"); err == nil {
if err := os.RemoveAll("/var/openebs"); err != nil {
return fmt.Errorf("failed to remove openebs storage: %w", err)
}
if err := helpers.RemoveAll("/var/openebs"); err != nil {
return fmt.Errorf("failed to remove openebs storage: %w", err)
}

if _, err := os.Stat("/etc/NetworkManager/conf.d/embedded-cluster.conf"); err == nil {
if err := os.RemoveAll("/etc/NetworkManager/conf.d/embedded-cluster.conf"); err != nil {
return fmt.Errorf("failed to remove NetworkManager configuration: %w", err)
}
if err := helpers.RemoveAll("/etc/NetworkManager/conf.d/embedded-cluster.conf"); err != nil {
return fmt.Errorf("failed to remove NetworkManager configuration: %w", err)
}

if _, err := os.Stat("/usr/local/bin/k0s"); err == nil {
if err := os.RemoveAll("/usr/local/bin/k0s"); err != nil {
return fmt.Errorf("failed to remove k0s binary: %w", err)
}
if err := helpers.RemoveAll("/usr/local/bin/k0s"); err != nil {
return fmt.Errorf("failed to remove k0s binary: %w", err)
}

if c.Bool("reboot") {
Expand Down
45 changes: 38 additions & 7 deletions pkg/helpers/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,75 @@ import (
"fmt"
"io"
"os"
"path/filepath"
)

// MoveFile moves a file from one location to another, overwriting the destination if it
// exists. File mode is preserved.
func MoveFile(src, dst string) error {
srcinfo, err := os.Stat(src)
if err != nil {
return fmt.Errorf("unable to stat %s: %s", src, err)
return fmt.Errorf("stat %s: %s", src, err)
}

if srcinfo.IsDir() {
return fmt.Errorf("unable to move directory %s", src)
return fmt.Errorf("move directory %s", src)
}

srcfp, err := os.Open(src)
if err != nil {
return fmt.Errorf("unable to open source file: %s", err)
return fmt.Errorf("open source file: %s", err)
}
defer srcfp.Close()

opts := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
dstfp, err := os.OpenFile(dst, opts, srcinfo.Mode())
if err != nil {
return fmt.Errorf("unable to open destination file: %s", err)
return fmt.Errorf("open destination file: %s", err)
}
defer dstfp.Close()

if _, err := io.Copy(dstfp, srcfp); err != nil {
return fmt.Errorf("unable to copy file: %s", err)
return fmt.Errorf("copy file: %s", err)
}

if err := dstfp.Sync(); err != nil {
return fmt.Errorf("unable to sync file: %s", err)
return fmt.Errorf("sync file: %s", err)
}

if err := os.Remove(src); err != nil {
return fmt.Errorf("unable to remove source file: %s", err)
return fmt.Errorf("remove source file: %s", err)
}

return nil
}

// RemoveAll removes path if it's a file. If path is a directory, it only removes its contents.
// This is to handle the case where path is a bind mounted directory.
func RemoveAll(path string) error {
info, err := os.Lstat(path)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("stat file: %w", err)
}
if os.IsNotExist(err) {
return nil
}
if !info.IsDir() {
return os.Remove(path)
}
d, err := os.Open(path)
if err != nil {
return fmt.Errorf("open directory: %w", err)
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return fmt.Errorf("read directory: %w", err)
}
for _, name := range names {
if err := os.RemoveAll(filepath.Join(path, name)); err != nil {
return fmt.Errorf("remove %s: %w", name, err)
}
}
return nil
}
108 changes: 108 additions & 0 deletions pkg/helpers/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package helpers
import (
"fmt"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestMoveFile(t *testing.T) {
Expand Down Expand Up @@ -85,3 +87,109 @@ func TestMoveFile_Symlink(t *testing.T) {
assert.Error(t, err)
assert.Empty(t, target)
}

func TestRemoveAll(t *testing.T) {
tests := []struct {
name string
setup func(t *testing.T) (string, bool)
isDir bool
}{
{
name: "remove file",
setup: func(t *testing.T) (string, bool) {
f, err := os.CreateTemp("", "test-file")
if err != nil {
t.Fatal(err)
}
return f.Name(), true
},
},
{
name: "remove directory",
setup: func(t *testing.T) (string, bool) {
dir, err := os.MkdirTemp("", "test-dir")
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "file1"), []byte("test"), 0644); err != nil {
t.Fatal(err)
}
if err := os.Mkdir(filepath.Join(dir, "subdir"), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "subdir", "file2"), []byte("test"), 0644); err != nil {
t.Fatal(err)
}
return dir, true
},
isDir: true,
},
{
name: "remove symlink",
setup: func(t *testing.T) (string, bool) {
f, err := os.CreateTemp("", "test-file")
if err != nil {
t.Fatal(err)
}
slink := filepath.Join(os.TempDir(), "test-symlink")
if err := os.Symlink(f.Name(), slink); err != nil {
t.Fatal(err)
}
return slink, true
},
},
{
name: "remove non-existent path",
setup: func(t *testing.T) (string, bool) {
return filepath.Join(os.TempDir(), "non-existent-path"), false
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)

path, shouldExist := tt.setup(t)
_, err := os.Lstat(path)
if shouldExist {
req.NoError(err)
} else {
req.Error(err)
}

if tt.isDir {
// validate dir has contents
d, err := os.Open(path)
req.NoError(err)
defer d.Close()

names, err := d.Readdirnames(-1)
req.NoError(err)
req.NotEmpty(names)
}

// remove the path
err = RemoveAll(path)
req.NoError(err)

if !tt.isDir {
// file should be gone
_, err := os.Lstat(path)
req.Error(err)
} else {
// dir should exist and be empty
_, err := os.Lstat(path)
req.NoError(err)

d, err := os.Open(path)
req.NoError(err)
defer d.Close()

names, err := d.Readdirnames(-1)
req.NoError(err)
req.Empty(names)
}
})
}
}

0 comments on commit cfa55c1

Please sign in to comment.