Skip to content

Commit 7c27de6

Browse files
authored
Merge pull request #52 from stacklok/fix-legacy-ref-orphan-display
Fix legacy ref files showing as orphans in cache list
2 parents 48e3692 + 0cf675b commit 7c27de6

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

image/cache.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,14 @@ func (c *Cache) buildRefMap() map[string][]string {
327327
if digest == "" {
328328
continue
329329
}
330-
// Skip empty image refs from legacy-format files. The entry still
331-
// counts as referenced for GC (via liveDigests), but we don't add
332-
// an empty string to the Refs slice.
333330
if imageRef != "" {
334331
refMap[digest] = append(refMap[digest], imageRef)
332+
} else if _, exists := refMap[digest]; !exists {
333+
// Legacy-format ref (digest only, no image name). Mark the
334+
// digest as referenced so it is not reported as orphaned.
335+
// The placeholder is replaced once the ref is upgraded to
336+
// extended format (e.g. on next LookupRef hit).
337+
refMap[digest] = []string{"(unknown image)"}
335338
}
336339
}
337340
return refMap
@@ -424,6 +427,10 @@ func (c *Cache) LookupRef(imageRef string) *RootFS {
424427
return nil
425428
}
426429

430+
// Upgrade legacy-format ref files (digest only) to extended format
431+
// (imageRef\tdigest) so that List/GC can recover the image name.
432+
c.putRef(imageRef, digest)
433+
427434
rootfsPath, ok := c.Get(digest)
428435
if !ok {
429436
return nil

image/cache_test.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,10 @@ func TestCache_List_LegacyRefFormat(t *testing.T) {
353353
require.NoError(t, err)
354354
require.Len(t, entries, 1)
355355

356-
// Legacy format: entry is referenced (not orphaned) but imageRef
357-
// is not recoverable, so Refs is empty (no empty strings).
356+
// Legacy format: entry is referenced (not orphaned) but the original
357+
// image name is not recoverable, so a placeholder is used.
358358
assert.Equal(t, "sha256:legacy", entries[0].Digest)
359-
assert.Empty(t, entries[0].Refs)
359+
assert.Equal(t, []string{"(unknown image)"}, entries[0].Refs)
360360
}
361361

362362
// --- GC tests ---
@@ -584,6 +584,47 @@ func TestCache_GC_CorruptRefFiles(t *testing.T) {
584584
assert.Equal(t, 1, removed)
585585
}
586586

587+
func TestLookupRef_UpgradesLegacyRef(t *testing.T) {
588+
t.Parallel()
589+
590+
cacheDir := t.TempDir()
591+
c := NewCache(cacheDir)
592+
593+
digest := "sha256:legacyupgrade"
594+
imageRef := "ghcr.io/org/image:latest"
595+
596+
// Create a rootfs entry with an OCI config.
597+
rootfsDir := filepath.Join(cacheDir, "sha256-legacyupgrade")
598+
require.NoError(t, os.MkdirAll(rootfsDir, 0o700))
599+
require.NoError(t, os.WriteFile(
600+
filepath.Join(rootfsDir, ".oci-config.json"),
601+
[]byte(`{"Entrypoint":["/bin/sh"]}`), 0o600,
602+
))
603+
604+
// Write a legacy-format ref file (digest only, no imageRef).
605+
refsDir := filepath.Join(cacheDir, "refs")
606+
require.NoError(t, os.MkdirAll(refsDir, 0o700))
607+
p := c.refPath(imageRef)
608+
require.NoError(t, os.WriteFile(p, []byte(digest+"\n"), 0o600))
609+
610+
// LookupRef should succeed (legacy format is readable).
611+
result := c.LookupRef(imageRef)
612+
require.NotNil(t, result)
613+
assert.Equal(t, rootfsDir, result.Path)
614+
615+
// After LookupRef, the ref file should be upgraded to extended format.
616+
data, err := os.ReadFile(p)
617+
require.NoError(t, err)
618+
assert.Equal(t, imageRef+"\t"+digest+"\n", string(data),
619+
"LookupRef should upgrade legacy ref to extended format")
620+
621+
// List should now show the real image name instead of (unknown image).
622+
entries, err := c.List()
623+
require.NoError(t, err)
624+
require.Len(t, entries, 1)
625+
assert.Equal(t, []string{imageRef}, entries[0].Refs)
626+
}
627+
587628
func TestGetRef_BackwardCompatible(t *testing.T) {
588629
t.Parallel()
589630

0 commit comments

Comments
 (0)