specgen,run: support running container from valid manifest list

Following PR adds support for running containers from a manifest list
present on localstorage. Before this PR podman only supports running
containers from valid images but not from manifest list.

So `podman run -it --platform <some> <manifest-list> command` should
become functional now and users should be able to resolve images on the
bases of provided `--platform` string.

Example
```
podman manifest create test
podman build --platform linux/amd64,linux/arm64 --manifest test .
podman run --rm --platform linux/arm64/v8 test uname -a
```

Closes: https://github.com/containers/podman/issues/14773

Signed-off-by: Aditya R <arajan@redhat.com>
This commit is contained in:
Aditya R
2022-07-05 12:06:47 +05:30
parent 3e7e66edad
commit efe1176dd9
7 changed files with 47 additions and 5 deletions

View File

@ -63,7 +63,7 @@ func clone(cmd *cobra.Command, args []string) error {
ctrClone.Image = args[2] ctrClone.Image = args[2]
if !cliVals.RootFS { if !cliVals.RootFS {
rawImageName := args[0] rawImageName := args[0]
name, err := PullImage(ctrClone.Image, ctrClone.CreateOpts) name, err := PullImage(ctrClone.Image, &ctrClone.CreateOpts)
if err != nil { if err != nil {
return err return err
} }

View File

@ -141,7 +141,7 @@ func create(cmd *cobra.Command, args []string) error {
rawImageName := "" rawImageName := ""
if !cliVals.RootFS { if !cliVals.RootFS {
rawImageName = args[0] rawImageName = args[0]
name, err := PullImage(args[0], cliVals) name, err := PullImage(args[0], &cliVals)
if err != nil { if err != nil {
return err return err
} }
@ -305,7 +305,8 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
return vals, nil return vals, nil
} }
func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (string, error) { // Pulls image if any also parses and populates OS, Arch and Variant in specified container create options
func PullImage(imageName string, cliVals *entities.ContainerCreateOptions) (string, error) {
pullPolicy, err := config.ParsePullPolicy(cliVals.Pull) pullPolicy, err := config.ParsePullPolicy(cliVals.Pull)
if err != nil { if err != nil {
return "", err return "", err

View File

@ -141,7 +141,7 @@ func run(cmd *cobra.Command, args []string) error {
rawImageName := "" rawImageName := ""
if !cliVals.RootFS { if !cliVals.RootFS {
rawImageName = args[0] rawImageName = args[0]
name, err := PullImage(args[0], cliVals) name, err := PullImage(args[0], &cliVals)
if err != nil { if err != nil {
return err return err
} }
@ -192,6 +192,9 @@ func run(cmd *cobra.Command, args []string) error {
return err return err
} }
s.RawImageName = rawImageName s.RawImageName = rawImageName
s.ImageOS = cliVals.OS
s.ImageArch = cliVals.Arch
s.ImageVariant = cliVals.Variant
s.Passwd = &runOpts.Passwd s.Passwd = &runOpts.Passwd
runOpts.Spec = s runOpts.Spec = s

View File

@ -38,10 +38,19 @@ func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGen
} }
// Need to look up image. // Need to look up image.
image, resolvedName, err := r.LibimageRuntime().LookupImage(s.Image, nil) lookupOptions := &libimage.LookupImageOptions{ManifestList: true}
image, resolvedName, err := r.LibimageRuntime().LookupImage(s.Image, lookupOptions)
if err != nil { if err != nil {
return nil, "", nil, err return nil, "", nil, err
} }
manifestList, err := image.ToManifestList()
// only process if manifest list found otherwise expect it to be regular image
if err == nil {
image, err = manifestList.LookupInstance(ctx, s.ImageArch, s.ImageOS, s.ImageVariant)
if err != nil {
return nil, "", nil, err
}
}
s.SetImage(image, resolvedName) s.SetImage(image, resolvedName)
inspectData, err := image.Inspect(ctx, nil) inspectData, err := image.Inspect(ctx, nil)
if err != nil { if err != nil {

View File

@ -103,6 +103,12 @@ type ContainerBasicConfig struct {
// RawImageName is the user-specified and unprocessed input referring // RawImageName is the user-specified and unprocessed input referring
// to a local or a remote image. // to a local or a remote image.
RawImageName string `json:"raw_image_name,omitempty"` RawImageName string `json:"raw_image_name,omitempty"`
// ImageOS is the user-specified image OS
ImageOS string `json:"image_os,omitempty"`
// ImageArch is the user-specified image architecture
ImageArch string `json:"image_arch,omitempty"`
// ImageVariant is the user-specified image variant
ImageVariant string `json:"image_variant,omitempty"`
// RestartPolicy is the container's restart policy - an action which // RestartPolicy is the container's restart policy - an action which
// will be taken when the container exits. // will be taken when the container exits.
// If not given, the default policy, which does nothing, will be used. // If not given, the default policy, which does nothing, will be used.

View File

@ -0,0 +1 @@
FROM --platform=$TARGETPLATFORM alpine

View File

@ -73,6 +73,28 @@ var _ = Describe("Podman run", func() {
Expect(session.OutputToString()).To(ContainSubstring("graphRootMounted=1")) Expect(session.OutputToString()).To(ContainSubstring("graphRootMounted=1"))
}) })
It("podman run from manifest list", func() {
session := podmanTest.Podman([]string{"manifest", "create", "localhost/test:latest"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"build", "-f", "build/Containerfile.with-platform", "--platform", "linux/amd64,linux/arm64", "--manifest", "localhost/test:latest"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"run", "--platform", "linux/arm64", "localhost/test", "uname", "-a"})
session.WaitWithDefaultTimeout()
exitCode := session.ExitCode()
// CI could either support requested platform or not, if it supports then output should contain `aarch64`
// if not run should fail with a very specific error i.e `Exec format error` anything other than this should
// be marked as failure of test.
if exitCode == 0 {
Expect(session.OutputToString()).To(ContainSubstring("aarch64"))
} else {
Expect(session.ErrorToString()).To(ContainSubstring("Exec format error"))
}
})
It("podman run a container based on a complex local image name", func() { It("podman run a container based on a complex local image name", func() {
imageName := strings.TrimPrefix(nginx, "quay.io/") imageName := strings.TrimPrefix(nginx, "quay.io/")
session := podmanTest.Podman([]string{"run", imageName, "ls"}) session := podmanTest.Podman([]string{"run", imageName, "ls"})