Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support pack different arch image together #2184

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/kubefile/parser/image_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (testImageEngine) PushManifest(name, destSpec string, opts *options.PushOpt
panic("implement me")
}

func (testImageEngine) AddToManifest(name, imageSpec string, opts *options.ManifestAddOpts) error {
func (testImageEngine) AddToManifest(name string, imageSpec []string, opts *options.ManifestAddOpts) error {
panic("implement me")
}

Expand Down
30 changes: 11 additions & 19 deletions cmd/sealer/cmd/alpha/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,35 +101,26 @@ func manifestAddCommand() *cobra.Command {
Long: manifestAddDescription,
RunE: func(cmd *cobra.Command, args []string) error {
var (
name, imageSpec string
manifestName = addManifestOpts.TargetName
imagesToAdd = args
)

switch len(args) {
case 0, 1:
return errors.New("at least a manifest name and an image name to add must be specified")
case 2:
name = args[0]
if name == "" {
return fmt.Errorf(`invalid manifest name "%s" `, args[0])
}
imageSpec = args[1]
if imageSpec == "" {
return fmt.Errorf(`invalid image name "%s" `, args[1])
}
default:
return errors.New("at least two arguments are necessary: manifest name and an image name to add to list ")
// if not set `-t` flag , assume the first one is the manifestName,others is the images need to be added to.
if manifestName == "" {
manifestName = args[0]
imagesToAdd = args[1:]
}

engine, err := imageengine.NewImageEngine(options.EngineGlobalConfigurations{})
if err != nil {
return err
}

return engine.AddToManifest(name, imageSpec, &addManifestOpts)
return engine.AddToManifest(manifestName, imagesToAdd, &addManifestOpts)
},
Example: `sealer alpha manifest add mylist:v1.11 image:v1.11-amd64
sealer alpha manifest add mylist:v1.11 transport:imageName`,
Args: cobra.MinimumNArgs(2),
Example: `sealer alpha manifest add app-amd:v1 app-arm:v1 -t all-in-one:v1
sealer alpha manifest add mylist:v1.11 image:v1.11-amd64`,
Args: cobra.MinimumNArgs(1),
}

flags := addCommand.Flags()
Expand All @@ -140,6 +131,7 @@ func manifestAddCommand() *cobra.Command {
flags.StringSliceVar(&addManifestOpts.OsFeatures, "os-features", nil, "override the OS `features` of the specified image")
flags.StringSliceVar(&addManifestOpts.Annotations, "annotation", nil, "set an `annotation` for the specified image")
flags.BoolVar(&addManifestOpts.All, "all", false, "add all of the list's images if the image is a list")
flags.StringVarP(&addManifestOpts.TargetName, "target", "t", "", "target image name,if it is not exist,will create a new one")

return addCommand
}
Expand Down
1 change: 1 addition & 0 deletions pkg/define/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ type ManifestAddOpts struct {
OsFeatures []string
Annotations []string
All bool
TargetName string
}

type ManifestRemoveOpts struct {
Expand Down
18 changes: 3 additions & 15 deletions pkg/imageengine/buildah/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,9 @@ func (engine *Engine) Load(opts *options.LoadOptions) error {
return fmt.Errorf("failed to create new manifest %s :%v ", manifestName, err)
}

defer func() {
if errors.Is(err, LoadError) {
err = engine.DeleteManifests([]string{manifestName}, &options.ManifestDeleteOpts{})
if err != nil {
logrus.Errorf("failed to delete manifest %s :%v ", manifestName, err)
}
}
}()

for _, imageID := range instancesIDs {
err = engine.AddToManifest(manifestName, imageID, &options.ManifestAddOpts{})
if err != nil {
logrus.Errorf("failed to add new image %s to %s :%v ", imageID, manifestName, err)
return LoadError
}
err = engine.AddToManifest(manifestName, instancesIDs, &options.ManifestAddOpts{})
if err != nil {
return fmt.Errorf("failed to add new image to %s :%v ", manifestName, err)
}

return nil
Expand Down
137 changes: 130 additions & 7 deletions pkg/imageengine/buildah/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"os"
"strings"

"github.com/containers/storage"
"github.com/pkg/errors"

"github.com/containers/buildah/util"
"github.com/containers/common/libimage"
"github.com/containers/common/libimage/manifests"
Expand Down Expand Up @@ -158,20 +161,88 @@ func (engine *Engine) PushManifest(name, destSpec string, opts *options.PushOpti
return err
}

func (engine *Engine) AddToManifest(name, imageSpec string, opts *options.ManifestAddOpts) error {
runtime := engine.ImageRuntime()
store := engine.ImageStore()
systemCxt := engine.SystemContext()
// AddToManifest :
//for `manifestName`: if it is not exist,will create a new one. if not, it must be an existed manifest name.
//for `imageNameOrIDList`:
//if element is a single image just add it,
//if element is a manifest will add it’s s all instance no matter what platform it is.
func (engine *Engine) AddToManifest(manifestName string, imageNameOrIDList []string, opts *options.ManifestAddOpts) error {
var (
runtime = engine.ImageRuntime()
)

// check whether manifestName is already existed.
manifestList, err := runtime.LookupManifestList(manifestName)
if err == nil {
return engine.addToManifestList(manifestList, imageNameOrIDList, opts)
}

manifestList, err := runtime.LookupManifestList(name)
if !errors.Is(err, storage.ErrImageUnknown) {
return err
}

logrus.Infof("will create a new one manifest with name %s", manifestName)
// if not exit,create a new one
_, err = engine.CreateManifest(manifestName, &options.ManifestCreateOpts{})
if err != nil {
return fmt.Errorf("failed to create a new one manifest with name %s :%v", manifestName, err)
}
manifestList, err = runtime.LookupManifestList(manifestName)
if err != nil {
return err
}

err = engine.addToManifestList(manifestList, imageNameOrIDList, opts)
if err != nil {
delErr := engine.DeleteManifests([]string{manifestName}, &options.ManifestDeleteOpts{})
if delErr != nil {
return fmt.Errorf("failed to delete %s : %v", manifestName, delErr)
}
return err
}

return nil
}

func (engine *Engine) addToManifestList(manifestList *libimage.ManifestList, imageNameOrIDList []string, opts *options.ManifestAddOpts) error {
var (
imageIDToAdd []string
err error
store = engine.ImageStore()
)

// determine all images
for _, imageNameOrID := range imageNameOrIDList {
ret, err := engine.getImageIDList(imageNameOrID)
if err != nil {
return fmt.Errorf("failed to look up %s", imageNameOrID)
}

imageIDToAdd = append(imageIDToAdd, ret...)
}

_, list, err := manifests.LoadFromImage(store, manifestList.ID())
if err != nil {
return err
}

// add each to manifest list
for _, imageID := range imageIDToAdd {
err = engine.addOneToManifestList(list, imageID, opts)
if err != nil {
return fmt.Errorf("failed to add new image %s to manifest :%v ", imageID, err)
}
}

_, err = list.SaveToImage(store, manifestList.ID(), nil, "")

return err
}

func (engine *Engine) addOneToManifestList(list manifests.List, imageSpec string, opts *options.ManifestAddOpts) error {
store := engine.ImageStore()
systemCxt := engine.SystemContext()

ref, err := alltransports.ParseImageName(imageSpec)
if err != nil {
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
Expand Down Expand Up @@ -238,9 +309,61 @@ func (engine *Engine) AddToManifest(name, imageSpec string, opts *options.Manife
}
}

_, err = list.SaveToImage(store, manifestList.ID(), nil, "")
logrus.Infof("adding image %s successfully", imageSpec)

return err
return nil
}

//getImageId get imageID by name Or id,what ever it is an image or a manifest
// if it is image just return imageID
// if it is a manifest, return its included instance IDs.
func (engine *Engine) getImageIDList(imageNameOrID string) ([]string, error) {
// try to look up `imageNameOrID` as ManifestList
store := engine.ImageStore()
img, _, err := engine.ImageRuntime().LookupImage(imageNameOrID, &libimage.LookupImageOptions{
ManifestList: true,
})
if err != nil {
return nil, err
}

isManifest, err := img.IsManifestList(getContext())
if err != nil {
return nil, err
}

// if not manifest, just return its ID.
if !isManifest {
return []string{img.ID()}, nil
}

// if it is a manifest, return its included instance ID.
logrus.Infof("image %q is a manifest list, looking up matching instances", imageNameOrID)

imageName := img.Names()[0]
manifestList, err := engine.ImageRuntime().LookupManifestList(imageName)
if err != nil {
return nil, err
}

_, list, err := manifests.LoadFromImage(store, manifestList.ID())
if err != nil {
return nil, err
}

var imageIDList []string
for _, instanceDigest := range list.Instances() {
images, err := store.ImagesByDigest(instanceDigest)
if err != nil {
return nil, err
}
if len(images) == 0 {
return nil, fmt.Errorf("no image matched with digest %s", instanceDigest)
}
imageIDList = append(imageIDList, images[0].ID)
}

return imageIDList, nil
}

func (engine *Engine) RemoveFromManifest(name string, instanceDigest digest.Digest, opts *options.ManifestRemoveOpts) error {
Expand Down
19 changes: 11 additions & 8 deletions pkg/imageengine/buildah/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@ import (
"path/filepath"

"github.com/containers/common/libimage"
"github.com/containers/common/libimage/manifests"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/sealerio/sealer/common"
"github.com/sealerio/sealer/pkg/define/options"
"github.com/sealerio/sealer/utils/archive"
osi "github.com/sealerio/sealer/utils/os"
"github.com/sealerio/sealer/utils/os/fs"
"github.com/sirupsen/logrus"
)

// Save image as tar file, if image is multi-arch image, will save all its instances and manifest name as tar file.
func (engine *Engine) Save(opts *options.SaveOptions) error {
imageNameOrID := opts.ImageNameOrID
imageTar := opts.Output
store := engine.ImageStore()

if len(imageNameOrID) == 0 {
return errors.New("image name or id must be specified")
Expand Down Expand Up @@ -98,23 +99,25 @@ func (engine *Engine) Save(opts *options.SaveOptions) error {
return err
}

schema2List, err := manifestList.Inspect()
_, list, err := manifests.LoadFromImage(store, manifestList.ID())
if err != nil {
return err
}

for _, m := range schema2List.Manifests {
instance, err := manifestList.LookupInstance(engine.Context(), m.Platform.Architecture, m.Platform.OS, m.Platform.Variant)
for _, instanceDigest := range list.Instances() {
images, err := store.ImagesByDigest(instanceDigest)
if err != nil {
return err
}
if len(images) == 0 {
return fmt.Errorf("no image matched with digest %s", instanceDigest)
}

instanceTar := filepath.Join(tempDir, instance.ID()+".tar")
err = engine.saveOneImage(instance.ID(), opts.Format, instanceTar, opts.Compress)
instanceTar := filepath.Join(tempDir, images[0].ID+".tar")
err = engine.saveOneImage(images[0].ID, opts.Format, instanceTar, opts.Compress)
if err != nil {
return err
}

pathsToCompress = append(pathsToCompress, instanceTar)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/imageengine/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type Interface interface {

PushManifest(name, destSpec string, opts *options.PushOptions) error

AddToManifest(name, imageSpec string, opts *options.ManifestAddOpts) error
AddToManifest(name string, imageNameOrIDList []string, opts *options.ManifestAddOpts) error

RemoveFromManifest(name string, instanceDigest digest.Digest, opts *options.ManifestRemoveOpts) error
}