github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/pack_test.go (about) 1 /* 2 Copyright The ORAS Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package oras 17 18 import ( 19 "bytes" 20 "context" 21 "encoding/json" 22 "errors" 23 "io" 24 "reflect" 25 "testing" 26 "time" 27 28 "github.com/opcr-io/oras-go/v2/content" 29 "github.com/opcr-io/oras-go/v2/content/memory" 30 "github.com/opencontainers/go-digest" 31 specs "github.com/opencontainers/image-spec/specs-go" 32 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 33 ) 34 35 func Test_Pack_Default(t *testing.T) { 36 s := memory.New() 37 38 // prepare test content 39 blobs := []ocispec.Descriptor{ 40 content.NewDescriptorFromBytes("test", []byte("hello world")), 41 content.NewDescriptorFromBytes("test", []byte("goodbye world")), 42 } 43 artifactType := "application/vnd.test" 44 45 // test Pack 46 ctx := context.Background() 47 manifestDesc, err := Pack(ctx, s, artifactType, blobs, PackOptions{}) 48 if err != nil { 49 t.Fatal("Oras.Pack() error =", err) 50 } 51 52 // test blobs 53 var manifest ocispec.Artifact 54 rc, err := s.Fetch(ctx, manifestDesc) 55 if err != nil { 56 t.Fatal("Store.Fetch() error =", err) 57 } 58 if err := json.NewDecoder(rc).Decode(&manifest); err != nil { 59 t.Fatal("error decoding manifest, error =", err) 60 } 61 if err := rc.Close(); err != nil { 62 t.Fatal("Store.Fetch().Close() error =", err) 63 } 64 if !reflect.DeepEqual(manifest.Blobs, blobs) { 65 t.Errorf("Store.Fetch() = %v, want %v", manifest.Blobs, blobs) 66 } 67 68 // test media type 69 if got := manifest.MediaType; got != ocispec.MediaTypeArtifactManifest { 70 t.Fatalf("got media type = %s, want %s", got, ocispec.MediaTypeArtifactManifest) 71 } 72 73 // test artifact type 74 if got := manifest.ArtifactType; got != artifactType { 75 t.Fatalf("got artifact type = %s, want %s", got, artifactType) 76 } 77 78 // test created time annotation 79 createdTime, ok := manifest.Annotations[ocispec.AnnotationArtifactCreated] 80 if !ok { 81 t.Errorf("Annotation %s = %v, want %v", ocispec.AnnotationArtifactCreated, ok, true) 82 } 83 _, err = time.Parse(time.RFC3339, createdTime) 84 if err != nil { 85 t.Errorf("error parsing created time: %s, error = %v", createdTime, err) 86 } 87 } 88 89 func Test_Pack_WithOptions(t *testing.T) { 90 s := memory.New() 91 92 // prepare test content 93 blobs := []ocispec.Descriptor{ 94 content.NewDescriptorFromBytes("test", []byte("hello world")), 95 content.NewDescriptorFromBytes("test", []byte("goodbye world")), 96 } 97 98 artifactType := "application/vnd.test" 99 annotations := map[string]string{ 100 ocispec.AnnotationArtifactCreated: "2000-01-01T00:00:00Z", 101 } 102 subjectManifest := []byte(`{"layers":[]}`) 103 subjectDesc := ocispec.Descriptor{ 104 MediaType: ocispec.MediaTypeImageManifest, 105 Digest: digest.FromBytes(subjectManifest), 106 Size: int64(len(subjectManifest)), 107 ArtifactType: artifactType, 108 Annotations: annotations, 109 } 110 111 // test Pack 112 ctx := context.Background() 113 opts := PackOptions{ 114 Subject: &subjectDesc, 115 ManifestAnnotations: annotations, 116 } 117 manifestDesc, err := Pack(ctx, s, artifactType, blobs, opts) 118 if err != nil { 119 t.Fatal("Oras.Pack() error =", err) 120 } 121 122 expectedManifest := ocispec.Artifact{ 123 MediaType: ocispec.MediaTypeArtifactManifest, 124 ArtifactType: artifactType, 125 Blobs: blobs, 126 Subject: opts.Subject, 127 Annotations: annotations, 128 } 129 expectedManifestBytes, err := json.Marshal(expectedManifest) 130 if err != nil { 131 t.Fatal("failed to marshal manifest:", err) 132 } 133 134 // test manifest 135 rc, err := s.Fetch(ctx, manifestDesc) 136 if err != nil { 137 t.Fatal("Store.Fetch() error =", err) 138 } 139 got, err := io.ReadAll(rc) 140 if err != nil { 141 t.Fatal("Store.Fetch().Read() error =", err) 142 } 143 err = rc.Close() 144 if err != nil { 145 t.Error("Store.Fetch().Close() error =", err) 146 } 147 if !bytes.Equal(got, expectedManifestBytes) { 148 t.Errorf("Store.Fetch() = %v, want %v", got, expectedManifestBytes) 149 } 150 } 151 152 func Test_Pack_NoBlob(t *testing.T) { 153 s := memory.New() 154 155 // test Pack 156 ctx := context.Background() 157 artifactType := "application/vnd.test" 158 manifestDesc, err := Pack(ctx, s, artifactType, nil, PackOptions{}) 159 if err != nil { 160 t.Fatal("Oras.Pack() error =", err) 161 } 162 163 var manifest ocispec.Artifact 164 rc, err := s.Fetch(ctx, manifestDesc) 165 if err != nil { 166 t.Fatal("Store.Fetch() error =", err) 167 } 168 if err := json.NewDecoder(rc).Decode(&manifest); err != nil { 169 t.Fatal("error decoding manifest, error =", err) 170 } 171 if err := rc.Close(); err != nil { 172 t.Fatal("Store.Fetch().Close() error =", err) 173 } 174 175 // test blobs 176 var expectedBlobs []ocispec.Descriptor 177 if !reflect.DeepEqual(manifest.Blobs, expectedBlobs) { 178 t.Errorf("Store.Fetch() = %v, want %v", manifest.Blobs, expectedBlobs) 179 } 180 } 181 182 func Test_Pack_NoArtifactType(t *testing.T) { 183 s := memory.New() 184 185 ctx := context.Background() 186 manifestDesc, err := Pack(ctx, s, "", nil, PackOptions{}) 187 if err != nil { 188 t.Fatal("Oras.Pack() error =", err) 189 } 190 191 var manifest ocispec.Artifact 192 rc, err := s.Fetch(ctx, manifestDesc) 193 if err != nil { 194 t.Fatal("Store.Fetch() error =", err) 195 } 196 if err := json.NewDecoder(rc).Decode(&manifest); err != nil { 197 t.Fatal("error decoding manifest, error =", err) 198 } 199 if err := rc.Close(); err != nil { 200 t.Fatal("Store.Fetch().Close() error =", err) 201 } 202 203 // test artifact type 204 if manifestDesc.ArtifactType != MediaTypeUnknownArtifact { 205 t.Fatalf("got artifact type = %s, want %s", manifestDesc.ArtifactType, MediaTypeUnknownArtifact) 206 } 207 if manifest.ArtifactType != MediaTypeUnknownArtifact { 208 t.Fatalf("got artifact type = %s, want %s", manifest.ArtifactType, MediaTypeUnknownArtifact) 209 } 210 } 211 212 func Test_Pack_InvalidDateTimeFormat(t *testing.T) { 213 s := memory.New() 214 215 ctx := context.Background() 216 opts := PackOptions{ 217 ManifestAnnotations: map[string]string{ 218 ocispec.AnnotationArtifactCreated: "2000/01/01 00:00:00", 219 }, 220 } 221 artifactType := "application/vnd.test" 222 _, err := Pack(ctx, s, artifactType, nil, opts) 223 if err == nil || !errors.Is(err, ErrInvalidDateTimeFormat) { 224 t.Errorf("Oras.Pack() error = %v, wantErr = %v", err, ErrInvalidDateTimeFormat) 225 } 226 } 227 228 func Test_Pack_Image(t *testing.T) { 229 s := memory.New() 230 231 // prepare test content 232 layers := []ocispec.Descriptor{ 233 content.NewDescriptorFromBytes("test", []byte("hello world")), 234 content.NewDescriptorFromBytes("test", []byte("goodbye world")), 235 } 236 237 // test Pack 238 ctx := context.Background() 239 artifactType := "testconfig" 240 manifestDesc, err := Pack(ctx, s, artifactType, layers, PackOptions{PackImageManifest: true}) 241 if err != nil { 242 t.Fatal("Oras.Pack() error =", err) 243 } 244 245 var manifest ocispec.Manifest 246 rc, err := s.Fetch(ctx, manifestDesc) 247 if err != nil { 248 t.Fatal("Store.Fetch() error =", err) 249 } 250 if err := json.NewDecoder(rc).Decode(&manifest); err != nil { 251 t.Fatal("error decoding manifest, error =", err) 252 } 253 if err := rc.Close(); err != nil { 254 t.Fatal("Store.Fetch().Close() error =", err) 255 } 256 257 // test media type 258 got := manifest.MediaType 259 if got != ocispec.MediaTypeImageManifest { 260 t.Fatalf("got media type = %s, want %s", got, ocispec.MediaTypeImageManifest) 261 } 262 263 // test config 264 expectedConfigBytes := []byte("{}") 265 expectedConfig := ocispec.Descriptor{ 266 MediaType: artifactType, 267 Digest: digest.FromBytes(expectedConfigBytes), 268 Size: int64(len(expectedConfigBytes)), 269 } 270 if !reflect.DeepEqual(manifest.Config, expectedConfig) { 271 t.Errorf("got config = %v, want %v", manifest.Config, expectedConfig) 272 } 273 274 // test layers 275 if !reflect.DeepEqual(manifest.Layers, layers) { 276 t.Errorf("got layers = %v, want %v", manifest.Layers, layers) 277 } 278 279 // test created time annotation 280 createdTime, ok := manifest.Annotations[ocispec.AnnotationCreated] 281 if !ok { 282 t.Errorf("Annotation %s = %v, want %v", ocispec.AnnotationCreated, ok, true) 283 } 284 _, err = time.Parse(time.RFC3339, createdTime) 285 if err != nil { 286 t.Errorf("error parsing created time: %s, error = %v", createdTime, err) 287 } 288 } 289 290 func Test_Pack_Image_WithOptions(t *testing.T) { 291 s := memory.New() 292 293 // prepare test content 294 layers := []ocispec.Descriptor{ 295 content.NewDescriptorFromBytes("test", []byte("hello world")), 296 content.NewDescriptorFromBytes("test", []byte("goodbye world")), 297 } 298 configBytes := []byte("{}") 299 configDesc := content.NewDescriptorFromBytes("testconfig", configBytes) 300 configAnnotations := map[string]string{"foo": "bar"} 301 annotations := map[string]string{ 302 ocispec.AnnotationCreated: "2000-01-01T00:00:00Z", 303 } 304 artifactType := "application/vnd.test" 305 subjectManifest := []byte(`{"layers":[]}`) 306 subjectDesc := ocispec.Descriptor{ 307 MediaType: ocispec.MediaTypeImageManifest, 308 Digest: digest.FromBytes(subjectManifest), 309 Size: int64(len(subjectManifest)), 310 } 311 312 // test Pack with ConfigDescriptor 313 ctx := context.Background() 314 opts := PackOptions{ 315 PackImageManifest: true, 316 Subject: &subjectDesc, 317 ConfigDescriptor: &configDesc, 318 ConfigAnnotations: configAnnotations, 319 ManifestAnnotations: annotations, 320 } 321 manifestDesc, err := Pack(ctx, s, artifactType, layers, opts) 322 if err != nil { 323 t.Fatal("Oras.Pack() error =", err) 324 } 325 326 expectedManifest := ocispec.Manifest{ 327 Versioned: specs.Versioned{ 328 SchemaVersion: 2, // historical value. does not pertain to OCI or docker version 329 }, 330 MediaType: ocispec.MediaTypeImageManifest, 331 Subject: &subjectDesc, 332 Config: configDesc, 333 Layers: layers, 334 Annotations: annotations, 335 } 336 expectedManifestBytes, err := json.Marshal(expectedManifest) 337 if err != nil { 338 t.Fatal("failed to marshal manifest:", err) 339 } 340 341 rc, err := s.Fetch(ctx, manifestDesc) 342 if err != nil { 343 t.Fatal("Store.Fetch() error =", err) 344 } 345 got, err := io.ReadAll(rc) 346 if err != nil { 347 t.Fatal("Store.Fetch().Read() error =", err) 348 } 349 err = rc.Close() 350 if err != nil { 351 t.Error("Store.Fetch().Close() error =", err) 352 } 353 if !bytes.Equal(got, expectedManifestBytes) { 354 t.Errorf("Store.Fetch() = %v, want %v", string(got), string(expectedManifestBytes)) 355 } 356 357 // test Pack without ConfigDescriptor 358 opts = PackOptions{ 359 PackImageManifest: true, 360 Subject: &subjectDesc, 361 ConfigAnnotations: configAnnotations, 362 ManifestAnnotations: annotations, 363 } 364 manifestDesc, err = Pack(ctx, s, artifactType, layers, opts) 365 if err != nil { 366 t.Fatal("Oras.Pack() error =", err) 367 } 368 369 expectedConfigDesc := content.NewDescriptorFromBytes(artifactType, configBytes) 370 expectedConfigDesc.Annotations = configAnnotations 371 expectedManifest = ocispec.Manifest{ 372 Versioned: specs.Versioned{ 373 SchemaVersion: 2, // historical value. does not pertain to OCI or docker version 374 }, 375 MediaType: ocispec.MediaTypeImageManifest, 376 Subject: &subjectDesc, 377 Config: expectedConfigDesc, 378 Layers: layers, 379 Annotations: annotations, 380 } 381 expectedManifestBytes, err = json.Marshal(expectedManifest) 382 if err != nil { 383 t.Fatal("failed to marshal manifest:", err) 384 } 385 386 rc, err = s.Fetch(ctx, manifestDesc) 387 if err != nil { 388 t.Fatal("Store.Fetch() error =", err) 389 } 390 got, err = io.ReadAll(rc) 391 if err != nil { 392 t.Fatal("Store.Fetch().Read() error =", err) 393 } 394 err = rc.Close() 395 if err != nil { 396 t.Error("Store.Fetch().Close() error =", err) 397 } 398 if !bytes.Equal(got, expectedManifestBytes) { 399 t.Errorf("Store.Fetch() = %v, want %v", string(got), string(expectedManifestBytes)) 400 } 401 } 402 403 func Test_Pack_Image_NoArtifactType(t *testing.T) { 404 s := memory.New() 405 406 ctx := context.Background() 407 manifestDesc, err := Pack(ctx, s, "", nil, PackOptions{PackImageManifest: true}) 408 if err != nil { 409 t.Fatal("Oras.Pack() error =", err) 410 } 411 412 var manifest ocispec.Manifest 413 rc, err := s.Fetch(ctx, manifestDesc) 414 if err != nil { 415 t.Fatal("Store.Fetch() error =", err) 416 } 417 if err := json.NewDecoder(rc).Decode(&manifest); err != nil { 418 t.Fatal("error decoding manifest, error =", err) 419 } 420 if err := rc.Close(); err != nil { 421 t.Fatal("Store.Fetch().Close() error =", err) 422 } 423 424 // test artifact type and config media type 425 if manifestDesc.ArtifactType != MediaTypeUnknownConfig { 426 t.Fatalf("got artifact type = %s, want %s", manifestDesc.ArtifactType, MediaTypeUnknownConfig) 427 } 428 if manifest.Config.MediaType != MediaTypeUnknownConfig { 429 t.Fatalf("got artifact type = %s, want %s", manifest.Config.MediaType, MediaTypeUnknownConfig) 430 } 431 } 432 433 func Test_Pack_Image_NoLayer(t *testing.T) { 434 s := memory.New() 435 436 // test Pack 437 ctx := context.Background() 438 manifestDesc, err := Pack(ctx, s, "", nil, PackOptions{PackImageManifest: true}) 439 if err != nil { 440 t.Fatal("Oras.Pack() error =", err) 441 } 442 443 var manifest ocispec.Manifest 444 rc, err := s.Fetch(ctx, manifestDesc) 445 if err != nil { 446 t.Fatal("Store.Fetch() error =", err) 447 } 448 if err := json.NewDecoder(rc).Decode(&manifest); err != nil { 449 t.Fatal("error decoding manifest, error =", err) 450 } 451 if err := rc.Close(); err != nil { 452 t.Fatal("Store.Fetch().Close() error =", err) 453 } 454 455 // test layers 456 expectedLayers := []ocispec.Descriptor{} 457 if !reflect.DeepEqual(manifest.Layers, expectedLayers) { 458 t.Errorf("got layers = %v, want %v", manifest.Layers, expectedLayers) 459 } 460 } 461 462 func Test_Pack_Image_InvalidDateTimeFormat(t *testing.T) { 463 s := memory.New() 464 465 ctx := context.Background() 466 opts := PackOptions{ 467 PackImageManifest: true, 468 ManifestAnnotations: map[string]string{ 469 ocispec.AnnotationCreated: "2000/01/01 00:00:00", 470 }, 471 } 472 _, err := Pack(ctx, s, "", nil, opts) 473 if err == nil || !errors.Is(err, ErrInvalidDateTimeFormat) { 474 t.Errorf("Oras.Pack() error = %v, wantErr = %v", err, ErrInvalidDateTimeFormat) 475 } 476 }