github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/domain/infra/abi/manifest.go (about) 1 // +build !remote 2 3 package abi 4 5 import ( 6 "bytes" 7 "context" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "strings" 13 14 "github.com/containers/buildah/manifests" 15 buildahManifests "github.com/containers/buildah/pkg/manifests" 16 "github.com/containers/buildah/util" 17 buildahUtil "github.com/containers/buildah/util" 18 cp "github.com/containers/image/v5/copy" 19 "github.com/containers/image/v5/docker" 20 "github.com/containers/image/v5/manifest" 21 "github.com/containers/image/v5/transports" 22 "github.com/containers/image/v5/transports/alltransports" 23 "github.com/containers/image/v5/types" 24 libpodImage "github.com/containers/podman/v2/libpod/image" 25 "github.com/containers/podman/v2/pkg/domain/entities" 26 "github.com/opencontainers/go-digest" 27 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" 28 "github.com/sirupsen/logrus" 29 30 "github.com/pkg/errors" 31 ) 32 33 // ManifestCreate implements logic for creating manifest lists via ImageEngine 34 func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) { 35 fullNames, err := buildahUtil.ExpandNames(names, "", ir.Libpod.SystemContext(), ir.Libpod.GetStore()) 36 if err != nil { 37 return "", errors.Wrapf(err, "error encountered while expanding image name %q", names) 38 } 39 imageID, err := libpodImage.CreateManifestList(ir.Libpod.ImageRuntime(), *ir.Libpod.SystemContext(), fullNames, images, opts.All) 40 if err != nil { 41 return imageID, err 42 } 43 return imageID, err 44 } 45 46 // ManifestInspect returns the content of a manifest list or image 47 func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { 48 if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil { 49 // return the manifest in local storage 50 if list, err := newImage.InspectManifest(); err == nil { 51 buf, err := json.MarshalIndent(list, "", " ") 52 if err != nil { 53 return buf, errors.Wrapf(err, "error rendering manifest %s for display", name) 54 } 55 return buf, nil 56 // no return if local image is not a list of images type 57 // continue on getting valid manifest through remote serice 58 } else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported { 59 return nil, errors.Wrapf(err, "loading manifest %q", name) 60 } 61 } 62 sc := ir.Libpod.SystemContext() 63 refs, err := util.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name) 64 if err != nil { 65 return nil, err 66 } 67 var ( 68 latestErr error 69 result []byte 70 manType string 71 b bytes.Buffer 72 ) 73 appendErr := func(e error) { 74 if latestErr == nil { 75 latestErr = e 76 } else { 77 latestErr = errors.Wrapf(latestErr, "tried %v\n", e) 78 } 79 } 80 for _, ref := range refs { 81 src, err := ref.NewImageSource(ctx, sc) 82 if err != nil { 83 appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref))) 84 continue 85 } 86 defer src.Close() 87 88 manifestBytes, manifestType, err := src.GetManifest(ctx, nil) 89 if err != nil { 90 appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref))) 91 continue 92 } 93 94 result = manifestBytes 95 manType = manifestType 96 break 97 } 98 if len(result) == 0 && latestErr != nil { 99 return nil, latestErr 100 } 101 102 switch manType { 103 case manifest.DockerV2Schema2MediaType: 104 logrus.Warnf("Warning! The manifest type %s is not a manifest list but a single image.", manType) 105 schema2Manifest, err := manifest.Schema2FromManifest(result) 106 if err != nil { 107 return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType) 108 } 109 if result, err = schema2Manifest.Serialize(); err != nil { 110 return nil, err 111 } 112 default: 113 listBlob, err := manifest.ListFromBlob(result, manType) 114 if err != nil { 115 return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType) 116 } 117 list, err := listBlob.ConvertToMIMEType(manifest.DockerV2ListMediaType) 118 if err != nil { 119 return nil, err 120 } 121 if result, err = list.Serialize(); err != nil { 122 return nil, err 123 } 124 } 125 126 if err = json.Indent(&b, result, "", " "); err != nil { 127 return nil, errors.Wrapf(err, "error rendering manifest %s for display", name) 128 } 129 return b.Bytes(), nil 130 } 131 132 // ManifestAdd adds images to the manifest list 133 func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) { 134 imageSpec := opts.Images[0] 135 listImageSpec := opts.Images[1] 136 dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) 137 _, err := alltransports.ParseImageName(imageSpec) 138 if err != nil { 139 _, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, imageSpec)) 140 if err != nil { 141 return "", errors.Errorf("invalid image reference %q", imageSpec) 142 } 143 } 144 listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec) 145 if err != nil { 146 return "", errors.Wrapf(err, "error retrieving local image from image name %s", listImageSpec) 147 } 148 149 manifestAddOpts := libpodImage.ManifestAddOpts{ 150 All: opts.All, 151 Arch: opts.Arch, 152 Features: opts.Features, 153 Images: opts.Images, 154 OS: opts.OS, 155 OSVersion: opts.OSVersion, 156 Variant: opts.Variant, 157 } 158 if len(opts.Annotation) != 0 { 159 annotations := make(map[string]string) 160 for _, annotationSpec := range opts.Annotation { 161 spec := strings.SplitN(annotationSpec, "=", 2) 162 if len(spec) != 2 { 163 return "", errors.Errorf("no value given for annotation %q", spec[0]) 164 } 165 annotations[spec[0]] = spec[1] 166 } 167 manifestAddOpts.Annotation = annotations 168 } 169 170 // Set the system context. 171 sys := ir.Libpod.SystemContext() 172 if sys != nil { 173 sys = &types.SystemContext{} 174 } 175 sys.AuthFilePath = opts.Authfile 176 sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify 177 sys.DockerCertPath = opts.CertDir 178 179 if opts.Username != "" && opts.Password != "" { 180 sys.DockerAuthConfig = &types.DockerAuthConfig{ 181 Username: opts.Username, 182 Password: opts.Password, 183 } 184 } 185 186 listID, err := listImage.AddManifest(*sys, manifestAddOpts) 187 if err != nil { 188 return listID, err 189 } 190 return listID, nil 191 } 192 193 // ManifestAnnotate updates an entry of the manifest list 194 func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) { 195 listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) 196 if err != nil { 197 return "", errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) 198 } 199 digest, err := digest.Parse(names[1]) 200 if err != nil { 201 return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err) 202 } 203 manifestAnnotateOpts := libpodImage.ManifestAnnotateOpts{ 204 Arch: opts.Arch, 205 Features: opts.Features, 206 OS: opts.OS, 207 OSFeatures: opts.OSFeatures, 208 OSVersion: opts.OSVersion, 209 Variant: opts.Variant, 210 } 211 if len(opts.Annotation) > 0 { 212 annotations := make(map[string]string) 213 for _, annotationSpec := range opts.Annotation { 214 spec := strings.SplitN(annotationSpec, "=", 2) 215 if len(spec) != 2 { 216 return "", errors.Errorf("no value given for annotation %q", spec[0]) 217 } 218 annotations[spec[0]] = spec[1] 219 } 220 manifestAnnotateOpts.Annotation = annotations 221 } 222 updatedListID, err := listImage.AnnotateManifest(*ir.Libpod.SystemContext(), digest, manifestAnnotateOpts) 223 if err == nil { 224 return fmt.Sprintf("%s: %s", updatedListID, digest.String()), nil 225 } 226 return "", err 227 } 228 229 // ManifestRemove removes specified digest from the specified manifest list 230 func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { 231 instanceDigest, err := digest.Parse(names[1]) 232 if err != nil { 233 return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err) 234 } 235 listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) 236 if err != nil { 237 return "", errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) 238 } 239 updatedListID, err := listImage.RemoveManifest(instanceDigest) 240 if err == nil { 241 return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil 242 } 243 return "", err 244 } 245 246 // ManifestPush pushes a manifest list or image index to the destination 247 func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { 248 listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) 249 if err != nil { 250 return errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) 251 } 252 dest, err := alltransports.ParseImageName(names[1]) 253 if err != nil { 254 return err 255 } 256 var manifestType string 257 if opts.Format != "" { 258 switch opts.Format { 259 case "oci": 260 manifestType = imgspecv1.MediaTypeImageManifest 261 case "v2s2", "docker": 262 manifestType = manifest.DockerV2Schema2MediaType 263 default: 264 return errors.Errorf("unknown format %q. Choose one of the supported formats: 'oci' or 'v2s2'", opts.Format) 265 } 266 } 267 268 // Set the system context. 269 sys := ir.Libpod.SystemContext() 270 if sys != nil { 271 sys = &types.SystemContext{} 272 } 273 sys.AuthFilePath = opts.Authfile 274 sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify 275 sys.DockerCertPath = opts.CertDir 276 277 if opts.Username != "" && opts.Password != "" { 278 sys.DockerAuthConfig = &types.DockerAuthConfig{ 279 Username: opts.Username, 280 Password: opts.Password, 281 } 282 } 283 284 options := manifests.PushOptions{ 285 Store: ir.Libpod.GetStore(), 286 SystemContext: sys, 287 ImageListSelection: cp.CopySpecificImages, 288 Instances: nil, 289 RemoveSignatures: opts.RemoveSignatures, 290 SignBy: opts.SignBy, 291 ManifestType: manifestType, 292 } 293 if opts.All { 294 options.ImageListSelection = cp.CopyAllImages 295 } 296 if !opts.Quiet { 297 options.ReportWriter = os.Stderr 298 } 299 digest, err := listImage.PushManifest(dest, options) 300 if err == nil && opts.Purge { 301 _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) 302 } 303 if opts.DigestFile != "" { 304 if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil { 305 return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) 306 } 307 } 308 return err 309 }