github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/imageengine/buildah/manifest.go (about)

     1  // Copyright © 2022 Alibaba Group Holding Ltd.
     2  //
     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  package buildah
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"strings"
    22  
    23  	"github.com/containers/storage"
    24  	"github.com/pkg/errors"
    25  
    26  	"github.com/containers/buildah/util"
    27  	"github.com/containers/common/libimage"
    28  	"github.com/containers/common/libimage/manifests"
    29  	cp "github.com/containers/image/v5/copy"
    30  	"github.com/containers/image/v5/manifest"
    31  	"github.com/containers/image/v5/transports"
    32  	"github.com/containers/image/v5/transports/alltransports"
    33  	"github.com/containers/image/v5/types"
    34  	"github.com/hashicorp/go-multierror"
    35  	"github.com/opencontainers/go-digest"
    36  	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
    37  	"github.com/sealerio/sealer/pkg/define/options"
    38  	"github.com/sirupsen/logrus"
    39  )
    40  
    41  func (engine *Engine) LookupManifest(name string) (*libimage.ManifestList, error) {
    42  	return engine.libimageRuntime.LookupManifestList(name)
    43  }
    44  
    45  func (engine *Engine) CreateManifest(name string, opts *options.ManifestCreateOpts) (string, error) {
    46  	store := engine.ImageStore()
    47  	systemCxt := engine.SystemContext()
    48  	list := manifests.Create()
    49  
    50  	names, err := util.ExpandNames([]string{name}, systemCxt, store)
    51  	if err != nil {
    52  		return "", fmt.Errorf("encountered while expanding image name %q: %w", name, err)
    53  	}
    54  
    55  	return list.SaveToImage(store, "", names, manifest.DockerV2ListMediaType)
    56  }
    57  
    58  func (engine *Engine) DeleteManifests(names []string, opts *options.ManifestDeleteOpts) error {
    59  	runtime := engine.ImageRuntime()
    60  
    61  	rmiReports, rmiErrors := runtime.RemoveImages(context.Background(), names, &libimage.RemoveImagesOptions{
    62  		Filters:        []string{"readonly=false"},
    63  		LookupManifest: true,
    64  	})
    65  	for _, r := range rmiReports {
    66  		for _, u := range r.Untagged {
    67  			logrus.Infof("untagged: %s", u)
    68  		}
    69  	}
    70  	for _, r := range rmiReports {
    71  		if r.Removed {
    72  			logrus.Infof("%s", r.ID)
    73  		}
    74  	}
    75  
    76  	var multiE *multierror.Error
    77  	multiE = multierror.Append(multiE, rmiErrors...)
    78  	return multiE.ErrorOrNil()
    79  }
    80  
    81  func (engine *Engine) InspectManifest(name string, opts *options.ManifestInspectOpts) (*libimage.ManifestListData, error) {
    82  	runtime := engine.ImageRuntime()
    83  
    84  	// attempt to resolve the manifest list locally.
    85  	manifestList, err := runtime.LookupManifestList(name)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	return manifestList.Inspect()
    91  }
    92  
    93  func (engine *Engine) PushManifest(name, destSpec string, opts *options.PushOptions) error {
    94  	runtime := engine.ImageRuntime()
    95  	store := engine.ImageStore()
    96  	systemCxt := engine.SystemContext()
    97  	systemCxt.OCIInsecureSkipTLSVerify = opts.SkipTLSVerify
    98  	systemCxt.DockerInsecureSkipTLSVerify = types.NewOptionalBool(opts.SkipTLSVerify)
    99  
   100  	manifestList, err := runtime.LookupManifestList(name)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	_, list, err := manifests.LoadFromImage(store, manifestList.ID())
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	dest, err := alltransports.ParseImageName(destSpec)
   111  	if err != nil {
   112  		destTransport := strings.Split(destSpec, ":")[0]
   113  		if t := transports.Get(destTransport); t != nil {
   114  			return err
   115  		}
   116  
   117  		if strings.Contains(destSpec, "://") {
   118  			return err
   119  		}
   120  
   121  		destSpec = "docker://" + destSpec
   122  		dest2, err2 := alltransports.ParseImageName(destSpec)
   123  		if err2 != nil {
   124  			return err
   125  		}
   126  		dest = dest2
   127  		logrus.Debugf("Assuming docker:// as the transport method for DESTINATION: %s", destSpec)
   128  	}
   129  
   130  	var manifestType string
   131  	if opts.Format != "" {
   132  		switch opts.Format {
   133  		case "oci":
   134  			manifestType = imgspecv1.MediaTypeImageManifest
   135  		case "v2s2", "docker":
   136  			manifestType = manifest.DockerV2Schema2MediaType
   137  		default:
   138  			return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format)
   139  		}
   140  	}
   141  	pushOptions := manifests.PushOptions{
   142  		Store:              store,
   143  		SystemContext:      systemCxt,
   144  		ImageListSelection: cp.CopySystemImage,
   145  		Instances:          nil,
   146  		ManifestType:       manifestType,
   147  	}
   148  	if opts.All {
   149  		pushOptions.ImageListSelection = cp.CopyAllImages
   150  	}
   151  	if !opts.Quiet {
   152  		pushOptions.ReportWriter = os.Stderr
   153  	}
   154  
   155  	_, _, err = list.Push(getContext(), dest, pushOptions)
   156  
   157  	if err == nil && opts.Rm {
   158  		_, err = store.DeleteImage(manifestList.ID(), true)
   159  	}
   160  
   161  	return err
   162  }
   163  
   164  // AddToManifest :
   165  // for `manifestName`: if it is not exist,will create a new one. if not, it must be an existed manifest name.
   166  // for `imageNameOrIDList`:
   167  // if element is a single image just add it,
   168  // if element is a manifest will add it’s s all instance no matter what platform it is.
   169  func (engine *Engine) AddToManifest(manifestName string, imageNameOrIDList []string, opts *options.ManifestAddOpts) error {
   170  	var (
   171  		runtime = engine.ImageRuntime()
   172  	)
   173  
   174  	// check whether manifestName is already existed.
   175  	manifestList, err := runtime.LookupManifestList(manifestName)
   176  	if err == nil {
   177  		return engine.addToManifestList(manifestList, imageNameOrIDList, opts)
   178  	}
   179  
   180  	if !errors.Is(err, storage.ErrImageUnknown) {
   181  		return err
   182  	}
   183  
   184  	logrus.Infof("will create a new one manifest with name %s", manifestName)
   185  	// if not exit,create a new one
   186  	_, err = engine.CreateManifest(manifestName, &options.ManifestCreateOpts{})
   187  	if err != nil {
   188  		return fmt.Errorf("failed to create a new one manifest with name %s :%v", manifestName, err)
   189  	}
   190  	manifestList, err = runtime.LookupManifestList(manifestName)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	err = engine.addToManifestList(manifestList, imageNameOrIDList, opts)
   196  	if err != nil {
   197  		delErr := engine.DeleteManifests([]string{manifestName}, &options.ManifestDeleteOpts{})
   198  		if delErr != nil {
   199  			return fmt.Errorf("failed to delete %s : %v", manifestName, delErr)
   200  		}
   201  		return err
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  func (engine *Engine) addToManifestList(manifestList *libimage.ManifestList, imageNameOrIDList []string, opts *options.ManifestAddOpts) error {
   208  	var (
   209  		imageIDToAdd []string
   210  		err          error
   211  		store        = engine.ImageStore()
   212  	)
   213  
   214  	// determine all images
   215  	for _, imageNameOrID := range imageNameOrIDList {
   216  		ret, err := engine.getImageIDList(imageNameOrID)
   217  		if err != nil {
   218  			return fmt.Errorf("failed to look up %s", imageNameOrID)
   219  		}
   220  
   221  		imageIDToAdd = append(imageIDToAdd, ret...)
   222  	}
   223  
   224  	_, list, err := manifests.LoadFromImage(store, manifestList.ID())
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	// add each to manifest list
   230  	for _, imageID := range imageIDToAdd {
   231  		err = engine.addOneToManifestList(list, imageID, opts)
   232  		if err != nil {
   233  			return fmt.Errorf("failed to add new image %s to manifest :%v ", imageID, err)
   234  		}
   235  	}
   236  
   237  	_, err = list.SaveToImage(store, manifestList.ID(), nil, "")
   238  
   239  	return err
   240  }
   241  
   242  func (engine *Engine) addOneToManifestList(list manifests.List, imageSpec string, opts *options.ManifestAddOpts) error {
   243  	store := engine.ImageStore()
   244  	systemCxt := engine.SystemContext()
   245  
   246  	ref, err := alltransports.ParseImageName(imageSpec)
   247  	if err != nil {
   248  		if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
   249  			// check if the local image exists
   250  			if ref, _, err = util.FindImage(store, "", systemCxt, imageSpec); err != nil {
   251  				return err
   252  			}
   253  		}
   254  	}
   255  
   256  	digestID, err := list.Add(getContext(), systemCxt, ref, opts.All)
   257  	if err != nil {
   258  		var storeErr error
   259  		// Retry without a custom system context.  A user may want to add
   260  		// a custom platform (see #3511).
   261  		if ref, _, storeErr = util.FindImage(store, "", nil, imageSpec); storeErr != nil {
   262  			logrus.Errorf("Error while trying to find image on local storage: %v", storeErr)
   263  			return err
   264  		}
   265  		digestID, storeErr = list.Add(getContext(), systemCxt, ref, opts.All)
   266  		if storeErr != nil {
   267  			logrus.Errorf("Error while trying to add on manifest list: %v", storeErr)
   268  			return err
   269  		}
   270  	}
   271  
   272  	if opts.Os != "" {
   273  		if err = list.SetOS(digestID, opts.Os); err != nil {
   274  			return err
   275  		}
   276  	}
   277  	if opts.OsVersion != "" {
   278  		if err = list.SetOSVersion(digestID, opts.OsVersion); err != nil {
   279  			return err
   280  		}
   281  	}
   282  	if len(opts.OsFeatures) != 0 {
   283  		if err = list.SetOSFeatures(digestID, opts.OsFeatures); err != nil {
   284  			return err
   285  		}
   286  	}
   287  	if opts.Arch != "" {
   288  		if err = list.SetArchitecture(digestID, opts.Arch); err != nil {
   289  			return err
   290  		}
   291  	}
   292  	if opts.Variant != "" {
   293  		if err = list.SetVariant(digestID, opts.Variant); err != nil {
   294  			return err
   295  		}
   296  	}
   297  
   298  	if len(opts.Annotations) != 0 {
   299  		annotations := make(map[string]string)
   300  		for _, annotationSpec := range opts.Annotations {
   301  			spec := strings.SplitN(annotationSpec, "=", 2)
   302  			if len(spec) != 2 {
   303  				return fmt.Errorf("no value given for annotation %q", spec[0])
   304  			}
   305  			annotations[spec[0]] = spec[1]
   306  		}
   307  		if err = list.SetAnnotations(&digestID, annotations); err != nil {
   308  			return err
   309  		}
   310  	}
   311  
   312  	logrus.Infof("adding image %s successfully", imageSpec)
   313  
   314  	return nil
   315  }
   316  
   317  // getImageId get imageID by name Or id,what ever it is an image or a manifest
   318  // if it is image just return imageID
   319  // if it is a manifest, return its included instance IDs.
   320  func (engine *Engine) getImageIDList(imageNameOrID string) ([]string, error) {
   321  	// try to look up `imageNameOrID` as ManifestList
   322  	store := engine.ImageStore()
   323  	img, _, err := engine.ImageRuntime().LookupImage(imageNameOrID, &libimage.LookupImageOptions{
   324  		ManifestList: true,
   325  	})
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  
   330  	isManifest, err := img.IsManifestList(getContext())
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	// if not manifest, just return its ID.
   336  	if !isManifest {
   337  		return []string{img.ID()}, nil
   338  	}
   339  
   340  	// if it is a manifest, return its included instance ID.
   341  	logrus.Infof("image %q is a manifest list, looking up matching instances", imageNameOrID)
   342  
   343  	imageName := img.Names()[0]
   344  	manifestList, err := engine.ImageRuntime().LookupManifestList(imageName)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	_, list, err := manifests.LoadFromImage(store, manifestList.ID())
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  
   354  	var imageIDList []string
   355  	for _, instanceDigest := range list.Instances() {
   356  		images, err := store.ImagesByDigest(instanceDigest)
   357  		if err != nil {
   358  			return nil, err
   359  		}
   360  		if len(images) == 0 {
   361  			return nil, fmt.Errorf("no image matched with digest %s", instanceDigest)
   362  		}
   363  		imageIDList = append(imageIDList, images[0].ID)
   364  	}
   365  
   366  	return imageIDList, nil
   367  }
   368  
   369  func (engine *Engine) RemoveFromManifest(name string, instanceDigest digest.Digest, opts *options.ManifestRemoveOpts) error {
   370  	runtime := engine.ImageRuntime()
   371  
   372  	manifestList, err := runtime.LookupManifestList(name)
   373  	if err != nil {
   374  		return err
   375  	}
   376  
   377  	if err = manifestList.RemoveInstance(instanceDigest); err != nil {
   378  		return err
   379  	}
   380  
   381  	logrus.Infof("%s: %s", manifestList.ID(), instanceDigest.String())
   382  
   383  	return nil
   384  }