console: clear the geoip staging directory at startup (geoip spec §4.2, codex R6-2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+31
−0
internal/console/geoippublish.go+11 −0internal/console/geoippublish_test.go+20 −0
diff --git a/internal/console/geoippublish.go b/internal/console/geoippublish.go| index f75668643..50935360a 100644 |
| --- a/internal/console/geoippublish.go |
| +++ b/internal/console/geoippublish.go |
| @@ -52,7 +52,18 @@ type geoipPublishOpts struct { |
| Force bool |
| } |
| +// newGeoipPublisher clears tmpRoot (the dedicated geoip-staging directory) |
| +// before anything can stage into it: a console that crashed mid-upload or |
| +// mid-job must not leave its 1 GiB behind for good (spec §4.2, codex R6-2). |
| func newGeoipPublisher(store release.Store, tmpRoot string) *geoipPublisher { |
| + if tmpRoot != "" { |
| + if err := os.RemoveAll(tmpRoot); err != nil { |
| + log.Printf("console: geoip staging directory could not be cleared: %v", err) |
| + } |
| + if err := os.MkdirAll(tmpRoot, 0o700); err != nil { |
| + log.Printf("console: geoip staging directory could not be created: %v", err) |
| + } |
| + } |
| return &geoipPublisher{store: store, tmpRoot: tmpRoot, maxBytes: geoip.MaxBlobBytes, |
| deadline: 15 * time.Minute, now: time.Now} |
| } |
diff --git a/internal/console/geoippublish_test.go b/internal/console/geoippublish_test.go| index 106190a70..c055dbfa2 100644 |
| --- a/internal/console/geoippublish_test.go |
| +++ b/internal/console/geoippublish_test.go |
| @@ -5,6 +5,7 @@ import ( |
| "context" |
| "errors" |
| "os" |
| + "path/filepath" |
| "strings" |
| "sync" |
| "testing" |
| @@ -223,3 +224,22 @@ func putPointer(t *testing.T, s release.Store, p geoip.Pointer) { |
| } |
| var _ = sync.Mutex{} |
| + |
| +func TestPublisherStartupClearsAbandonedStaging(t *testing.T) { |
| + withFixtureProbes(t) |
| + root := filepath.Join(t.TempDir(), "geoip-staging") |
| + abandoned := filepath.Join(root, "geoip-publish-123") |
| + if err := os.MkdirAll(abandoned, 0o700); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(filepath.Join(abandoned, "db.mmdb"), []byte("half an upload"), 0o600); err != nil { |
| + t.Fatal(err) |
| + } |
| + p := newGeoipPublisher(release.NewDirStore(t.TempDir()), root) |
| + if entries, _ := os.ReadDir(root); len(entries) != 0 { |
| + t.Fatalf("staging not cleared at startup: %v", entries) |
| + } |
| + if _, err := p.Publish(context.Background(), bytes.NewReader(fixture(t, "GeoIP2-City-Test.mmdb")), geoipPublishOpts{Origin: "manual"}); err != nil { |
| + t.Fatalf("publish after restart: %v", err) |
| + } |
| +} |