github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/domain/infra/abi/images.go (about) 1 package abi 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/url" 9 "os" 10 "path" 11 "path/filepath" 12 "strconv" 13 "strings" 14 15 "github.com/containers/common/pkg/config" 16 "github.com/containers/image/v5/docker" 17 "github.com/containers/image/v5/docker/reference" 18 "github.com/containers/image/v5/manifest" 19 "github.com/containers/image/v5/signature" 20 "github.com/containers/image/v5/transports" 21 "github.com/containers/image/v5/transports/alltransports" 22 "github.com/containers/image/v5/types" 23 "github.com/containers/podman/v2/libpod/define" 24 "github.com/containers/podman/v2/libpod/image" 25 libpodImage "github.com/containers/podman/v2/libpod/image" 26 "github.com/containers/podman/v2/pkg/domain/entities" 27 domainUtils "github.com/containers/podman/v2/pkg/domain/utils" 28 "github.com/containers/podman/v2/pkg/rootless" 29 "github.com/containers/podman/v2/pkg/trust" 30 "github.com/containers/podman/v2/pkg/util" 31 "github.com/containers/storage" 32 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" 33 "github.com/pkg/errors" 34 "github.com/sirupsen/logrus" 35 ) 36 37 // SignatureStoreDir defines default directory to store signatures 38 const SignatureStoreDir = "/var/lib/containers/sigstore" 39 40 func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { 41 _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) 42 if err != nil { 43 if errors.Cause(err) == define.ErrMultipleImages { 44 return &entities.BoolReport{Value: true}, nil 45 } else { 46 if errors.Cause(err) != define.ErrNoSuchImage { 47 return nil, err 48 } 49 } 50 } 51 return &entities.BoolReport{Value: err == nil}, nil 52 } 53 54 func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { 55 results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) 56 if err != nil { 57 return nil, err 58 } 59 60 report := entities.ImagePruneReport{ 61 Report: entities.Report{ 62 Id: results, 63 Err: nil, 64 }, 65 } 66 return &report, nil 67 } 68 69 func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { 70 image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) 71 if err != nil { 72 return nil, err 73 } 74 results, err := image.History(ctx) 75 if err != nil { 76 return nil, err 77 } 78 79 history := entities.ImageHistoryReport{ 80 Layers: make([]entities.ImageHistoryLayer, len(results)), 81 } 82 83 for i, layer := range results { 84 history.Layers[i] = ToDomainHistoryLayer(layer) 85 } 86 return &history, nil 87 } 88 89 func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entities.ImageMountOptions) ([]*entities.ImageMountReport, error) { 90 var ( 91 images []*image.Image 92 err error 93 ) 94 if os.Geteuid() != 0 { 95 if driver := ir.Libpod.StorageConfig().GraphDriverName; driver != "vfs" { 96 // Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part 97 // of the mount command. 98 return nil, errors.Errorf("cannot mount using driver %s in rootless mode", driver) 99 } 100 101 became, ret, err := rootless.BecomeRootInUserNS("") 102 if err != nil { 103 return nil, err 104 } 105 if became { 106 os.Exit(ret) 107 } 108 } 109 if opts.All { 110 allImages, err := ir.Libpod.ImageRuntime().GetImages() 111 if err != nil { 112 return nil, err 113 } 114 for _, img := range allImages { 115 if !img.IsReadOnly() { 116 images = append(images, img) 117 } 118 } 119 } else { 120 for _, i := range nameOrIDs { 121 img, err := ir.Libpod.ImageRuntime().NewFromLocal(i) 122 if err != nil { 123 return nil, err 124 } 125 images = append(images, img) 126 } 127 } 128 reports := make([]*entities.ImageMountReport, 0, len(images)) 129 for _, img := range images { 130 report := entities.ImageMountReport{Id: img.ID()} 131 if img.IsReadOnly() { 132 report.Err = errors.Errorf("mounting readonly %s image not supported", img.ID()) 133 } else { 134 report.Path, report.Err = img.Mount([]string{}, "") 135 } 136 reports = append(reports, &report) 137 } 138 if len(reports) > 0 { 139 return reports, nil 140 } 141 142 images, err = ir.Libpod.ImageRuntime().GetImages() 143 if err != nil { 144 return nil, err 145 } 146 for _, i := range images { 147 mounted, path, err := i.Mounted() 148 if err != nil { 149 if errors.Cause(err) == storage.ErrLayerUnknown { 150 continue 151 } 152 return nil, err 153 } 154 if mounted { 155 tags, err := i.RepoTags() 156 if err != nil { 157 return nil, err 158 } 159 reports = append(reports, &entities.ImageMountReport{ 160 Id: i.ID(), 161 Name: string(i.Digest()), 162 Repositories: tags, 163 Path: path, 164 }) 165 } 166 } 167 return reports, nil 168 } 169 170 func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options entities.ImageUnmountOptions) ([]*entities.ImageUnmountReport, error) { 171 var images []*image.Image 172 173 if options.All { 174 allImages, err := ir.Libpod.ImageRuntime().GetImages() 175 if err != nil { 176 return nil, err 177 } 178 for _, img := range allImages { 179 if !img.IsReadOnly() { 180 images = append(images, img) 181 } 182 } 183 } else { 184 for _, i := range nameOrIDs { 185 img, err := ir.Libpod.ImageRuntime().NewFromLocal(i) 186 if err != nil { 187 return nil, err 188 } 189 images = append(images, img) 190 } 191 } 192 193 reports := []*entities.ImageUnmountReport{} 194 for _, img := range images { 195 report := entities.ImageUnmountReport{Id: img.ID()} 196 mounted, _, err := img.Mounted() 197 if err != nil { 198 // Errors will be caught in Unmount call below 199 // Default assumption to mounted 200 mounted = true 201 } 202 if !mounted { 203 continue 204 } 205 if err := img.Unmount(options.Force); err != nil { 206 if options.All && errors.Cause(err) == storage.ErrLayerNotMounted { 207 logrus.Debugf("Error umounting image %s, storage.ErrLayerNotMounted", img.ID()) 208 continue 209 } 210 report.Err = errors.Wrapf(err, "error unmounting image %s", img.ID()) 211 } 212 reports = append(reports, &report) 213 } 214 return reports, nil 215 } 216 217 func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer { 218 l := entities.ImageHistoryLayer{} 219 l.ID = layer.ID 220 l.Created = *layer.Created 221 l.CreatedBy = layer.CreatedBy 222 copy(l.Tags, layer.Tags) 223 l.Size = layer.Size 224 l.Comment = layer.Comment 225 return l 226 } 227 228 func pull(ctx context.Context, runtime *image.Runtime, rawImage string, options entities.ImagePullOptions, label *string) (*entities.ImagePullReport, error) { 229 var writer io.Writer 230 if !options.Quiet { 231 writer = os.Stderr 232 } 233 234 dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) 235 imageRef, err := alltransports.ParseImageName(rawImage) 236 if err != nil { 237 imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, rawImage)) 238 if err != nil { 239 return nil, errors.Wrapf(err, "invalid image reference %q", rawImage) 240 } 241 } 242 243 var registryCreds *types.DockerAuthConfig 244 if len(options.Username) > 0 && len(options.Password) > 0 { 245 registryCreds = &types.DockerAuthConfig{ 246 Username: options.Username, 247 Password: options.Password, 248 } 249 } 250 dockerRegistryOptions := image.DockerRegistryOptions{ 251 DockerRegistryCreds: registryCreds, 252 DockerCertPath: options.CertDir, 253 OSChoice: options.OverrideOS, 254 ArchitectureChoice: options.OverrideArch, 255 VariantChoice: options.OverrideVariant, 256 DockerInsecureSkipTLSVerify: options.SkipTLSVerify, 257 } 258 259 if !options.AllTags { 260 newImage, err := runtime.New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, label, options.PullPolicy) 261 if err != nil { 262 return nil, err 263 } 264 return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil 265 } 266 267 // --all-tags requires the docker transport 268 if imageRef.Transport().Name() != docker.Transport.Name() { 269 return nil, errors.New("--all-tags requires docker transport") 270 } 271 272 // Trim the docker-transport prefix. 273 rawImage = strings.TrimPrefix(rawImage, docker.Transport.Name()) 274 275 // all-tags doesn't work with a tagged reference, so let's check early 276 namedRef, err := reference.Parse(rawImage) 277 if err != nil { 278 return nil, errors.Wrapf(err, "error parsing %q", rawImage) 279 } 280 if _, isTagged := namedRef.(reference.Tagged); isTagged { 281 return nil, errors.New("--all-tags requires a reference without a tag") 282 283 } 284 285 systemContext := image.GetSystemContext("", options.Authfile, false) 286 tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef) 287 if err != nil { 288 return nil, errors.Wrapf(err, "error getting repository tags") 289 } 290 291 foundIDs := []string{} 292 for _, tag := range tags { 293 name := rawImage + ":" + tag 294 newImage, err := runtime.New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways) 295 if err != nil { 296 logrus.Errorf("error pulling image %q", name) 297 continue 298 } 299 foundIDs = append(foundIDs, newImage.ID()) 300 } 301 302 if len(tags) != len(foundIDs) { 303 return nil, err 304 } 305 return &entities.ImagePullReport{Images: foundIDs}, nil 306 } 307 308 func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) { 309 return pull(ctx, ir.Libpod.ImageRuntime(), rawImage, options, nil) 310 } 311 312 func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) { 313 reports := []*entities.ImageInspectReport{} 314 errs := []error{} 315 for _, i := range namesOrIDs { 316 img, err := ir.Libpod.ImageRuntime().NewFromLocal(i) 317 if err != nil { 318 // This is probably a no such image, treat as nonfatal. 319 errs = append(errs, err) 320 continue 321 } 322 result, err := img.Inspect(ctx) 323 if err != nil { 324 // This is more likely to be fatal. 325 return nil, nil, err 326 } 327 report := entities.ImageInspectReport{} 328 if err := domainUtils.DeepCopy(&report, result); err != nil { 329 return nil, nil, err 330 } 331 reports = append(reports, &report) 332 } 333 return reports, errs, nil 334 } 335 336 func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error { 337 var writer io.Writer 338 if !options.Quiet { 339 writer = os.Stderr 340 } 341 342 var manifestType string 343 switch options.Format { 344 case "": 345 // Default 346 case "oci": 347 manifestType = imgspecv1.MediaTypeImageManifest 348 case "v2s1": 349 manifestType = manifest.DockerV2Schema1SignedMediaType 350 case "v2s2", "docker": 351 manifestType = manifest.DockerV2Schema2MediaType 352 default: 353 return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format) 354 } 355 356 var registryCreds *types.DockerAuthConfig 357 if len(options.Username) > 0 && len(options.Password) > 0 { 358 registryCreds = &types.DockerAuthConfig{ 359 Username: options.Username, 360 Password: options.Password, 361 } 362 } 363 dockerRegistryOptions := image.DockerRegistryOptions{ 364 DockerRegistryCreds: registryCreds, 365 DockerCertPath: options.CertDir, 366 DockerInsecureSkipTLSVerify: options.SkipTLSVerify, 367 } 368 369 signOptions := image.SigningOptions{ 370 RemoveSignatures: options.RemoveSignatures, 371 SignBy: options.SignBy, 372 } 373 374 newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(source) 375 if err != nil { 376 return err 377 } 378 379 return newImage.PushImageToHeuristicDestination( 380 ctx, 381 destination, 382 manifestType, 383 options.Authfile, 384 options.DigestFile, 385 options.SignaturePolicy, 386 writer, 387 options.Compress, 388 signOptions, 389 &dockerRegistryOptions, 390 nil) 391 } 392 393 // func (r *imageRuntime) Delete(ctx context.Context, nameOrID string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { 394 // image, err := r.libpod.ImageEngine().NewFromLocal(nameOrID) 395 // if err != nil { 396 // return nil, err 397 // } 398 // 399 // results, err := r.libpod.RemoveImage(ctx, image, opts.Force) 400 // if err != nil { 401 // return nil, err 402 // } 403 // 404 // report := entities.ImageDeleteReport{} 405 // if err := domainUtils.DeepCopy(&report, results); err != nil { 406 // return nil, err 407 // } 408 // return &report, nil 409 // } 410 // 411 // func (r *imageRuntime) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { 412 // // TODO: map FilterOptions 413 // id, err := r.libpod.ImageEngine().PruneImages(ctx, opts.All, []string{}) 414 // if err != nil { 415 // return nil, err 416 // } 417 // 418 // // TODO: Determine Size 419 // report := entities.ImagePruneReport{} 420 // copy(report.Report.ID, id) 421 // return &report, nil 422 // } 423 424 func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { 425 newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) 426 if err != nil { 427 return err 428 } 429 for _, tag := range tags { 430 if err := newImage.TagImage(tag); err != nil { 431 return err 432 } 433 } 434 return nil 435 } 436 437 func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error { 438 newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) 439 if err != nil { 440 return err 441 } 442 // If only one arg is provided, all names are to be untagged 443 if len(tags) == 0 { 444 tags = newImage.Names() 445 } 446 for _, tag := range tags { 447 if err := newImage.UntagImage(tag); err != nil { 448 return err 449 } 450 } 451 return nil 452 } 453 454 func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) { 455 var ( 456 writer io.Writer 457 ) 458 if !opts.Quiet { 459 writer = os.Stderr 460 } 461 name, err := ir.Libpod.LoadImage(ctx, opts.Name, opts.Input, writer, opts.SignaturePolicy) 462 if err != nil { 463 return nil, err 464 } 465 names := strings.Split(name, ",") 466 if len(names) <= 1 { 467 newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) 468 if err != nil { 469 return nil, errors.Wrap(err, "image loaded but no additional tags were created") 470 } 471 if len(opts.Name) > 0 { 472 if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { 473 return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) 474 } 475 } 476 } 477 return &entities.ImageLoadReport{Names: names}, nil 478 } 479 480 func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { 481 id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.SignaturePolicy, opts.Changes, opts.Message, opts.Quiet) 482 if err != nil { 483 return nil, err 484 } 485 return &entities.ImageImportReport{Id: id}, nil 486 } 487 488 func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { 489 if options.MultiImageArchive { 490 nameOrIDs := append([]string{nameOrID}, tags...) 491 return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet, true) 492 } 493 newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) 494 if err != nil { 495 return err 496 } 497 return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress, true) 498 } 499 500 func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { 501 changes, err := ir.Libpod.GetDiff("", nameOrID) 502 if err != nil { 503 return nil, err 504 } 505 return &entities.DiffReport{Changes: changes}, nil 506 } 507 508 func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { 509 filter, err := image.ParseSearchFilter(opts.Filters) 510 if err != nil { 511 return nil, err 512 } 513 514 searchOpts := image.SearchOptions{ 515 Authfile: opts.Authfile, 516 Filter: *filter, 517 Limit: opts.Limit, 518 NoTrunc: opts.NoTrunc, 519 InsecureSkipTLSVerify: opts.SkipTLSVerify, 520 ListTags: opts.ListTags, 521 } 522 523 searchResults, err := image.SearchImages(term, searchOpts) 524 if err != nil { 525 return nil, err 526 } 527 528 // Convert from image.SearchResults to entities.ImageSearchReport. We don't 529 // want to leak any low-level packages into the remote client, which 530 // requires converting. 531 reports := make([]entities.ImageSearchReport, len(searchResults)) 532 for i := range searchResults { 533 reports[i].Index = searchResults[i].Index 534 reports[i].Name = searchResults[i].Name 535 reports[i].Description = searchResults[i].Description 536 reports[i].Stars = searchResults[i].Stars 537 reports[i].Official = searchResults[i].Official 538 reports[i].Automated = searchResults[i].Automated 539 reports[i].Tag = searchResults[i].Tag 540 } 541 542 return reports, nil 543 } 544 545 // GetConfig returns a copy of the configuration used by the runtime 546 func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { 547 return ir.Libpod.GetConfig() 548 } 549 550 func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) { 551 552 id, _, err := ir.Libpod.Build(ctx, opts.BuildOptions, containerFiles...) 553 if err != nil { 554 return nil, err 555 } 556 return &entities.BuildReport{ID: id}, nil 557 } 558 559 func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { 560 img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) 561 if err != nil { 562 return nil, err 563 } 564 results, err := img.GenerateTree(opts.WhatRequires) 565 if err != nil { 566 return nil, err 567 } 568 return &entities.ImageTreeReport{Tree: results}, nil 569 } 570 571 // removeErrorsToExitCode returns an exit code for the specified slice of 572 // image-removal errors. The error codes are set according to the documented 573 // behaviour in the Podman man pages. 574 func removeErrorsToExitCode(rmErrors []error) int { 575 var ( 576 // noSuchImageErrors indicates that at least one image was not found. 577 noSuchImageErrors bool 578 // inUseErrors indicates that at least one image is being used by a 579 // container. 580 inUseErrors bool 581 // otherErrors indicates that at least one error other than the two 582 // above occurred. 583 otherErrors bool 584 ) 585 586 if len(rmErrors) == 0 { 587 return 0 588 } 589 590 for _, e := range rmErrors { 591 switch errors.Cause(e) { 592 case define.ErrNoSuchImage: 593 noSuchImageErrors = true 594 case define.ErrImageInUse, storage.ErrImageUsedByContainer: 595 inUseErrors = true 596 default: 597 otherErrors = true 598 } 599 } 600 601 switch { 602 case inUseErrors: 603 // One of the specified images has child images or is 604 // being used by a container. 605 return 2 606 case noSuchImageErrors && !(otherErrors || inUseErrors): 607 // One of the specified images did not exist, and no other 608 // failures. 609 return 1 610 default: 611 return 125 612 } 613 } 614 615 // Remove removes one or more images from local storage. 616 func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, rmErrors []error) { 617 report = &entities.ImageRemoveReport{} 618 619 // Set the exit code at very end. 620 defer func() { 621 report.ExitCode = removeErrorsToExitCode(rmErrors) 622 }() 623 624 // deleteImage is an anonymous function to conveniently delete an image 625 // without having to pass all local data around. 626 deleteImage := func(img *image.Image) error { 627 results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force) 628 if err != nil { 629 return err 630 } 631 report.Deleted = append(report.Deleted, results.Deleted) 632 report.Untagged = append(report.Untagged, results.Untagged...) 633 return nil 634 } 635 636 // Delete all images from the local storage. 637 if opts.All { 638 previousImages := 0 639 // Remove all images one-by-one. 640 for { 641 storageImages, err := ir.Libpod.ImageRuntime().GetRWImages() 642 if err != nil { 643 rmErrors = append(rmErrors, err) 644 return 645 } 646 // No images (left) to remove, so we're done. 647 if len(storageImages) == 0 { 648 return 649 } 650 // Prevent infinity loops by making a delete-progress check. 651 if previousImages == len(storageImages) { 652 rmErrors = append(rmErrors, errors.New("unable to delete all images, check errors and re-run image removal if needed")) 653 break 654 } 655 previousImages = len(storageImages) 656 // Delete all "leaves" (i.e., images without child images). 657 for _, img := range storageImages { 658 isParent, err := img.IsParent(ctx) 659 if err != nil { 660 logrus.Warnf("%v, ignoring the error", err) 661 isParent = false 662 } 663 // Skip parent images. 664 if isParent { 665 continue 666 } 667 if err := deleteImage(img); err != nil { 668 rmErrors = append(rmErrors, err) 669 } 670 } 671 } 672 673 return 674 } 675 676 // Delete only the specified images. 677 for _, id := range images { 678 img, err := ir.Libpod.ImageRuntime().NewFromLocal(id) 679 if err != nil { 680 rmErrors = append(rmErrors, err) 681 continue 682 } 683 err = deleteImage(img) 684 if err != nil { 685 rmErrors = append(rmErrors, err) 686 } 687 } 688 return //nolint 689 } 690 691 // Shutdown Libpod engine 692 func (ir *ImageEngine) Shutdown(_ context.Context) { 693 shutdownSync.Do(func() { 694 _ = ir.Libpod.Shutdown(false) 695 }) 696 } 697 698 func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) { 699 mech, err := signature.NewGPGSigningMechanism() 700 if err != nil { 701 return nil, errors.Wrap(err, "error initializing GPG") 702 } 703 defer mech.Close() 704 if err := mech.SupportsSigning(); err != nil { 705 return nil, errors.Wrap(err, "signing is not supported") 706 } 707 sc := ir.Libpod.SystemContext() 708 sc.DockerCertPath = options.CertDir 709 710 systemRegistriesDirPath := trust.RegistriesDirPath(sc) 711 registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) 712 if err != nil { 713 return nil, errors.Wrapf(err, "error reading registry configuration") 714 } 715 716 for _, signimage := range names { 717 err = func() error { 718 srcRef, err := alltransports.ParseImageName(signimage) 719 if err != nil { 720 return errors.Wrapf(err, "error parsing image name") 721 } 722 rawSource, err := srcRef.NewImageSource(ctx, sc) 723 if err != nil { 724 return errors.Wrapf(err, "error getting image source") 725 } 726 defer func() { 727 if err = rawSource.Close(); err != nil { 728 logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err) 729 } 730 }() 731 getManifest, _, err := rawSource.GetManifest(ctx, nil) 732 if err != nil { 733 return errors.Wrapf(err, "error getting getManifest") 734 } 735 dockerReference := rawSource.Reference().DockerReference() 736 if dockerReference == nil { 737 return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) 738 } 739 var sigStoreDir string 740 if options.Directory != "" { 741 sigStoreDir = options.Directory 742 } 743 if sigStoreDir == "" { 744 if rootless.IsRootless() { 745 sigStoreDir = filepath.Join(filepath.Dir(ir.Libpod.StorageConfig().GraphRoot), "sigstore") 746 } else { 747 var sigStoreURI string 748 registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs) 749 if registryInfo != nil { 750 if sigStoreURI = registryInfo.SigStoreStaging; sigStoreURI == "" { 751 sigStoreURI = registryInfo.SigStore 752 } 753 } 754 if sigStoreURI == "" { 755 return errors.Errorf("no signature storage configuration found for %s", rawSource.Reference().DockerReference().String()) 756 757 } 758 sigStoreDir, err = localPathFromURI(sigStoreURI) 759 if err != nil { 760 return errors.Wrapf(err, "invalid signature storage %s", sigStoreURI) 761 } 762 } 763 } 764 manifestDigest, err := manifest.Digest(getManifest) 765 if err != nil { 766 return err 767 } 768 repo := reference.Path(dockerReference) 769 if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references 770 return errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dockerReference.String()) 771 } 772 773 // create signature 774 newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy) 775 if err != nil { 776 return errors.Wrapf(err, "error creating new signature") 777 } 778 // create the signstore file 779 signatureDir := fmt.Sprintf("%s@%s=%s", filepath.Join(sigStoreDir, repo), manifestDigest.Algorithm(), manifestDigest.Hex()) 780 if err := os.MkdirAll(signatureDir, 0751); err != nil { 781 // The directory is allowed to exist 782 if !os.IsExist(err) { 783 logrus.Error(err) 784 return nil 785 } 786 } 787 sigFilename, err := getSigFilename(signatureDir) 788 if err != nil { 789 logrus.Errorf("error creating sigstore file: %v", err) 790 return nil 791 } 792 err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644) 793 if err != nil { 794 logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) 795 return nil 796 } 797 return nil 798 }() 799 if err != nil { 800 return nil, err 801 } 802 } 803 return nil, nil 804 } 805 806 func getSigFilename(sigStoreDirPath string) (string, error) { 807 sigFileSuffix := 1 808 sigFiles, err := ioutil.ReadDir(sigStoreDirPath) 809 if err != nil { 810 return "", err 811 } 812 sigFilenames := make(map[string]bool) 813 for _, file := range sigFiles { 814 sigFilenames[file.Name()] = true 815 } 816 for { 817 sigFilename := "signature-" + strconv.Itoa(sigFileSuffix) 818 if _, exists := sigFilenames[sigFilename]; !exists { 819 return sigFilename, nil 820 } 821 sigFileSuffix++ 822 } 823 } 824 825 func localPathFromURI(sigStoreDir string) (string, error) { 826 url, err := url.Parse(sigStoreDir) 827 if err != nil { 828 return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir) 829 } 830 if url.Scheme != "file" { 831 return sigStoreDir, errors.Errorf("writing to %s is not supported. Use a supported scheme", sigStoreDir) 832 } 833 sigStoreDir = url.Path 834 return sigStoreDir, nil 835 }