github.com/werf/oras@v0.4.0/pkg/oras/oras_test.go (about) 1 package oras 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "testing" 10 "time" 11 12 orascontent "github.com/deislabs/oras/pkg/content" 13 14 "github.com/containerd/containerd/images" 15 "github.com/containerd/containerd/remotes" 16 "github.com/containerd/containerd/remotes/docker" 17 "github.com/docker/distribution/configuration" 18 "github.com/docker/distribution/registry" 19 _ "github.com/docker/distribution/registry/storage/driver/inmemory" 20 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 21 "github.com/phayes/freeport" 22 "github.com/stretchr/testify/suite" 23 ) 24 25 var ( 26 testTarball = "../../testdata/charts/chartmuseum-1.8.2.tgz" 27 testDir = "../../testdata/charts/chartmuseum" 28 testDirFiles = []string{ 29 "Chart.yaml", 30 "values.yaml", 31 "README.md", 32 "templates/_helpers.tpl", 33 "templates/NOTES.txt", 34 "templates/service.yaml", 35 ".helmignore", 36 } 37 ) 38 39 type ORASTestSuite struct { 40 suite.Suite 41 DockerRegistryHost string 42 } 43 44 func newContext() context.Context { 45 return context.Background() 46 } 47 48 func newResolver() remotes.Resolver { 49 return docker.NewResolver(docker.ResolverOptions{}) 50 } 51 52 // Start Docker registry 53 func (suite *ORASTestSuite) SetupSuite() { 54 config := &configuration.Configuration{} 55 port, err := freeport.GetFreePort() 56 if err != nil { 57 suite.Nil(err, "no error finding free port for test registry") 58 } 59 suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port) 60 config.HTTP.Addr = fmt.Sprintf(":%d", port) 61 config.HTTP.DrainTimeout = time.Duration(10) * time.Second 62 config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} 63 dockerRegistry, err := registry.NewRegistry(context.Background(), config) 64 suite.Nil(err, "no error finding free port for test registry") 65 66 go dockerRegistry.ListenAndServe() 67 } 68 69 // Push files to docker registry 70 func (suite *ORASTestSuite) Test_0_Push() { 71 var ( 72 err error 73 ref string 74 desc ocispec.Descriptor 75 descriptors []ocispec.Descriptor 76 store *orascontent.FileStore 77 ) 78 79 _, err = Push(newContext(), nil, ref, nil, descriptors) 80 suite.NotNil(err, "error pushing with empty resolver") 81 82 _, err = Push(newContext(), newResolver(), ref, nil, descriptors) 83 suite.NotNil(err, "error pushing when context missing hostname") 84 85 ref = fmt.Sprintf("%s/empty:test", suite.DockerRegistryHost) 86 _, err = Push(newContext(), newResolver(), ref, nil, descriptors) 87 suite.NotNil(ErrEmptyDescriptors, err, "error pushing with empty descriptors") 88 89 // Load descriptors with test chart tgz (as single layer) 90 store = orascontent.NewFileStore("") 91 basename := filepath.Base(testTarball) 92 desc, err = store.Add(basename, "", testTarball) 93 suite.Nil(err, "no error loading test chart") 94 descriptors = []ocispec.Descriptor{desc} 95 96 ref = fmt.Sprintf("%s/chart-tgz:test", suite.DockerRegistryHost) 97 _, err = Push(newContext(), newResolver(), ref, store, descriptors) 98 suite.Nil(err, "no error pushing test chart tgz (as single layer)") 99 100 // Load descriptors with test chart dir (each file as layer) 101 testDirAbs, err := filepath.Abs(testDir) 102 suite.Nil(err, "no error parsing test directory") 103 store = orascontent.NewFileStore(testDirAbs) 104 descriptors = []ocispec.Descriptor{} 105 var ff = func(pathX string, infoX os.FileInfo, errX error) error { 106 if !infoX.IsDir() { 107 filename := filepath.Join(filepath.Dir(pathX), infoX.Name()) 108 name := filepath.ToSlash(filename) 109 desc, err = store.Add(name, "", filename) 110 if err != nil { 111 return err 112 } 113 descriptors = append(descriptors, desc) 114 } 115 return nil 116 } 117 118 cwd, _ := os.Getwd() 119 os.Chdir(testDir) 120 filepath.Walk(".", ff) 121 os.Chdir(cwd) 122 123 ref = fmt.Sprintf("%s/chart-dir:test", suite.DockerRegistryHost) 124 _, err = Push(newContext(), newResolver(), ref, store, descriptors) 125 suite.Nil(err, "no error pushing test chart dir (each file as layer)") 126 } 127 128 // Pull files and verify descriptors 129 func (suite *ORASTestSuite) Test_1_Pull() { 130 var ( 131 err error 132 ref string 133 descriptors []ocispec.Descriptor 134 store *orascontent.Memorystore 135 ) 136 137 _, descriptors, err = Pull(newContext(), nil, ref, nil) 138 suite.NotNil(err, "error pulling with empty resolver") 139 suite.Nil(descriptors, "descriptors nil pulling with empty resolver") 140 141 // Pull non-existant 142 store = orascontent.NewMemoryStore() 143 ref = fmt.Sprintf("%s/nonexistant:test", suite.DockerRegistryHost) 144 _, descriptors, err = Pull(newContext(), newResolver(), ref, store) 145 suite.NotNil(err, "error pulling non-existant ref") 146 suite.Nil(descriptors, "descriptors empty with error") 147 148 // Pull chart-tgz 149 store = orascontent.NewMemoryStore() 150 ref = fmt.Sprintf("%s/chart-tgz:test", suite.DockerRegistryHost) 151 _, descriptors, err = Pull(newContext(), newResolver(), ref, store) 152 suite.Nil(err, "no error pulling chart-tgz ref") 153 154 // Verify the descriptors, single layer/file 155 content, err := ioutil.ReadFile(testTarball) 156 suite.Nil(err, "no error loading test chart") 157 name := filepath.Base(testTarball) 158 _, actualContent, ok := store.GetByName(name) 159 suite.True(ok, "find in memory") 160 suite.Equal(content, actualContent, ".tgz content matches on pull") 161 162 // Pull chart-dir 163 store = orascontent.NewMemoryStore() 164 ref = fmt.Sprintf("%s/chart-dir:test", suite.DockerRegistryHost) 165 _, descriptors, err = Pull(newContext(), newResolver(), ref, store) 166 suite.Nil(err, "no error pulling chart-dir ref") 167 168 // Verify the descriptors, multiple layers/files 169 cwd, _ := os.Getwd() 170 os.Chdir(testDir) 171 for _, filename := range testDirFiles { 172 content, err = ioutil.ReadFile(filename) 173 suite.Nil(err, fmt.Sprintf("no error loading %s", filename)) 174 _, actualContent, ok := store.GetByName(filename) 175 suite.True(ok, "find in memory") 176 suite.Equal(content, actualContent, fmt.Sprintf("%s content matches on pull", filename)) 177 } 178 os.Chdir(cwd) 179 } 180 181 // Push and pull with customized media types 182 func (suite *ORASTestSuite) Test_2_MediaType() { 183 var ( 184 testData = [][]string{ 185 {"hi.txt", "application/vnd.me.hi", "hi"}, 186 {"bye.txt", "application/vnd.me.bye", "bye"}, 187 } 188 err error 189 ref string 190 descriptors []ocispec.Descriptor 191 store *orascontent.Memorystore 192 ) 193 194 // Push content with customized media types 195 store = orascontent.NewMemoryStore() 196 descriptors = nil 197 for _, data := range testData { 198 desc := store.Add(data[0], data[1], []byte(data[2])) 199 descriptors = append(descriptors, desc) 200 } 201 ref = fmt.Sprintf("%s/media-type:test", suite.DockerRegistryHost) 202 _, err = Push(newContext(), newResolver(), ref, store, descriptors) 203 suite.Nil(err, "no error pushing test data with customized media type") 204 205 // Pull with all media types 206 store = orascontent.NewMemoryStore() 207 ref = fmt.Sprintf("%s/media-type:test", suite.DockerRegistryHost) 208 _, descriptors, err = Pull(newContext(), newResolver(), ref, store) 209 suite.Nil(err, "no error pulling media-type ref") 210 suite.Equal(2, len(descriptors), "number of contents matches on pull") 211 for _, data := range testData { 212 _, actualContent, ok := store.GetByName(data[0]) 213 suite.True(ok, "find in memory") 214 content := []byte(data[2]) 215 suite.Equal(content, actualContent, "test content matches on pull") 216 } 217 218 // Pull with specified media type 219 store = orascontent.NewMemoryStore() 220 ref = fmt.Sprintf("%s/media-type:test", suite.DockerRegistryHost) 221 _, descriptors, err = Pull(newContext(), newResolver(), ref, store, WithAllowedMediaType(testData[0][1])) 222 suite.Nil(err, "no error pulling media-type ref") 223 suite.Equal(1, len(descriptors), "number of contents matches on pull") 224 for _, data := range testData[:1] { 225 _, actualContent, ok := store.GetByName(data[0]) 226 suite.True(ok, "find in memory") 227 content := []byte(data[2]) 228 suite.Equal(content, actualContent, "test content matches on pull") 229 } 230 231 // Pull with non-existing media type 232 store = orascontent.NewMemoryStore() 233 ref = fmt.Sprintf("%s/media-type:test", suite.DockerRegistryHost) 234 _, descriptors, err = Pull(newContext(), newResolver(), ref, store, WithAllowedMediaType("non.existing.media.type")) 235 suite.Nil(err, "no error pulling media-type ref") 236 suite.Equal(0, len(descriptors), "number of contents matches on pull") 237 } 238 239 // Pull with condition 240 func (suite *ORASTestSuite) Test_3_Conditional_Pull() { 241 var ( 242 testData = [][]string{ 243 {"version.txt", "edge"}, 244 {"content.txt", "hello world"}, 245 } 246 err error 247 ref string 248 descriptors []ocispec.Descriptor 249 store *orascontent.Memorystore 250 stop bool 251 ) 252 253 // Push test content 254 store = orascontent.NewMemoryStore() 255 descriptors = nil 256 for _, data := range testData { 257 desc := store.Add(data[0], "", []byte(data[1])) 258 descriptors = append(descriptors, desc) 259 } 260 ref = fmt.Sprintf("%s/conditional-pull:test", suite.DockerRegistryHost) 261 _, err = Push(newContext(), newResolver(), ref, store, descriptors) 262 suite.Nil(err, "no error pushing test data") 263 264 // Pull all contents in sequence 265 store = orascontent.NewMemoryStore() 266 ref = fmt.Sprintf("%s/conditional-pull:test", suite.DockerRegistryHost) 267 _, descriptors, err = Pull(newContext(), newResolver(), ref, store, WithPullByBFS) 268 suite.Nil(err, "no error pulling ref") 269 suite.Equal(2, len(descriptors), "number of contents matches on pull") 270 for i, data := range testData { 271 _, actualContent, ok := store.GetByName(data[0]) 272 suite.True(ok, "find in memory") 273 content := []byte(data[1]) 274 suite.Equal(content, actualContent, "test content matches on pull") 275 name, _ := orascontent.ResolveName(descriptors[i]) 276 suite.Equal(data[0], name, "content sequence matches on pull") 277 } 278 279 // Selective pull contents: stop at the very beginning 280 store = orascontent.NewMemoryStore() 281 ref = fmt.Sprintf("%s/conditional-pull:test", suite.DockerRegistryHost) 282 _, descriptors, err = Pull(newContext(), newResolver(), ref, store, WithPullByBFS, 283 WithPullBaseHandler(images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { 284 if name, ok := orascontent.ResolveName(desc); ok && name == testData[0][0] { 285 return nil, ErrStopProcessing 286 } 287 return nil, nil 288 }))) 289 suite.Nil(err, "no error pulling ref") 290 suite.Equal(0, len(descriptors), "number of contents matches on pull") 291 292 // Selective pull contents: stop in the middle 293 store = orascontent.NewMemoryStore() 294 ref = fmt.Sprintf("%s/conditional-pull:test", suite.DockerRegistryHost) 295 stop = false 296 _, descriptors, err = Pull(newContext(), newResolver(), ref, store, WithPullByBFS, 297 WithPullBaseHandler(images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { 298 if stop { 299 return nil, ErrStopProcessing 300 } 301 if name, ok := orascontent.ResolveName(desc); ok && name == testData[0][0] { 302 stop = true 303 } 304 return nil, nil 305 }))) 306 suite.Nil(err, "no error pulling ref") 307 suite.Equal(1, len(descriptors), "number of contents matches on pull") 308 for _, data := range testData[:1] { 309 _, actualContent, ok := store.GetByName(data[0]) 310 suite.True(ok, "find in memory") 311 content := []byte(data[1]) 312 suite.Equal(content, actualContent, "test content matches on pull") 313 } 314 } 315 316 func TestORASTestSuite(t *testing.T) { 317 suite.Run(t, new(ORASTestSuite)) 318 }