Repository: spegel-org/spegel
Pull request #604 (2024-10-13 11:38:27)

Create empty backup directory when mirror directory is empty
This change makes sure that mirror configuration created by Spegel is not backed up after a restart when the mirror configuration is empty. We use the presence of a backup directory  as a way to indicate that the backup as been done. If we don't create it on the first run, when Spegel restarts it is then assumed that backup has not been done before.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7fc1de0..867de81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Fixed
 
 - [#603](https://github.com/spegel-org/spegel/pull/603) Fix append to backup always happening.
+- [#604](https://github.com/spegel-org/spegel/pull/604) Create empty backup directory when mirror directory is empty.
 
 ### Security
 
diff --git a/pkg/oci/containerd.go b/pkg/oci/containerd.go
index f812646..26f96c9 100644
--- a/pkg/oci/containerd.go
+++ b/pkg/oci/containerd.go
@@ -410,20 +410,17 @@ func validateRegistries(urls []url.URL) error {
 
 func backupConfig(log logr.Logger, fs afero.Fs, configPath string) error {
 	backupDirPath := path.Join(configPath, backupDir)
-	_, err := fs.Stat(backupDirPath)
-	if err != nil && !os.IsNotExist(err) {
+	ok, err := afero.DirExists(fs, backupDirPath)
+	if err != nil {
 		return err
 	}
-	if err == nil {
+	if ok {
 		return nil
 	}
 	files, err := afero.ReadDir(fs, configPath)
 	if err != nil {
 		return err
 	}
-	if len(files) == 0 {
-		return nil
-	}
 	err = fs.MkdirAll(backupDirPath, 0o755)
 	if err != nil {
 		return err


Test patch:
diff --git a/pkg/oci/containerd_test.go b/pkg/oci/containerd_test.go
index 5fa4d82..9b96690 100644
--- a/pkg/oci/containerd_test.go
+++ b/pkg/oci/containerd_test.go
@@ -10,6 +10,7 @@ import (
 
 	eventtypes "github.com/containerd/containerd/api/events"
 	"github.com/containerd/typeurl/v2"
+	"github.com/go-logr/logr"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/require"
 	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
@@ -92,6 +93,38 @@ func TestVerifyStatusResponse(t *testing.T) {
 	}
 }
 
+func TestBackupConfig(t *testing.T) {
+	t.Parallel()
+
+	log := logr.Discard()
+
+	fs := afero.NewMemMapFs()
+	err := fs.MkdirAll("/config", 0o755)
+	require.NoError(t, err)
+	err = backupConfig(log, fs, "/config")
+	require.NoError(t, err)
+	ok, err := afero.DirExists(fs, "/config/_backup/")
+	require.NoError(t, err)
+	require.True(t, ok)
+	files, err := afero.ReadDir(fs, "/config/_backup")
+	require.NoError(t, err)
+	require.Empty(t, files)
+
+	fs = afero.NewMemMapFs()
+	err = fs.MkdirAll("/config", 0o755)
+	require.NoError(t, err)
+	err = afero.WriteFile(fs, "/config/test.txt", []byte("Hello World"), 0o644)
+	require.NoError(t, err)
+	err = backupConfig(log, fs, "/config/")
+	require.NoError(t, err)
+	ok, err = afero.DirExists(fs, "/config/_backup/")
+	require.NoError(t, err)
+	require.True(t, ok)
+	files, err = afero.ReadDir(fs, "/config/_backup/")
+	require.NoError(t, err)
+	require.Len(t, files, 1)
+}
+
 func TestCreateFilter(t *testing.T) {
 	t.Parallel()
 
@@ -450,11 +483,9 @@ capabilities = ['pull', 'resolve']`,
 			}
 			err := AddMirrorConfiguration(context.TODO(), fs, registryConfigPath, tt.registries, tt.mirrors, tt.resolveTags, tt.appendToBackup)
 			require.NoError(t, err)
-			if len(tt.existingFiles) == 0 {
-				ok, err := afero.DirExists(fs, "/etc/containerd/certs.d/_backup")
-				require.NoError(t, err)
-				require.False(t, ok)
-			}
+			ok, err := afero.DirExists(fs, "/etc/containerd/certs.d/_backup")
+			require.NoError(t, err)
+			require.True(t, ok)
 			err = afero.Walk(fs, registryConfigPath, func(path string, fi iofs.FileInfo, _ error) error {
 				if fi.IsDir() {
 					return nil