Skip to content
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
13 changes: 10 additions & 3 deletions image/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,14 @@ func (c *Cache) buildRefMap() map[string][]string {
if digest == "" {
continue
}
// Skip empty image refs from legacy-format files. The entry still
// counts as referenced for GC (via liveDigests), but we don't add
// an empty string to the Refs slice.
if imageRef != "" {
refMap[digest] = append(refMap[digest], imageRef)
} else if _, exists := refMap[digest]; !exists {
// Legacy-format ref (digest only, no image name). Mark the
// digest as referenced so it is not reported as orphaned.
// The placeholder is replaced once the ref is upgraded to
// extended format (e.g. on next LookupRef hit).
refMap[digest] = []string{"(unknown image)"}
}
}
return refMap
Expand Down Expand Up @@ -424,6 +427,10 @@ func (c *Cache) LookupRef(imageRef string) *RootFS {
return nil
}

// Upgrade legacy-format ref files (digest only) to extended format
// (imageRef\tdigest) so that List/GC can recover the image name.
c.putRef(imageRef, digest)

rootfsPath, ok := c.Get(digest)
if !ok {
return nil
Expand Down
47 changes: 44 additions & 3 deletions image/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,10 +353,10 @@ func TestCache_List_LegacyRefFormat(t *testing.T) {
require.NoError(t, err)
require.Len(t, entries, 1)

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

// --- GC tests ---
Expand Down Expand Up @@ -584,6 +584,47 @@ func TestCache_GC_CorruptRefFiles(t *testing.T) {
assert.Equal(t, 1, removed)
}

func TestLookupRef_UpgradesLegacyRef(t *testing.T) {
t.Parallel()

cacheDir := t.TempDir()
c := NewCache(cacheDir)

digest := "sha256:legacyupgrade"
imageRef := "ghcr.io/org/image:latest"

// Create a rootfs entry with an OCI config.
rootfsDir := filepath.Join(cacheDir, "sha256-legacyupgrade")
require.NoError(t, os.MkdirAll(rootfsDir, 0o700))
require.NoError(t, os.WriteFile(
filepath.Join(rootfsDir, ".oci-config.json"),
[]byte(`{"Entrypoint":["/bin/sh"]}`), 0o600,
))

// Write a legacy-format ref file (digest only, no imageRef).
refsDir := filepath.Join(cacheDir, "refs")
require.NoError(t, os.MkdirAll(refsDir, 0o700))
p := c.refPath(imageRef)
require.NoError(t, os.WriteFile(p, []byte(digest+"\n"), 0o600))

// LookupRef should succeed (legacy format is readable).
result := c.LookupRef(imageRef)
require.NotNil(t, result)
assert.Equal(t, rootfsDir, result.Path)

// After LookupRef, the ref file should be upgraded to extended format.
data, err := os.ReadFile(p)
require.NoError(t, err)
assert.Equal(t, imageRef+"\t"+digest+"\n", string(data),
"LookupRef should upgrade legacy ref to extended format")

// List should now show the real image name instead of (unknown image).
entries, err := c.List()
require.NoError(t, err)
require.Len(t, entries, 1)
assert.Equal(t, []string{imageRef}, entries[0].Refs)
}

func TestGetRef_BackwardCompatible(t *testing.T) {
t.Parallel()

Expand Down
Loading