Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make reset work with bind mounts #1025

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
})
}
}
Loading