Skip to content

Commit 162c4f1

Browse files
authored
Internal Rukpak Tests (#1100)
Adds more tests to the packages migrated from rukpak. Signed-off-by: dtfranz <dfranz@redhat.com>
1 parent 2391092 commit 162c4f1

File tree

3 files changed

+625
-0
lines changed

3 files changed

+625
-0
lines changed
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
package source_test
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"log"
10+
"net/http"
11+
"net/http/httptest"
12+
"net/url"
13+
"os"
14+
"path/filepath"
15+
"testing"
16+
17+
"github.com/google/go-containerregistry/pkg/crane"
18+
"github.com/google/go-containerregistry/pkg/name"
19+
v1 "github.com/google/go-containerregistry/pkg/v1"
20+
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
22+
23+
"github.com/operator-framework/operator-controller/internal/rukpak/source"
24+
)
25+
26+
const (
27+
testFilePathBase string = ".image-registry-test"
28+
bogusDigestHex string = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
29+
testImageTag string = "test-tag"
30+
testImageName string = "test-image"
31+
badImageName string = "bad-image"
32+
testFileName string = "test-file"
33+
testFileContents string = "test-content"
34+
)
35+
36+
func newReference(host, repo, ref string) (name.Reference, error) {
37+
tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", host, repo, ref), name.WeakValidation)
38+
if err == nil {
39+
return tag, nil
40+
}
41+
return name.NewDigest(fmt.Sprintf("%s/%s@%s", host, repo, ref), name.WeakValidation)
42+
}
43+
44+
func imageToRawManifest(img v1.Image) ([]byte, error) {
45+
manifest, err := img.Manifest()
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
layers, err := img.Layers()
51+
if err != nil {
52+
return nil, err
53+
}
54+
55+
rc, err := layers[0].Compressed()
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
lb, err := io.ReadAll(rc)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
manifest.Layers[0].Data = lb
66+
rawManifest, err := json.Marshal(manifest)
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
return rawManifest, nil
72+
}
73+
74+
// Adapted from: https://github.com/google/go-containerregistry/blob/main/pkg/v1/remote/image_test.go
75+
// serveImageManifest starts a primitive image registry server hosting two images: "test-image" and "bad-image".
76+
func serveImageManifest(rawManifest []byte) *httptest.Server {
77+
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
78+
switch r.URL.Path {
79+
case "/v2/":
80+
w.WriteHeader(http.StatusOK)
81+
case fmt.Sprintf("/v2/%s/manifests/%s", testImageName, testImageTag):
82+
if r.Method == http.MethodHead {
83+
w.Header().Set("Content-Length", fmt.Sprint(len(rawManifest)))
84+
w.Header().Set("Docker-Content-Digest", fmt.Sprintf("sha256:%s", bogusDigestHex))
85+
w.WriteHeader(http.StatusOK)
86+
} else if r.Method != http.MethodGet {
87+
w.WriteHeader(http.StatusBadRequest)
88+
}
89+
_, err := w.Write(rawManifest)
90+
if err != nil {
91+
w.WriteHeader(http.StatusInternalServerError)
92+
}
93+
case fmt.Sprintf("/v2/%s/manifests/%s", badImageName, testImageTag):
94+
case fmt.Sprintf("/v2/%s/manifests/sha256:%s", badImageName, bogusDigestHex):
95+
if r.Method == http.MethodHead {
96+
w.Header().Set("Content-Length", fmt.Sprint(len(rawManifest)))
97+
w.Header().Set("Docker-Content-Digest", fmt.Sprintf("sha256:%s", bogusDigestHex))
98+
// We must set Content-Type since we're returning empty data below
99+
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
100+
w.WriteHeader(http.StatusOK)
101+
return
102+
} else if r.Method != http.MethodGet {
103+
w.WriteHeader(http.StatusBadRequest)
104+
}
105+
_, err := w.Write(make([]byte, 0))
106+
if err != nil {
107+
w.WriteHeader(http.StatusInternalServerError)
108+
}
109+
default:
110+
w.WriteHeader(http.StatusBadRequest)
111+
}
112+
}))
113+
}
114+
115+
func testFileCleanup() {
116+
if _, err := os.Stat(testFilePathBase); err != nil && errors.Is(err, os.ErrNotExist) {
117+
// Nothing to clean up
118+
return
119+
} else if err != nil {
120+
log.Fatalf("error occurred locating unpack folder in post-test cleanup: %v", err)
121+
}
122+
// Ensure permissions and remove the temporary directory
123+
err := os.Chmod(testFilePathBase, os.ModePerm)
124+
if err != nil {
125+
log.Fatalf("error occurred ensuring unpack folder permissions in post-test cleanup: %v", err)
126+
}
127+
err = os.RemoveAll(testFilePathBase)
128+
if err != nil {
129+
log.Fatalf("error occurred deleting unpack folder in post-test cleanup: %v", err)
130+
}
131+
}
132+
133+
var HostedImageReference name.Reference
134+
135+
func TestMain(m *testing.M) {
136+
// Generate an image with file contents
137+
img, err := crane.Image(map[string][]byte{testFileName: []byte(testFileContents)})
138+
if err != nil {
139+
log.Fatalf("failed to generate image for test")
140+
}
141+
// Create a raw bytes manifest from the image
142+
rawManifest, err := imageToRawManifest(img)
143+
if err != nil {
144+
log.Fatalf("failed to generate manifest from image")
145+
}
146+
147+
// Start the image registry and serve the generated image manifest
148+
server := serveImageManifest(rawManifest)
149+
if err != nil {
150+
log.Fatalf("image registry server failed to start")
151+
}
152+
u, err := url.Parse(server.URL)
153+
if err != nil {
154+
log.Fatalf("invalid server URL from image registry")
155+
}
156+
HostedImageReference, err = newReference(u.Host, testImageName, testImageTag)
157+
if err != nil {
158+
log.Fatalf("failed to generate image reference for served image")
159+
}
160+
code := m.Run()
161+
server.Close()
162+
os.Exit(code)
163+
}
164+
165+
func TestUnpackValidInsecure(t *testing.T) {
166+
defer testFileCleanup()
167+
unpacker := &source.ImageRegistry{
168+
BaseCachePath: testFilePathBase,
169+
}
170+
bundleSource := &source.BundleSource{
171+
Type: source.SourceTypeImage,
172+
Image: &source.ImageSource{
173+
Ref: HostedImageReference.String(),
174+
InsecureSkipTLSVerify: true,
175+
},
176+
}
177+
178+
unpackPath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, bogusDigestHex)
179+
180+
// Create another folder to simulate an old unpacked bundle
181+
oldBundlePath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, "foo")
182+
require.NoError(t, os.MkdirAll(oldBundlePath, os.ModePerm))
183+
184+
// Attempt to pull and unpack the image
185+
result, err := unpacker.Unpack(context.Background(), bundleSource)
186+
// Check Result
187+
require.NoError(t, err)
188+
require.NotNil(t, result)
189+
assert.Equal(t, result.State, source.StateUnpacked)
190+
// Make sure the old bundle was cleaned up
191+
require.NoDirExists(t, oldBundlePath)
192+
193+
// Give permissions to read the file
194+
require.NoError(t, os.Chmod(filepath.Join(unpackPath, testFileName), 0400))
195+
unpackedFile, err := os.ReadFile(filepath.Join(unpackPath, testFileName))
196+
require.NoError(t, err)
197+
// Ensure the unpacked file matches the source content
198+
require.Equal(t, []byte(testFileContents), unpackedFile)
199+
}
200+
201+
func TestUnpackValidUsesCache(t *testing.T) {
202+
defer testFileCleanup()
203+
unpacker := &source.ImageRegistry{
204+
BaseCachePath: testFilePathBase,
205+
}
206+
bundleSource := &source.BundleSource{
207+
Type: source.SourceTypeImage,
208+
Image: &source.ImageSource{
209+
Ref: fmt.Sprintf("%s@sha256:%s", HostedImageReference.Context().Name(), bogusDigestHex),
210+
InsecureSkipTLSVerify: true,
211+
},
212+
}
213+
214+
// Populate the bundle cache with a folder
215+
testCacheFilePath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, bogusDigestHex, "test-folder")
216+
require.NoError(t, os.MkdirAll(testCacheFilePath, os.ModePerm))
217+
218+
// Attempt to pull and unpack the image
219+
result, err := unpacker.Unpack(context.Background(), bundleSource)
220+
// Check Result
221+
require.NoError(t, err)
222+
require.NotNil(t, result)
223+
assert.Equal(t, result.State, source.StateUnpacked)
224+
// Make sure the old file was not cleaned up
225+
require.DirExists(t, testCacheFilePath)
226+
}
227+
228+
func TestUnpackCacheCheckError(t *testing.T) {
229+
defer testFileCleanup()
230+
unpacker := &source.ImageRegistry{
231+
BaseCachePath: testFilePathBase,
232+
}
233+
bundleSource := &source.BundleSource{
234+
Type: source.SourceTypeImage,
235+
Image: &source.ImageSource{
236+
Ref: HostedImageReference.String(),
237+
InsecureSkipTLSVerify: true,
238+
},
239+
}
240+
241+
// Create the unpack path and restrict its permissions
242+
unpackPath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, bogusDigestHex)
243+
require.NoError(t, os.MkdirAll(unpackPath, os.ModePerm))
244+
require.NoError(t, os.Chmod(testFilePathBase, 0000))
245+
// Attempt to pull and unpack the image
246+
_, err := unpacker.Unpack(context.Background(), bundleSource)
247+
// Check Result
248+
require.Error(t, err)
249+
}
250+
251+
func TestUnpackUnservedImageReference(t *testing.T) {
252+
defer testFileCleanup()
253+
unpacker := &source.ImageRegistry{
254+
BaseCachePath: testFilePathBase,
255+
}
256+
bundleSource := &source.BundleSource{
257+
Type: source.SourceTypeImage,
258+
Image: &source.ImageSource{
259+
// Use a valid reference that is not served
260+
Ref: fmt.Sprintf("%s/%s:unserved-tag", HostedImageReference.Context().Registry.RegistryStr(), badImageName),
261+
InsecureSkipTLSVerify: true,
262+
},
263+
}
264+
265+
// Attempt to pull and unpack the image
266+
_, err := unpacker.Unpack(context.Background(), bundleSource)
267+
// Check Result
268+
require.Error(t, err)
269+
}
270+
271+
func TestUnpackFailure(t *testing.T) {
272+
defer testFileCleanup()
273+
unpacker := &source.ImageRegistry{
274+
BaseCachePath: testFilePathBase,
275+
}
276+
bundleSource := &source.BundleSource{
277+
Type: source.SourceTypeImage,
278+
Image: &source.ImageSource{
279+
// Use a valid reference that is served but will return bad image content
280+
Ref: fmt.Sprintf("%s/%s:%s", HostedImageReference.Context().Registry.RegistryStr(), badImageName, testImageTag),
281+
InsecureSkipTLSVerify: true,
282+
},
283+
}
284+
285+
// Attempt to pull and unpack the image
286+
_, err := unpacker.Unpack(context.Background(), bundleSource)
287+
// Check Result
288+
require.Error(t, err)
289+
}
290+
291+
func TestUnpackFailureDigest(t *testing.T) {
292+
defer testFileCleanup()
293+
unpacker := &source.ImageRegistry{
294+
BaseCachePath: testFilePathBase,
295+
}
296+
bundleSource := &source.BundleSource{
297+
Type: source.SourceTypeImage,
298+
Image: &source.ImageSource{
299+
// Use a valid reference that is served but will return bad image content
300+
Ref: fmt.Sprintf("%s/%s@sha256:%s", HostedImageReference.Context().Registry.RegistryStr(), badImageName, bogusDigestHex),
301+
InsecureSkipTLSVerify: true,
302+
},
303+
}
304+
305+
// Attempt to pull and unpack the image
306+
_, err := unpacker.Unpack(context.Background(), bundleSource)
307+
// Check Result
308+
require.Error(t, err)
309+
// Unpacker gives an error of type Unrecoverable
310+
require.IsType(t, &source.Unrecoverable{}, err)
311+
}
312+
313+
func TestUnpackInvalidSourceType(t *testing.T) {
314+
unpacker := &source.ImageRegistry{}
315+
// Create BundleSource with invalid source type
316+
bundleSource := &source.BundleSource{
317+
Type: "invalid",
318+
}
319+
320+
shouldPanic := func() {
321+
// Attempt to pull and unpack the image
322+
_, err := unpacker.Unpack(context.Background(), bundleSource)
323+
if err != nil {
324+
t.Error("func should have panicked")
325+
}
326+
}
327+
require.Panics(t, shouldPanic)
328+
}
329+
330+
func TestUnpackInvalidNilImage(t *testing.T) {
331+
unpacker := &source.ImageRegistry{}
332+
// Create BundleSource with nil Image
333+
bundleSource := &source.BundleSource{
334+
Type: source.SourceTypeImage,
335+
Image: nil,
336+
}
337+
338+
// Attempt to unpack
339+
result, err := unpacker.Unpack(context.Background(), bundleSource)
340+
require.Error(t, err)
341+
require.NoDirExists(t, testFilePathBase)
342+
assert.Nil(t, result)
343+
}
344+
345+
func TestUnpackInvalidImageRef(t *testing.T) {
346+
unpacker := &source.ImageRegistry{}
347+
// Create BundleSource with malformed image reference
348+
bundleSource := &source.BundleSource{
349+
Type: source.SourceTypeImage,
350+
Image: &source.ImageSource{
351+
Ref: "invalid image ref",
352+
},
353+
}
354+
355+
// Attempt to unpack
356+
result, err := unpacker.Unpack(context.Background(), bundleSource)
357+
require.Error(t, err)
358+
require.NoDirExists(t, testFilePathBase)
359+
assert.Nil(t, result)
360+
}

0 commit comments

Comments
 (0)