@@ -20,6 +20,7 @@ import (
20
20
"errors"
21
21
"fmt"
22
22
"io"
23
+ "io/fs"
23
24
"os"
24
25
"path/filepath"
25
26
@@ -37,6 +38,100 @@ var layoutFile = `{
37
38
"imageLayoutVersion": "1.0.0"
38
39
}`
39
40
41
+ // GarbageCollect removes unreferenced blobs from the oci-layout
42
+ func (l Path ) GarbageCollect () error {
43
+ idx , err := l .ImageIndex ()
44
+ if err != nil {
45
+ return err
46
+ }
47
+ blobsToKeep := map [string ]bool {}
48
+ if err := l .garbageCollectImageIndex (idx , blobsToKeep ); err != nil {
49
+ return err
50
+ }
51
+ blobsDir := l .path ("blobs" )
52
+
53
+ if err := filepath .WalkDir (blobsDir , func (path string , d fs.DirEntry , err error ) error {
54
+ if d .IsDir () {
55
+ return nil
56
+ }
57
+
58
+ rel , err := filepath .Rel (blobsDir , path )
59
+ if err != nil {
60
+ return err
61
+ }
62
+ if ok := blobsToKeep [rel ]; ! ok {
63
+ if err := os .Remove (path ); err != nil {
64
+ return err
65
+ }
66
+ }
67
+ return nil
68
+ }); err != nil {
69
+ return err
70
+ }
71
+
72
+ return nil
73
+ }
74
+
75
+ func (l Path ) garbageCollectImageIndex (index v1.ImageIndex , blobsToKeep map [string ]bool ) error {
76
+ idxm , err := index .IndexManifest ()
77
+ if err != nil {
78
+ return err
79
+ }
80
+ if h , err := index .Digest (); err != nil {
81
+ return err
82
+ } else {
83
+ blobsToKeep [fmt .Sprintf ("%s/%s" , h .Algorithm , h .Hex )] = true
84
+ }
85
+ for _ , descriptor := range idxm .Manifests {
86
+ if descriptor .MediaType .IsImage () {
87
+ img , err := index .Image (descriptor .Digest )
88
+ if err != nil {
89
+ return err
90
+ }
91
+ if err := l .garbageCollectImage (img , blobsToKeep ); err != nil {
92
+ return err
93
+ }
94
+ } else if descriptor .MediaType .IsIndex () {
95
+ idx , err := index .ImageIndex (descriptor .Digest )
96
+ if err != nil {
97
+ return err
98
+ }
99
+ if err := l .garbageCollectImageIndex (idx , blobsToKeep ); err != nil {
100
+ return err
101
+ }
102
+ }
103
+ }
104
+ return nil
105
+ }
106
+
107
+ func (l Path ) garbageCollectImage (image v1.Image , blobsToKeep map [string ]bool ) error {
108
+
109
+ if h , err := image .Digest (); err != nil {
110
+ return err
111
+ } else {
112
+ blobsToKeep [fmt .Sprintf ("%s/%s" , h .Algorithm , h .Hex )] = true
113
+ }
114
+
115
+ if h , err := image .ConfigName (); err != nil {
116
+ return err
117
+ } else {
118
+ blobsToKeep [fmt .Sprintf ("%s/%s" , h .Algorithm , h .Hex )] = true
119
+ }
120
+
121
+ ls , err := image .Layers ()
122
+ if err != nil {
123
+ return err
124
+ }
125
+ for _ , l := range ls {
126
+ if h , err := l .Digest (); err != nil {
127
+ return err
128
+ } else {
129
+ blobsToKeep [fmt .Sprintf ("%s/%s" , h .Algorithm , h .Hex )] = true
130
+ }
131
+ }
132
+ return nil
133
+ }
134
+
40
135
// AppendImage writes a v1.Image to the Path and updates
41
136
// the index.json to reference it.
42
137
func (l Path ) AppendImage (img v1.Image , options ... Option ) error {
0 commit comments