Skip to content
Draft
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
11 changes: 7 additions & 4 deletions cmd/podman/containers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

"github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/podman/v6/cmd/podman/common"
"github.com/containers/podman/v6/cmd/podman/registry"
"github.com/containers/podman/v6/cmd/podman/utils"
Expand Down Expand Up @@ -352,11 +353,13 @@ func pullImage(cmd *cobra.Command, imageName string, cliVals *entities.Container
if cliVals.Arch != "" || cliVals.OS != "" {
return "", errors.New("--platform option can not be specified with --arch or --os")
}
OS, Arch, hasArch := strings.Cut(cliVals.Platform, "/")
cliVals.OS = OS
if hasArch {
cliVals.Arch = Arch
pOS, pArch, pVariant, pErr := parse.Platform(cliVals.Platform)
if pErr != nil {
return "", fmt.Errorf("parsing platform %q: %w", cliVals.Platform, pErr)
}
cliVals.OS = pOS
cliVals.Arch = pArch
cliVals.Variant = pVariant
}
}

Expand Down
12 changes: 4 additions & 8 deletions cmd/podman/images/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"errors"
"fmt"
"os"
"strings"

"github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/podman/v6/cmd/podman/common"
"github.com/containers/podman/v6/cmd/podman/registry"
"github.com/containers/podman/v6/cmd/podman/utils"
Expand Down Expand Up @@ -188,13 +188,9 @@ func imagePull(cmd *cobra.Command, args []string) error {
return errors.New("--platform option can not be specified with --arch or --os")
}

specs := strings.Split(platform, "/")
pullOptions.OS = specs[0] // may be empty
if len(specs) > 1 {
pullOptions.Arch = specs[1]
if len(specs) > 2 {
pullOptions.Variant = specs[2]
}
pullOptions.OS, pullOptions.Arch, pullOptions.Variant, err = parse.Platform(platform)
if err != nil {
return fmt.Errorf("parsing platform %q: %w", platform, err)
}
}

Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/options/platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@
Specify the platform for selecting the image. (Conflicts with --arch and --os)
The `--platform` option can be used to override the current architecture and operating system.
Unless overridden, subsequent lookups of the same image in the local storage matches this platform, regardless of the host.

If not specified, the default platform is resolved in the following order:
1. The **CONTAINER_DEFAULT_PLATFORM** environment variable.
2. The **platform** setting in **containers.conf**(5).
3. The host's native OS/architecture.
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-build.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,11 @@ architecture of the host (for example `linux/arm`). Unless overridden,
subsequent lookups of the same image in the local storage matches this
platform, regardless of the host.

If not specified, the default platform is resolved in the following order:
1. The **CONTAINER_DEFAULT_PLATFORM** environment variable.
2. The **platform** setting in **containers.conf**(5).
3. The host's native OS/architecture.

If `--platform` is set, then the values of the `--arch`, `--os`, and
`--variant` options is overridden.

Expand Down
17 changes: 15 additions & 2 deletions pkg/api/handlers/compat/containers_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/containers/podman/v6/pkg/rootless"
"github.com/containers/podman/v6/pkg/specgen"
"github.com/containers/podman/v6/pkg/specgenutil"
"github.com/containers/podman/v6/pkg/util"
"github.com/docker/docker/api/types/mount"
"go.podman.io/common/libimage"
"go.podman.io/common/libnetwork/types"
Expand Down Expand Up @@ -74,13 +75,25 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
body.Config.Image = imageName

lookupImageOptions := libimage.LookupImageOptions{}
if query.Platform != "" {
// If no platform was specified in the query, fall back to
// CONTAINER_DEFAULT_PLATFORM env var, then containers.conf platform.
platform := query.Platform
if platform != "" {
var err error
lookupImageOptions.OS, lookupImageOptions.Architecture, lookupImageOptions.Variant, err = parse.Platform(query.Platform)
lookupImageOptions.OS, lookupImageOptions.Architecture, lookupImageOptions.Variant, err = parse.Platform(platform)
if err != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("parsing platform: %w", err))
return
}
} else {
defOS, defArch, defVariant, err := util.DefaultPlatform(rtc.Engine.Platform)
if err != nil {
utils.Error(w, http.StatusBadRequest, err)
return
}
lookupImageOptions.OS = defOS
lookupImageOptions.Architecture = defArch
lookupImageOptions.Variant = defVariant
}
newImage, resolvedName, err := runtime.LibimageRuntime().LookupImage(body.Config.Image, &lookupImageOptions)
if err != nil {
Expand Down
66 changes: 51 additions & 15 deletions pkg/api/handlers/compat/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/containers/buildah"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/podman/v6/libpod"
"github.com/containers/podman/v6/pkg/api/handlers"
"github.com/containers/podman/v6/pkg/api/handlers/utils"
Expand Down Expand Up @@ -222,16 +223,37 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) {
reference = possiblyNormalizedName
}

platformSpecs := strings.Split(query.Platform, "/")
opts := entities.ImageImportOptions{
Source: source,
Changes: query.Changes,
Message: query.Message,
Reference: reference,
OS: platformSpecs[0],
// If no platform was specified in the query, fall back to
// CONTAINER_DEFAULT_PLATFORM env var, then containers.conf platform.
var platOS, platArch, platVariant string
if query.Platform != "" {
var pErr error
platOS, platArch, platVariant, pErr = parse.Platform(query.Platform)
if pErr != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("parsing platform: %w", pErr))
return
}
} else {
rtc, err := runtime.GetConfig()
confPlatform := ""
if err == nil {
confPlatform = rtc.Engine.Platform
}
var pErr error
platOS, platArch, platVariant, pErr = util.DefaultPlatform(confPlatform)
if pErr != nil {
utils.Error(w, http.StatusBadRequest, pErr)
return
}
}
if len(platformSpecs) > 1 {
opts.Architecture = platformSpecs[1]
opts := entities.ImageImportOptions{
Source: source,
Changes: query.Changes,
Message: query.Message,
Reference: reference,
OS: platOS,
Architecture: platArch,
Variant: platVariant,
}

imageEngine := abi.ImageEngine{Libpod: runtime}
Expand Down Expand Up @@ -310,12 +332,26 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
}

// Handle the platform.
platformSpecs := strings.Split(query.Platform, "/")
pullOptions.OS = platformSpecs[0] // may be empty
if len(platformSpecs) > 1 {
pullOptions.Architecture = platformSpecs[1]
if len(platformSpecs) > 2 {
pullOptions.Variant = platformSpecs[2]
// If no platform was specified in the query, fall back to
// CONTAINER_DEFAULT_PLATFORM env var, then containers.conf platform.
if query.Platform != "" {
var pErr error
pullOptions.OS, pullOptions.Architecture, pullOptions.Variant, pErr = parse.Platform(query.Platform)
if pErr != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("parsing platform: %w", pErr))
return
}
} else {
rtc, err := runtime.GetConfig()
confPlatform := ""
if err == nil {
confPlatform = rtc.Engine.Platform
}
var pErr error
pullOptions.OS, pullOptions.Architecture, pullOptions.Variant, pErr = util.DefaultPlatform(confPlatform)
if pErr != nil {
utils.Error(w, http.StatusBadRequest, pErr)
return
}
}

Expand Down
45 changes: 32 additions & 13 deletions pkg/api/handlers/compat/images_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,20 +776,39 @@ func createBuildOptions(query *BuildQuery, buildCtx *BuildContext, queryValues u

// Process platforms
platforms := query.Platform
if len(platforms) == 1 {
// Docker API uses comma separated platform arg so match this here
platforms = strings.Split(query.Platform[0], ",")
}
for _, platformSpec := range platforms {
os, arch, variant, err := parse.Platform(platformSpec)
if err != nil {
return nil, cleanup, utils.GetBadRequestError("platform", platformSpec, err)
if len(platforms) == 0 || (len(platforms) == 1 && platforms[0] == "") {
// No explicit platform specified; fall back to
// CONTAINER_DEFAULT_PLATFORM env var, then containers.conf platform.
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
confPlatform := ""
if rtc, err := runtime.GetConfigNoCopy(); err == nil {
confPlatform = rtc.Engine.Platform
}
defOS, defArch, defVariant, pErr := util.DefaultPlatform(confPlatform)
if pErr != nil {
return nil, cleanup, utils.GetBadRequestError("platform", confPlatform, pErr)
}
if defOS != "" || defArch != "" || defVariant != "" {
buildOptions.Platforms = append(buildOptions.Platforms, struct{ OS, Arch, Variant string }{
OS: defOS, Arch: defArch, Variant: defVariant,
})
}
} else {
if len(platforms) == 1 {
// Docker API uses comma separated platform arg so match this here
platforms = strings.Split(platforms[0], ",")
}
for _, platformSpec := range platforms {
os, arch, variant, err := parse.Platform(platformSpec)
if err != nil {
return nil, cleanup, utils.GetBadRequestError("platform", platformSpec, err)
}
buildOptions.Platforms = append(buildOptions.Platforms, struct{ OS, Arch, Variant string }{
OS: os,
Arch: arch,
Variant: variant,
})
}
buildOptions.Platforms = append(buildOptions.Platforms, struct{ OS, Arch, Variant string }{
OS: os,
Arch: arch,
Variant: variant,
})
}

// Process source policy
Expand Down
18 changes: 18 additions & 0 deletions pkg/api/handlers/libpod/images_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/containers/podman/v6/pkg/auth"
"github.com/containers/podman/v6/pkg/channel"
"github.com/containers/podman/v6/pkg/domain/entities"
"github.com/containers/podman/v6/pkg/util"
"github.com/gorilla/schema"
"github.com/sirupsen/logrus"
"go.podman.io/common/libimage"
Expand Down Expand Up @@ -75,6 +76,23 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
pullOptions.OS = query.OS
pullOptions.Variant = query.Variant

// If no explicit platform fields were given, fall back to
// CONTAINER_DEFAULT_PLATFORM env var, then containers.conf platform.
if query.Arch == "" && query.OS == "" && query.Variant == "" {
confPlatform := ""
if rtc, err := runtime.GetConfig(); err == nil {
confPlatform = rtc.Engine.Platform
}
defOS, defArch, defVariant, pErr := util.DefaultPlatform(confPlatform)
if pErr != nil {
utils.Error(w, http.StatusBadRequest, pErr)
return
}
pullOptions.OS = defOS
pullOptions.Architecture = defArch
pullOptions.Variant = defVariant
}

if _, found := r.URL.Query()["tlsVerify"]; found {
pullOptions.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
}
Expand Down
51 changes: 51 additions & 0 deletions pkg/domain/infra/abi/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
domainUtils "github.com/containers/podman/v6/pkg/domain/utils"
"github.com/containers/podman/v6/pkg/errorhandling"
"github.com/containers/podman/v6/pkg/rootless"
"github.com/containers/podman/v6/pkg/util"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -282,6 +283,22 @@ func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options
}

func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
// If no explicit platform was requested, fall back to
// containers.conf [engine] platform (resolved server-side).
if options.Arch == "" && options.OS == "" && options.Variant == "" {
rtc, err := ir.Libpod.GetConfigNoCopy()
if err != nil {
return nil, err
}
defOS, defArch, defVariant, err := util.DefaultPlatform(rtc.Engine.Platform)
if err != nil {
return nil, err
}
options.OS = defOS
options.Arch = defArch
options.Variant = defVariant
}

pullOptions := &libimage.PullOptions{AllTags: options.AllTags}
pullOptions.AuthFilePath = options.Authfile
pullOptions.CertDirPath = options.CertDir
Expand Down Expand Up @@ -512,6 +529,22 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string,
}

func (ir *ImageEngine) Import(ctx context.Context, options entities.ImageImportOptions) (*entities.ImageImportReport, error) {
// If no explicit platform was requested, fall back to
// containers.conf [engine] platform (resolved server-side).
if options.OS == "" && options.Architecture == "" && options.Variant == "" {
rtc, err := ir.Libpod.GetConfigNoCopy()
if err != nil {
return nil, err
}
defOS, defArch, defVariant, err := util.DefaultPlatform(rtc.Engine.Platform)
if err != nil {
return nil, err
}
options.OS = defOS
options.Architecture = defArch
options.Variant = defVariant
}

importOptions := &libimage.ImportOptions{}
importOptions.Changes = options.Changes
importOptions.CommitMessage = options.Message
Expand Down Expand Up @@ -581,6 +614,24 @@ func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) {
}

func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) {
// If no explicit platform was requested, fall back to
// containers.conf [engine] platform (resolved server-side).
if len(opts.Platforms) == 0 {
rtc, err := ir.Libpod.GetConfigNoCopy()
if err != nil {
return nil, err
}
defOS, defArch, defVariant, err := util.DefaultPlatform(rtc.Engine.Platform)
if err != nil {
return nil, err
}
if defOS != "" || defArch != "" || defVariant != "" {
opts.Platforms = append(opts.Platforms, struct{ OS, Arch, Variant string }{
OS: defOS, Arch: defArch, Variant: defVariant,
})
}
}

id, _, err := ir.Libpod.Build(ctx, opts.BuildOptions, containerFiles...)
if err != nil {
return nil, err
Expand Down
23 changes: 23 additions & 0 deletions pkg/util/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"syscall"
"time"

"github.com/containers/buildah/pkg/parse"
"github.com/containers/podman/v6/libpod/define"
"github.com/containers/podman/v6/pkg/namespaces"
"github.com/containers/podman/v6/pkg/rootless"
Expand Down Expand Up @@ -143,6 +144,28 @@ func ParseRegistryCreds(creds string) (*types.DockerAuthConfig, error) {
}, nil
}

// DefaultPlatform returns the default platform (platOS, arch, variant) from
// the CONTAINER_DEFAULT_PLATFORM environment variable or the given
// containers.conf Engine.Platform value. The confPlatform parameter should
// be the caller's config.Engine.Platform string. If neither source provides
// a value, empty strings and a nil error are returned. The platform string
// is always parsed through buildah's parse.Platform() so that validation and
// normalisation are consistent across all call sites.
func DefaultPlatform(confPlatform string) (platOS, arch, variant string, err error) {
platform, ok := os.LookupEnv("CONTAINER_DEFAULT_PLATFORM")
if !ok || platform == "" {
platform = confPlatform
}
if platform == "" {
return "", "", "", nil
}
platOS, arch, variant, err = parse.Platform(platform)
if err != nil {
return "", "", "", fmt.Errorf("parsing default platform %q: %w", platform, err)
}
return platOS, arch, variant, nil
}

// StringMatchRegexSlice determines if a given string matches one of the given regexes, returns bool
func StringMatchRegexSlice(s string, re []string) bool {
for _, r := range re {
Expand Down
Loading