Skip to content

Commit b03fec2

Browse files
author
Michal Minář
committed
Extended test for registry garbage collector
Signed-off-by: Michal Minář <[email protected]>
1 parent fce1a37 commit b03fec2

File tree

4 files changed

+570
-134
lines changed

4 files changed

+570
-134
lines changed

test/extended/images/hardprune.go

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package images
2+
3+
import (
4+
"fmt"
5+
6+
g "github.com/onsi/ginkgo"
7+
o "github.com/onsi/gomega"
8+
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
kerrors "k8s.io/apimachinery/pkg/util/errors"
11+
"k8s.io/apimachinery/pkg/util/sets"
12+
13+
"github.com/docker/distribution/digest"
14+
"github.com/docker/distribution/manifest/schema2"
15+
16+
imageapi "github.com/openshift/origin/pkg/image/api"
17+
registryutil "github.com/openshift/origin/test/extended/registry/util"
18+
exutil "github.com/openshift/origin/test/extended/util"
19+
testutil "github.com/openshift/origin/test/util"
20+
)
21+
22+
type repoLink map[string][]string
23+
type expectedDeletions struct {
24+
manifestLinks repoLink
25+
layerLinks repoLink
26+
blobs []string
27+
}
28+
29+
var _ = g.Describe("[Feature:ImagePrune] Image hard prune", func() {
30+
defer g.GinkgoRecover()
31+
var oc = exutil.NewCLI("prune-images", exutil.KubeConfigPath())
32+
var originalAcceptSchema2 *bool
33+
34+
g.JustBeforeEach(func() {
35+
if originalAcceptSchema2 == nil {
36+
accepts, err := registryutil.DoesRegistryAcceptSchema2(oc)
37+
o.Expect(err).NotTo(o.HaveOccurred())
38+
originalAcceptSchema2 = &accepts
39+
}
40+
41+
readOnly := false
42+
acceptSchema2 := true
43+
err := registryutil.ConfigureRegistry(oc,
44+
registryutil.RegistryConfiguration{
45+
ReadOnly: &readOnly,
46+
AcceptSchema2: &acceptSchema2,
47+
})
48+
o.Expect(err).NotTo(o.HaveOccurred())
49+
50+
err = exutil.WaitForBuilderAccount(oc.KubeClient().Core().ServiceAccounts(oc.Namespace()))
51+
o.Expect(err).NotTo(o.HaveOccurred())
52+
53+
g.By(fmt.Sprintf("give a user %s a right to prune images with %s role", oc.Username(), "system:image-pruner"))
54+
err = oc.AsAdmin().WithoutNamespace().Run("adm").Args("policy", "add-cluster-role-to-user", "system:image-pruner", oc.Username()).Execute()
55+
o.Expect(err).NotTo(o.HaveOccurred())
56+
})
57+
58+
g.AfterEach(func() {
59+
readOnly := false
60+
err := registryutil.ConfigureRegistry(oc,
61+
registryutil.RegistryConfiguration{
62+
ReadOnly: &readOnly,
63+
AcceptSchema2: originalAcceptSchema2,
64+
})
65+
o.Expect(err).NotTo(o.HaveOccurred())
66+
})
67+
68+
g.It("should prune orphaned blobs", func() {
69+
oc.SetOutputDir(exutil.TestContext.OutputDir)
70+
outSink := g.GinkgoWriter
71+
registryURL, err := registryutil.GetDockerRegistryURL(oc)
72+
o.Expect(err).NotTo(o.HaveOccurred())
73+
74+
cleanUp := cleanUpContainer{oc: oc}
75+
defer cleanUp.run()
76+
77+
dClient, err := testutil.NewDockerClient()
78+
o.Expect(err).NotTo(o.HaveOccurred())
79+
80+
_, err = RunHardPrune(oc)
81+
o.Expect(err).NotTo(o.HaveOccurred())
82+
83+
baseImg1, imageId, err := BuildAndPushImageOfSizeWithDocker(oc, dClient, "A", "latest", testImageSize, 2, outSink, true, false)
84+
o.Expect(err).NotTo(o.HaveOccurred())
85+
cleanUp.addImage(baseImg1, imageId, "")
86+
baseImg1Spec := fmt.Sprintf("%s/%s/A:latest", registryURL, oc.Namespace())
87+
88+
baseImg2, imageId, err := BuildAndPushImageOfSizeWithDocker(oc, dClient, "B", "latest", testImageSize, 2, outSink, true, false)
89+
o.Expect(err).NotTo(o.HaveOccurred())
90+
cleanUp.addImage(baseImg2, imageId, "")
91+
baseImg2Spec := fmt.Sprintf("%s/%s/B:latest", registryURL, oc.Namespace())
92+
93+
baseImg3, imageId, err := BuildAndPushImageOfSizeWithDocker(oc, dClient, "C", "latest", testImageSize, 2, outSink, true, false)
94+
o.Expect(err).NotTo(o.HaveOccurred())
95+
cleanUp.addImage(baseImg3, imageId, "")
96+
baseImg3Spec := fmt.Sprintf("%s/%s/C:latest", registryURL, oc.Namespace())
97+
98+
baseImg4, imageId, err := BuildAndPushImageOfSizeWithDocker(oc, dClient, "A", "latest", testImageSize, 2, outSink, true, false)
99+
o.Expect(err).NotTo(o.HaveOccurred())
100+
cleanUp.addImage(baseImg4, imageId, "")
101+
//baseImg4Spec := fmt.Sprintf("%s/%s/A:new", registryURL, oc.Namespace())
102+
103+
childImg1, imageId, err := BuildAndPushChildImage(oc, dClient, baseImg1Spec, "A", "latest", 1, outSink, true)
104+
o.Expect(err).NotTo(o.HaveOccurred())
105+
cleanUp.addImage(childImg1, "", "")
106+
childImg2, imageId, err := BuildAndPushChildImage(oc, dClient, baseImg2Spec, "B", "latest", 1, outSink, true)
107+
o.Expect(err).NotTo(o.HaveOccurred())
108+
cleanUp.addImage(childImg2, "", "")
109+
childImg3, imageId, err := BuildAndPushChildImage(oc, dClient, baseImg3Spec, "D", "latest", 1, outSink, true)
110+
o.Expect(err).NotTo(o.HaveOccurred())
111+
cleanUp.addImage(childImg3, "", "")
112+
113+
err = oc.Run("tag").Args("--source=istag", "A:latest", "Atagged:latest").Execute()
114+
o.Expect(err).NotTo(o.HaveOccurred())
115+
116+
imgs := map[string]*imageapi.Image{}
117+
for _, imgName := range []string{baseImg1, baseImg2, baseImg3, baseImg4, childImg1, childImg2, childImg3} {
118+
img, err := oc.AsAdmin().Client().Images().Get(imgName, metav1.GetOptions{})
119+
o.Expect(err).NotTo(o.HaveOccurred())
120+
imgs[imgName] = img
121+
o.Expect(img.DockerImageManifestMediaType).To(o.Equal(schema2.MediaTypeManifest))
122+
}
123+
124+
deleted, err := RunHardPrune(oc)
125+
o.Expect(err).NotTo(o.HaveOccurred())
126+
// this shouldn't delete anything
127+
o.Expect(deleted).To(o.Equal([]string{}))
128+
129+
readOnly := true
130+
err = registryutil.ConfigureRegistry(oc, registryutil.RegistryConfiguration{ReadOnly: &readOnly})
131+
o.Expect(err).NotTo(o.HaveOccurred())
132+
133+
/* imageName | parent | layers | imagestreams
134+
* ---------- | -------- | ------ | ------------
135+
* baseImg1 | | 1 2 | A Atagged
136+
* baseImg2 | | 4 5 | B
137+
* baseImg3 | | 7 8 | C
138+
* baseImg4 | | 11 12 | A
139+
* childImg1 | baseImg1 | 1 2 3 | C
140+
* childImg2 | baseImg2 | 4 5 6 | B
141+
* childImg3 | baseImg3 | 7 8 9 | D
142+
*/
143+
144+
// verify that the registry doesn't accept any images
145+
_, _, err = BuildAndPushImageOfSizeWithDocker(oc, dClient, "fail", "tag", testImageSize, 1, outSink, false, true)
146+
o.Expect(err).NotTo(o.HaveOccurred())
147+
148+
err = oc.AsAdmin().Client().ImageStreamTags(oc.Namespace()).Delete("A", "latest")
149+
deleted, err = RunHardPrune(oc)
150+
o.Expect(err).NotTo(o.HaveOccurred())
151+
err = expectDeletions(deleted, oc.Namespace(),
152+
expectedDeletions{
153+
manifestLinks: repoLink{"A": []string{baseImg1}},
154+
layerLinks: repoLink{"A": []string{
155+
imgs[baseImg1].DockerImageMetadata.ID,
156+
imgs[baseImg1].DockerImageLayers[0].Name,
157+
imgs[baseImg1].DockerImageLayers[1].Name,
158+
}},
159+
blobs: []string{imgs[baseImg1].DockerImageMetadata.ID},
160+
})
161+
o.Expect(err).NotTo(o.HaveOccurred())
162+
163+
err = oc.AsAdmin().Client().Images().Delete(childImg1)
164+
deleted, err = RunHardPrune(oc)
165+
o.Expect(err).NotTo(o.HaveOccurred())
166+
err = expectDeletions(deleted, oc.Namespace(),
167+
expectedDeletions{
168+
manifestLinks: repoLink{"C": []string{childImg1}},
169+
layerLinks: repoLink{"C": []string{
170+
imgs[childImg1].DockerImageMetadata.ID,
171+
imgs[childImg1].DockerImageLayers[0].Name,
172+
}},
173+
blobs: []string{
174+
childImg1,
175+
imgs[childImg1].DockerImageMetadata.ID,
176+
imgs[childImg1].DockerImageLayers[0].Name,
177+
},
178+
})
179+
o.Expect(err).NotTo(o.HaveOccurred())
180+
181+
err = oc.AsAdmin().Client().Images().Delete(baseImg1)
182+
deleted, err = RunHardPrune(oc)
183+
o.Expect(err).NotTo(o.HaveOccurred())
184+
err = expectDeletions(deleted, oc.Namespace(),
185+
expectedDeletions{
186+
blobs: []string{
187+
imgs[baseImg1].DockerImageLayers[0].Name,
188+
imgs[baseImg1].DockerImageLayers[1].Name,
189+
},
190+
})
191+
o.Expect(err).NotTo(o.HaveOccurred())
192+
193+
// TODO continue to remove images
194+
})
195+
196+
})
197+
198+
func expectDeletions(deletedPaths []string, namespace string, expect expectedDeletions) error {
199+
var errors []error
200+
expectedPaths := sets.NewString()
201+
deletedSet := sets.NewString()
202+
verifiedSet := sets.NewString()
203+
204+
for repo, links := range expect.layerLinks {
205+
for _, link := range links {
206+
dgst := digest.Digest(link)
207+
expectedPaths.Insert(fmt.Sprintf("v2/repositories/%s/%s/_layers/%s/%s/link",
208+
namespace, repo, dgst.Algorithm(), dgst.Hex()))
209+
}
210+
}
211+
for repo, links := range expect.manifestLinks {
212+
for _, link := range links {
213+
dgst := digest.Digest(link)
214+
expectedPaths.Insert(fmt.Sprintf("v2/repositories/%s/%s/_manifests/revisions/%s/%s/link",
215+
namespace, repo, dgst.Algorithm(), dgst.Hex()))
216+
}
217+
}
218+
for _, blob := range expect.blobs {
219+
dgst := digest.Digest(blob)
220+
expectedPaths.Insert(fmt.Sprintf("v2/blobs/%s/%s/%s/data",
221+
dgst.Algorithm(), dgst.Hex()[0:2], dgst.Hex()))
222+
}
223+
224+
for _, pth := range deletedPaths {
225+
deletedSet.Insert(pth)
226+
}
227+
228+
for pth := range expectedPaths {
229+
if deletedSet.Has(pth) {
230+
verifiedSet.Insert(pth)
231+
} else {
232+
errors = append(errors, fmt.Errorf("expected path %s was not deleted", pth))
233+
}
234+
}
235+
for pth := range deletedSet {
236+
if !expectedPaths.Has(pth) {
237+
errors = append(errors, fmt.Errorf("path %s got unexpectedly deleted", pth))
238+
}
239+
}
240+
241+
return kerrors.NewAggregate(errors)
242+
}

0 commit comments

Comments
 (0)