@@ -4,19 +4,21 @@ import (
4
4
"context"
5
5
"crypto/rand"
6
6
"crypto/x509"
7
+ "encoding/pem"
7
8
"errors"
8
9
"fmt"
9
10
"io"
10
11
"math"
11
12
"math/big"
12
13
"net/http"
14
+ "net/url"
13
15
"os"
16
+ "path/filepath"
14
17
"sync"
15
18
"testing"
16
19
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"
20
22
"github.com/distribution/reference"
21
23
"github.com/opencontainers/go-digest"
22
24
"github.com/sirupsen/logrus"
@@ -25,32 +27,60 @@ import (
25
27
26
28
"github.com/operator-framework/operator-registry/pkg/image"
27
29
"github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
30
+ "github.com/operator-framework/operator-registry/pkg/image/containersimageregistry"
28
31
libimage "github.com/operator-framework/operator-registry/pkg/lib/image"
29
32
)
30
33
31
34
// cleanupFunc is a function that cleans up after some test infra.
32
35
type cleanupFunc func ()
33
36
34
37
// 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 )
36
39
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" ))
40
44
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 )
42
57
return rootCAs
43
58
}
44
59
45
60
func TestRegistries (t * testing.T ) {
46
61
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 ) {
48
78
val , err := rand .Int (rand .Reader , big .NewInt (math .MaxInt64 ))
49
79
require .NoError (t , err )
50
80
r , err := containerdregistry .NewRegistry (
51
81
containerdregistry .WithLog (logrus .New ().WithField ("test" , t .Name ())),
52
82
containerdregistry .WithCacheDir (fmt .Sprintf ("cache-%x" , val )),
53
- containerdregistry .WithRootCAs (poolForCertFile ( t , cafile )),
83
+ containerdregistry .WithRootCAs (poolForCert ( serverCert )),
54
84
)
55
85
require .NoError (t , err )
56
86
cleanup := func () {
@@ -59,37 +89,25 @@ func TestRegistries(t *testing.T) {
59
89
60
90
return r , cleanup
61
91
},
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
- // },
86
92
}
87
93
88
94
for name , registry := range registries {
89
95
testPullAndUnpack (t , name , registry )
90
96
}
91
97
}
92
98
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
+
93
111
func testPullAndUnpack (t * testing.T , name string , newRegistry newRegistryFunc ) {
94
112
type args struct {
95
113
dockerRootDir string
@@ -100,7 +118,18 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
100
118
type expected struct {
101
119
checksum string
102
120
pullAssertion require.ErrorAssertionFunc
121
+ labels map [string ]string
103
122
}
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
+
104
133
tests := []struct {
105
134
description string
106
135
args args
@@ -114,6 +143,7 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
114
143
},
115
144
expected : expected {
116
145
checksum : dirChecksum (t , "testdata/golden/bundles/kiali" ),
146
+ labels : expectedLabels ,
117
147
pullAssertion : require .NoError ,
118
148
},
119
149
},
@@ -125,6 +155,7 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
125
155
},
126
156
expected : expected {
127
157
checksum : dirChecksum (t , "testdata/golden/bundles/kiali" ),
158
+ labels : expectedLabels ,
128
159
pullAssertion : require .NoError ,
129
160
},
130
161
},
@@ -134,10 +165,11 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
134
165
dockerRootDir : "testdata/golden" ,
135
166
img : "/olmtest/kiali:1.4.2" ,
136
167
pullErrCount : 1 ,
137
- pullErr : errors . New ( "dummy" ) ,
168
+ pullErr : & httpError { statusCode : http . StatusTooManyRequests } ,
138
169
},
139
170
expected : expected {
140
171
checksum : dirChecksum (t , "testdata/golden/bundles/kiali" ),
172
+ labels : expectedLabels ,
141
173
pullAssertion : require .NoError ,
142
174
},
143
175
},
@@ -158,7 +190,7 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
158
190
dockerRootDir : "testdata/golden" ,
159
191
img : "/olmtest/kiali:1.4.2" ,
160
192
pullErrCount : math .MaxInt64 ,
161
- pullErr : errors . New ( "dummy" ) ,
193
+ pullErr : & httpError { statusCode : http . StatusTooManyRequests } ,
162
194
},
163
195
expected : expected {
164
196
pullAssertion : require .Error ,
@@ -168,51 +200,43 @@ func testPullAndUnpack(t *testing.T, name string, newRegistry newRegistryFunc) {
168
200
for _ , tt := range tests {
169
201
t .Run (tt .description , func (t * testing.T ) {
170
202
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 ()
175
205
206
+ var middlewares []func (next http.Handler ) http.Handler
176
207
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 ))
195
209
}
196
210
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 ( )
199
213
200
- r , cleanup := newRegistry (t , cafile )
214
+ r , cleanup := newRegistry (t , dockerServer . Certificate () )
201
215
defer cleanup ()
202
216
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 )
205
219
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
+ }
210
227
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 )
213
231
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 ))
216
240
})
217
241
}
218
242
}
@@ -303,3 +327,24 @@ func (f *mockBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter, r
303
327
func (f * mockBlobStore ) Delete (ctx context.Context , dgst digest.Digest ) error {
304
328
return f .base .Delete (ctx , dgst )
305
329
}
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