@@ -4,19 +4,21 @@ import (
44 "context"
55 "crypto/rand"
66 "crypto/x509"
7+ "encoding/pem"
78 "errors"
89 "fmt"
910 "io"
1011 "math"
1112 "math/big"
1213 "net/http"
14+ "net/url"
1315 "os"
16+ "path/filepath"
1417 "sync"
1518 "testing"
1619
17- distribution "github.com/distribution/distribution/v3"
18- "github.com/distribution/distribution/v3/configuration"
19- repositorymiddleware "github.com/distribution/distribution/v3/registry/middleware/repository"
20+ "github.com/containers/image/v5/types"
21+ "github.com/distribution/distribution/v3"
2022 "github.com/distribution/reference"
2123 "github.com/opencontainers/go-digest"
2224 "github.com/sirupsen/logrus"
@@ -25,32 +27,60 @@ import (
2527
2628 "github.com/operator-framework/operator-registry/pkg/image"
2729 "github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
30+ "github.com/operator-framework/operator-registry/pkg/image/containersimageregistry"
2831 libimage "github.com/operator-framework/operator-registry/pkg/lib/image"
2932)
3033
3134// cleanupFunc is a function that cleans up after some test infra.
3235type cleanupFunc func ()
3336
3437// newRegistryFunc is a function that creates and returns a new image.Registry to test its cleanupFunc.
35- type newRegistryFunc func (t * testing.T , cafile string ) (image.Registry , cleanupFunc )
38+ type newRegistryFunc func (t * testing.T , serverCert * x509. Certificate ) (image.Registry , cleanupFunc )
3639
37- func poolForCertFile (t * testing.T , file string ) * x509.CertPool {
38- rootCAs := x509 .NewCertPool ()
39- certs , err := os .ReadFile (file )
40+ func caDirForCert (t * testing.T , serverCert * x509.Certificate ) string {
41+ caDir , err := os .MkdirTemp ("" , "opm-registry-test-ca-" )
42+ require .NoError (t , err )
43+ caFile , err := os .Create (filepath .Join (caDir , "ca.crt" ))
4044 require .NoError (t , err )
41- require .True (t , rootCAs .AppendCertsFromPEM (certs ))
45+
46+ require .NoError (t , pem .Encode (caFile , & pem.Block {
47+ Type : "CERTIFICATE" ,
48+ Bytes : serverCert .Raw ,
49+ }))
50+ require .NoError (t , caFile .Close ())
51+ return caDir
52+ }
53+
54+ func poolForCert (serverCert * x509.Certificate ) * x509.CertPool {
55+ rootCAs := x509 .NewCertPool ()
56+ rootCAs .AddCert (serverCert )
4257 return rootCAs
4358}
4459
4560func TestRegistries (t * testing.T ) {
4661 registries := map [string ]newRegistryFunc {
47- "containerd" : func (t * testing.T , cafile string ) (image.Registry , cleanupFunc ) {
62+ "containersimage" : func (t * testing.T , serverCert * x509.Certificate ) (image.Registry , cleanupFunc ) {
63+ caDir := caDirForCert (t , serverCert )
64+ sourceCtx := & types.SystemContext {
65+ OCICertPath : caDir ,
66+ DockerCertPath : caDir ,
67+ DockerPerHostCertDirPath : caDir ,
68+ }
69+ r , err := containersimageregistry .New (sourceCtx , containersimageregistry .ForceTemporaryImageCache ())
70+ require .NoError (t , err )
71+ cleanup := func () {
72+ require .NoError (t , os .RemoveAll (caDir ))
73+ require .NoError (t , r .Destroy ())
74+ }
75+ return r , cleanup
76+ },
77+ "containerd" : func (t * testing.T , serverCert * x509.Certificate ) (image.Registry , cleanupFunc ) {
4878 val , err := rand .Int (rand .Reader , big .NewInt (math .MaxInt64 ))
4979 require .NoError (t , err )
5080 r , err := containerdregistry .NewRegistry (
5181 containerdregistry .WithLog (logrus .New ().WithField ("test" , t .Name ())),
5282 containerdregistry .WithCacheDir (fmt .Sprintf ("cache-%x" , val )),
53- containerdregistry .WithRootCAs (poolForCertFile ( t , cafile )),
83+ containerdregistry .WithRootCAs (poolForCert ( serverCert )),
5484 )
5585 require .NoError (t , err )
5686 cleanup := func () {
@@ -59,37 +89,25 @@ func TestRegistries(t *testing.T) {
5989
6090 return r , cleanup
6191 },
62- // TODO: enable docker tests - currently blocked on a cross-platform way to configure either insecure registries
63- // or CA certs
64- //"docker": func(t *testing.T, cafile string) (image.Registry, cleanupFunc) {
65- // r, err := execregistry.NewRegistry(containertools.DockerTool,
66- // logrus.New().WithField("test", t.Name()),
67- // cafile,
68- // )
69- // require.NoError(t, err)
70- // cleanup := func() {
71- // require.NoError(t, r.Destroy())
72- // }
73- //
74- // return r, cleanup
75- //},
76- // TODO: Enable buildah tests
77- // func(t *testing.T) image.Registry {
78- // r, err := buildahregistry.NewRegistry(
79- // buildahregistry.WithLog(logrus.New().WithField("test", t.Name())),
80- // buildahregistry.WithCacheDir(fmt.Sprintf("cache-%x", rand.Int())),
81- // )
82- // require.NoError(t, err)
83-
84- // return r
85- // },
8692 }
8793
8894 for name , registry := range registries {
8995 testPullAndUnpack (t , name , registry )
9096 }
9197}
9298
99+ type httpError struct {
100+ statusCode int
101+ error error
102+ }
103+
104+ func (e * httpError ) Error () string {
105+ if e .error != nil {
106+ return e .error .Error ()
107+ }
108+ return http .StatusText (e .statusCode )
109+ }
110+
93111func testPullAndUnpack (t * testing.T , name string , newRegistry newRegistryFunc ) {
94112 type args struct {
95113 dockerRootDir string
@@ -100,7 +118,18 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
100118 type expected struct {
101119 checksum string
102120 pullAssertion require.ErrorAssertionFunc
121+ labels map [string ]string
103122 }
123+
124+ expectedLabels := map [string ]string {
125+ "operators.operatorframework.io.bundle.mediatype.v1" : "registry+v1" ,
126+ "operators.operatorframework.io.bundle.manifests.v1" : "manifests/" ,
127+ "operators.operatorframework.io.bundle.metadata.v1" : "metadata/" ,
128+ "operators.operatorframework.io.bundle.package.v1" : "kiali" ,
129+ "operators.operatorframework.io.bundle.channels.v1" : "stable,alpha" ,
130+ "operators.operatorframework.io.bundle.channel.default.v1" : "stable" ,
131+ }
132+
104133 tests := []struct {
105134 description string
106135 args args
@@ -114,6 +143,7 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
114143 },
115144 expected : expected {
116145 checksum : dirChecksum (t , "testdata/golden/bundles/kiali" ),
146+ labels : expectedLabels ,
117147 pullAssertion : require .NoError ,
118148 },
119149 },
@@ -125,6 +155,7 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
125155 },
126156 expected : expected {
127157 checksum : dirChecksum (t , "testdata/golden/bundles/kiali" ),
158+ labels : expectedLabels ,
128159 pullAssertion : require .NoError ,
129160 },
130161 },
@@ -134,10 +165,11 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
134165 dockerRootDir : "testdata/golden" ,
135166 img : "/olmtest/kiali:1.4.2" ,
136167 pullErrCount : 1 ,
137- pullErr : errors . New ( "dummy" ) ,
168+ pullErr : & httpError { statusCode : http . StatusTooManyRequests } ,
138169 },
139170 expected : expected {
140171 checksum : dirChecksum (t , "testdata/golden/bundles/kiali" ),
172+ labels : expectedLabels ,
141173 pullAssertion : require .NoError ,
142174 },
143175 },
@@ -158,7 +190,7 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
158190 dockerRootDir : "testdata/golden" ,
159191 img : "/olmtest/kiali:1.4.2" ,
160192 pullErrCount : math .MaxInt64 ,
161- pullErr : errors . New ( "dummy" ) ,
193+ pullErr : & httpError { statusCode : http . StatusTooManyRequests } ,
162194 },
163195 expected : expected {
164196 pullAssertion : require .Error ,
@@ -168,51 +200,43 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
168200 for _ , tt := range tests {
169201 t .Run (tt .description , func (t * testing.T ) {
170202 logrus .SetLevel (logrus .DebugLevel )
171- ctx , close := context .WithCancel (context .Background ())
172- defer close ()
173-
174- configOpts := []libimage.ConfigOpt {}
203+ ctx , cancel := context .WithCancel (context .Background ())
204+ defer cancel ()
175205
206+ var middlewares []func (next http.Handler ) http.Handler
176207 if tt .args .pullErrCount > 0 {
177- configOpts = append (configOpts , func (config * configuration.Configuration ) {
178- if config .Middleware == nil {
179- config .Middleware = make (map [string ][]configuration.Middleware )
180- }
181-
182- mockRepo := & mockRepo {blobStore : & mockBlobStore {
183- maxCount : tt .args .pullErrCount ,
184- err : tt .args .pullErr ,
185- }}
186- val , err := rand .Int (rand .Reader , big .NewInt (math .MaxInt64 ))
187- require .NoError (t , err )
188-
189- middlewareName := fmt .Sprintf ("test-%x" , val )
190- require .NoError (t , repositorymiddleware .Register (middlewareName , mockRepo .init ))
191- config .Middleware ["repository" ] = append (config .Middleware ["repository" ], configuration.Middleware {
192- Name : middlewareName ,
193- })
194- })
208+ middlewares = append (middlewares , failureMiddleware (tt .args .pullErrCount , tt .args .pullErr ))
195209 }
196210
197- host , cafile , err := libimage .RunDockerRegistry (ctx , tt .args .dockerRootDir , configOpts ... )
198- require . NoError ( t , err )
211+ dockerServer := libimage .RunDockerRegistry (ctx , tt .args .dockerRootDir , middlewares ... )
212+ defer dockerServer . Close ( )
199213
200- r , cleanup := newRegistry (t , cafile )
214+ r , cleanup := newRegistry (t , dockerServer . Certificate () )
201215 defer cleanup ()
202216
203- ref := image . SimpleReference ( host + tt . args . img )
204- tt . expected . pullAssertion (t , r . Pull ( ctx , ref ) )
217+ url , err := url . Parse ( dockerServer . URL )
218+ require . NoError (t , err )
205219
206- if tt .expected .checksum != "" {
207- // Copy golden manifests to a temp dir
208- dir := "kiali-unpacked"
209- require .NoError (t , r .Unpack (ctx , ref , dir ))
220+ ref := image .SimpleReference (fmt .Sprintf ("%s%s" , url .Host , tt .args .img ))
221+ t .Log ("pulling image" , ref )
222+ pullErr := r .Pull (ctx , ref )
223+ tt .expected .pullAssertion (t , pullErr )
224+ if pullErr != nil {
225+ return
226+ }
210227
211- checksum := dirChecksum (t , dir )
212- require .Equal (t , tt .expected .checksum , checksum )
228+ labels , err := r .Labels (ctx , ref )
229+ require .NoError (t , err )
230+ require .Equal (t , tt .expected .labels , labels )
213231
214- require .NoError (t , os .RemoveAll (dir ))
215- }
232+ // Copy golden manifests to a temp dir
233+ dir := "kiali-unpacked"
234+ require .NoError (t , r .Unpack (ctx , ref , dir ))
235+
236+ checksum := dirChecksum (t , dir )
237+ require .Equal (t , tt .expected .checksum , checksum )
238+
239+ require .NoError (t , os .RemoveAll (dir ))
216240 })
217241 }
218242}
@@ -303,3 +327,24 @@ func (f *mockBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter, r
303327func (f * mockBlobStore ) Delete (ctx context.Context , dgst digest.Digest ) error {
304328 return f .base .Delete (ctx , dgst )
305329}
330+
331+ func failureMiddleware (totalCount int , err error ) func (next http.Handler ) http.Handler {
332+ return func (next http.Handler ) http.Handler {
333+ count := 0
334+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
335+ if count >= totalCount {
336+ next .ServeHTTP (w , r )
337+ return
338+ }
339+ count ++
340+ statusCode := http .StatusInternalServerError
341+
342+ var httpErr * httpError
343+ if errors .As (err , & httpErr ) {
344+ statusCode = httpErr .statusCode
345+ }
346+
347+ http .Error (w , err .Error (), statusCode )
348+ })
349+ }
350+ }
0 commit comments