Skip to content

Commit 41047e4

Browse files
committed
engine: add platform field to EngineConfig
Signed-off-by: Trevor Burnham <trevorburnham@gmail.com>
1 parent ed7ccf9 commit 41047e4

4 files changed

Lines changed: 117 additions & 0 deletions

File tree

common/docs/containers.conf.5.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,14 @@ Locks are recycled and can be reused after the associated container, pod, or vol
809809
The default number available is 2048.
810810
If this is changed, a lock renumbering must be performed, using the `podman system renumber` command.
811811

812+
**platform**=""
813+
814+
Specifies the default platform for image operations such as pull, build, run,
815+
and create. When set, container engines will use this platform instead of the
816+
host's native platform. The format is `os/arch` or `os/arch/variant` (e.g.,
817+
`linux/amd64`, `linux/arm64/v8`). If empty (the default), the host's platform
818+
is used.
819+
812820
**pod_exit_policy**="continue"
813821

814822
Set the exit policy of the pod when the last container exits. Supported policies are:

common/pkg/config/config.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,12 @@ type EngineConfig struct {
422422
// OCIRuntimesFlags are the set of configured OCI runtimes' flags
423423
OCIRuntimesFlags map[string][]string `toml:"runtimes_flags,omitempty"`
424424

425+
// Platform specifies the default platform (os/arch[/variant]) for image
426+
// operations such as pull, build, run, and create. If empty, the host's
427+
// platform is used. Format: "os/arch" or "os/arch/variant" (e.g.,
428+
// "linux/amd64", "linux/arm64/v8").
429+
Platform string `toml:"platform,omitempty"`
430+
425431
// PlatformToOCIRuntime requests specific OCI runtime for a specified platform of image.
426432
PlatformToOCIRuntime map[string]string `toml:"platform_to_oci_runtime,omitempty"`
427433

@@ -738,6 +744,40 @@ func (c *EngineConfig) ImagePlatformToRuntime(os string, arch string) string {
738744
return c.OCIRuntime
739745
}
740746

747+
// PlatformComponents parses the Platform field and returns the individual
748+
// os, architecture, and variant components. Empty strings are returned for
749+
// unset components. If Platform is empty, all returned values are empty.
750+
func (c *EngineConfig) PlatformComponents() (os, arch, variant string) {
751+
if c.Platform == "" {
752+
return "", "", ""
753+
}
754+
parts := strings.SplitN(c.Platform, "/", 3)
755+
switch len(parts) {
756+
case 3:
757+
return parts[0], parts[1], parts[2]
758+
case 2:
759+
return parts[0], parts[1], ""
760+
default:
761+
return parts[0], "", ""
762+
}
763+
}
764+
765+
// validatePlatform checks that the Platform field, if set, is a valid
766+
// os/arch or os/arch/variant string.
767+
func (c *EngineConfig) validatePlatform() error {
768+
if c.Platform == "" {
769+
return nil
770+
}
771+
parts := strings.SplitN(c.Platform, "/", 3)
772+
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
773+
return fmt.Errorf("invalid platform %q: must be in the format os/arch[/variant]", c.Platform)
774+
}
775+
if len(parts) == 3 && parts[2] == "" {
776+
return fmt.Errorf("invalid platform %q: variant must not be empty when specified", c.Platform)
777+
}
778+
return nil
779+
}
780+
741781
// CheckCgroupsAndAdjustConfig checks if we're running rootless with the systemd
742782
// cgroup manager. In case the user session isn't available, we're switching the
743783
// cgroup manager to cgroupfs. Note, this only applies to rootless.
@@ -852,6 +892,10 @@ func (c *EngineConfig) Validate() error {
852892
return err
853893
}
854894

895+
if err := c.validatePlatform(); err != nil {
896+
return err
897+
}
898+
855899
if err := ValidateImageVolumeMode(c.ImageVolumeMode); err != nil {
856900
return err
857901
}

common/pkg/config/config_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ image_copy_tmp_dir="storage"`
216216
})
217217
})
218218

219+
219220
Describe("NewConfig", func() {
220221
It("should success with default config", func() {
221222
// Given
@@ -347,6 +348,7 @@ image_copy_tmp_dir="storage"`
347348
gomega.Expect(config.Containers.Privileged).To(gomega.BeTrue())
348349
gomega.Expect(config.Containers.ReadOnly).To(gomega.BeTrue())
349350
gomega.Expect(config.Engine.ImageParallelCopies).To(gomega.Equal(uint(10)))
351+
gomega.Expect(config.Engine.Platform).To(gomega.Equal("linux/amd64"))
350352
gomega.Expect(config.Engine.PlatformToOCIRuntime).To(gomega.Equal(PlatformToOCIRuntimeMap))
351353
gomega.Expect(config.Engine.ImageDefaultFormat).To(gomega.Equal("v2s2"))
352354
gomega.Expect(config.Engine.CompressionFormat).To(gomega.BeEquivalentTo("zstd:chunked"))
@@ -480,6 +482,68 @@ image_copy_tmp_dir="storage"`
480482
err = defConf.Engine.Validate()
481483
gomega.Expect(err).To(gomega.HaveOccurred())
482484
})
485+
486+
It("should succeed with valid platform", func() {
487+
defConf, err := defaultConfig()
488+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
489+
490+
defConf.Engine.Platform = "linux/amd64"
491+
err = defConf.Engine.Validate()
492+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
493+
494+
defConf.Engine.Platform = "linux/arm64/v8"
495+
err = defConf.Engine.Validate()
496+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
497+
})
498+
499+
It("should succeed with empty platform", func() {
500+
defConf, err := defaultConfig()
501+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
502+
503+
defConf.Engine.Platform = ""
504+
err = defConf.Engine.Validate()
505+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
506+
})
507+
508+
It("should fail with invalid platform", func() {
509+
defConf, err := defaultConfig()
510+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
511+
512+
defConf.Engine.Platform = "linux"
513+
err = defConf.Engine.Validate()
514+
gomega.Expect(err).To(gomega.HaveOccurred())
515+
516+
defConf.Engine.Platform = "linux/amd64/"
517+
err = defConf.Engine.Validate()
518+
gomega.Expect(err).To(gomega.HaveOccurred())
519+
520+
defConf.Engine.Platform = "/amd64"
521+
err = defConf.Engine.Validate()
522+
gomega.Expect(err).To(gomega.HaveOccurred())
523+
})
524+
525+
It("should parse platform components correctly", func() {
526+
defConf, err := defaultConfig()
527+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
528+
529+
defConf.Engine.Platform = "linux/amd64"
530+
os, arch, variant := defConf.Engine.PlatformComponents()
531+
gomega.Expect(os).To(gomega.Equal("linux"))
532+
gomega.Expect(arch).To(gomega.Equal("amd64"))
533+
gomega.Expect(variant).To(gomega.Equal(""))
534+
535+
defConf.Engine.Platform = "linux/arm64/v8"
536+
os, arch, variant = defConf.Engine.PlatformComponents()
537+
gomega.Expect(os).To(gomega.Equal("linux"))
538+
gomega.Expect(arch).To(gomega.Equal("arm64"))
539+
gomega.Expect(variant).To(gomega.Equal("v8"))
540+
541+
defConf.Engine.Platform = ""
542+
os, arch, variant = defConf.Engine.PlatformComponents()
543+
gomega.Expect(os).To(gomega.Equal(""))
544+
gomega.Expect(arch).To(gomega.Equal(""))
545+
gomega.Expect(variant).To(gomega.Equal(""))
546+
})
483547
})
484548

485549
Describe("Service Destinations", func() {

common/pkg/config/testdata/containers_override.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ events_container_create_inspect_data = true
1818
pod_exit_policy="stop"
1919
compression_format="zstd:chunked"
2020
cdi_spec_dirs = [ "/somepath" ]
21+
platform = "linux/amd64"
2122

2223
[engine.platform_to_oci_runtime]
2324
hello = "world"

0 commit comments

Comments
 (0)