Skip to content

Commit 75bfcc0

Browse files
committed
fix: clean up grove-configs directory when deleting a grove from hub
When deleting a grove via the hub API, the ~/.scion/grove-configs/<slug>__<uuid>/ directory (containing external settings, templates, and agent homes) was not being removed. This adds cleanup using the grove's slug and ID to compute the grove-configs directory path via GroveMarker, then calls RemoveGroveConfig.
1 parent d65dc09 commit 75bfcc0

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

pkg/hub/handlers.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3560,6 +3560,25 @@ func (s *Server) deleteGrove(w http.ResponseWriter, r *http.Request, id string)
35603560
}
35613561
}
35623562

3563+
// Clean up the grove-configs directory (~/.scion/grove-configs/<slug>__<short-uuid>/).
3564+
// This stores external settings, templates, and agent homes for both
3565+
// git-backed linked groves and non-git external groves.
3566+
if grove.Slug != "" && grove.ID != "" {
3567+
marker := &config.GroveMarker{
3568+
GroveID: grove.ID,
3569+
GroveSlug: grove.Slug,
3570+
}
3571+
if configPath, err := marker.ExternalGrovePath(); err == nil {
3572+
// ExternalGrovePath returns <grove-configs>/<slug__uuid>/.scion —
3573+
// remove the parent (<slug__uuid>) directory.
3574+
groveConfigDir := filepath.Dir(configPath)
3575+
if err := config.RemoveGroveConfig(groveConfigDir); err != nil && !os.IsNotExist(err) {
3576+
slog.Warn("failed to remove grove config directory",
3577+
"grove", id, "slug", grove.Slug, "path", groveConfigDir, "error", err)
3578+
}
3579+
}
3580+
}
3581+
35633582
s.events.PublishGroveDeleted(ctx, id)
35643583

35653584
w.WriteHeader(http.StatusNoContent)

pkg/hub/handlers_grove_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727

2828
"github.com/GoogleCloudPlatform/scion/pkg/agent/state"
2929
"github.com/GoogleCloudPlatform/scion/pkg/api"
30+
"github.com/GoogleCloudPlatform/scion/pkg/config"
3031
"github.com/GoogleCloudPlatform/scion/pkg/store"
3132
"github.com/GoogleCloudPlatform/scion/pkg/util"
3233
"github.com/stretchr/testify/assert"
@@ -1084,6 +1085,48 @@ func TestDeleteGrove_CascadesEnvVarsSecretsHarnessConfigs(t *testing.T) {
10841085
assert.Len(t, hubVars, 1, "hub-scoped env var should not be affected")
10851086
}
10861087

1088+
// TestDeleteGrove_CleansUpGroveConfigsDir verifies that deleting a grove
1089+
// removes the ~/.scion/grove-configs/<slug>__<short-uuid>/ directory.
1090+
func TestDeleteGrove_CleansUpGroveConfigsDir(t *testing.T) {
1091+
srv, s := testServer(t)
1092+
ctx := context.Background()
1093+
1094+
grove := createTestGitGrove(t, srv, "Config Cleanup Test", "github.com/test/config-cleanup-repo")
1095+
1096+
// Create the grove-configs directory that would exist in workstation mode
1097+
marker := &config.GroveMarker{
1098+
GroveID: grove.ID,
1099+
GroveSlug: grove.Slug,
1100+
}
1101+
extPath, err := marker.ExternalGrovePath()
1102+
require.NoError(t, err)
1103+
1104+
// Create the directory structure: ~/.scion/grove-configs/<slug>__<uuid>/.scion/
1105+
require.NoError(t, os.MkdirAll(extPath, 0755))
1106+
// Also create an agents/ sibling directory
1107+
agentsDir := filepath.Join(filepath.Dir(extPath), "agents", "test-agent", "home")
1108+
require.NoError(t, os.MkdirAll(agentsDir, 0755))
1109+
1110+
groveConfigDir := filepath.Dir(extPath)
1111+
t.Cleanup(func() { os.RemoveAll(groveConfigDir) })
1112+
1113+
// Verify directory exists before deletion
1114+
_, err = os.Stat(groveConfigDir)
1115+
require.NoError(t, err, "grove-configs dir should exist before deletion")
1116+
1117+
// Delete grove via API
1118+
rec := doRequest(t, srv, http.MethodDelete, "/api/v1/groves/"+grove.ID, nil)
1119+
assert.Equal(t, http.StatusNoContent, rec.Code)
1120+
1121+
// Verify grove-configs directory was removed
1122+
_, err = os.Stat(groveConfigDir)
1123+
assert.True(t, os.IsNotExist(err), "grove-configs dir should be removed after grove deletion")
1124+
1125+
// Verify grove deleted from database
1126+
_, err = s.GetGrove(ctx, grove.ID)
1127+
assert.ErrorIs(t, err, store.ErrNotFound)
1128+
}
1129+
10871130
// TestGroveSyncTemplates_GroveNotFound verifies 404 for non-existent grove.
10881131
func TestGroveSyncTemplates_GroveNotFound(t *testing.T) {
10891132
srv, _ := testServer(t)

0 commit comments

Comments
 (0)