add a podman-compose command

**podman compose** is a thin wrapper around an external compose provider
such as docker-compose or podman-compose.  This means that `podman
compose` is executing another tool that implements the compose
functionality but sets up the environment in a way to let the compose
provider communicate transparently with the local Podman socket.  The
specified options as well the command and argument are passed directly
to the compose provider.

The default compose providers are `docker-compose` and `podman-compose`.
If installed, `docker-compose` takes precedence since it is the original
implementation of the Compose specification and is widely used on the
supported platforms (i.e., Linux, Mac OS, Windows).

If you want to change the default behavior or have a custom installation
path for your provider of choice, please change the `compose_provider`
field in `containers.conf(5)`.  You may also set the
`PODMAN_COMPOSE_PROVIDER` environment variable.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2023-07-17 13:43:24 +02:00
parent b9383f41ac
commit e596b17fbe
26 changed files with 521 additions and 23 deletions

View File

@ -266,6 +266,17 @@ type EngineConfig struct {
// in containers-registries.conf(5).
CompatAPIEnforceDockerHub bool `toml:"compat_api_enforce_docker_hub,omitempty"`
// ComposeProviders specifies one or more external providers for the
// compose command. The first found provider is used for execution.
// Can be an absolute and relative path or a (file) name. Make sure to
// expand the return items via `os.ExpandEnv`.
ComposeProviders []string `toml:"compose_providers,omitempty"`
// ComposeWarningLogs emits logs on each invocation of the compose
// command indicating that an external compose provider is being
// executed.
ComposeWarningLogs bool `toml:"compose_warning_logs,omitempty"`
// DBBackend is the database backend to be used by Podman.
DBBackend string `toml:"database_backend,omitempty"`

View File

@ -11,7 +11,7 @@ import (
"strings"
"syscall"
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
"github.com/container-orchestrated-devices/container-device-interface/pkg/parser"
units "github.com/docker/go-units"
)
@ -58,7 +58,7 @@ func (c *EngineConfig) validatePaths() error {
func (c *ContainersConfig) validateDevices() error {
for _, d := range c.Devices {
if cdi.IsQualifiedName(d) {
if parser.IsQualifiedName(d) {
continue
}
_, _, _, err := Device(d)

View File

@ -376,6 +376,20 @@ default_sysctls = [
#
#active_service = "production"
# Enforces using docker.io for completing short names in Podman's compatibility
# REST API. Note that this will ignore unqualified-search-registries and
# short-name aliases defined in containers-registries.conf(5).
#compat_api_enforce_docker_hub = true
# Specify one or more external providers for the compose command. The first
# found provider is used for execution. Can be an absolute and relative path
# or a (file) name.
#compose_providers=[]
# Emit logs on each invocation of the compose command indicating that an
# external compose provider is being executed.
#compose_warning_logs = true
# The compression format to use when pushing an image.
# Valid options are: `gzip`, `zstd` and `zstd:chunked`.
#

View File

@ -500,7 +500,7 @@ default_sysctls = [
# List of the OCI runtimes that support --format=json. When json is supported
# engine will use it for reporting nicer errors.
#
#runtime_supports_json = ["crun", "runc", "kata", "runsc", "youki", "krun"]
#runtime_supports_json = ["crun", "runc", "kata", "runsc", "youki", "krun", "ocijail"]
# List of the OCI runtimes that supports running containers with KVM Separation.
#

View File

@ -87,6 +87,16 @@ var (
// should be set during link-time, if different packagers put their
// helper binary in a different location.
additionalHelperBinariesDir string
defaultUnixComposeProviders = []string{
"docker-compose",
"$HOME/.docker/cli-plugins/docker-compose",
"/usr/local/lib/docker/cli-plugins/docker-compose",
"/usr/local/libexec/docker/cli-plugins/docker-compose",
"/usr/lib/docker/cli-plugins/docker-compose",
"/usr/libexec/docker/cli-plugins/docker-compose",
"podman-compose",
}
)
// nolint:unparam
@ -260,6 +270,8 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
c.EventsLogFileMaxSize = eventsLogMaxSize(DefaultEventsLogSizeMax)
c.CompatAPIEnforceDockerHub = true
c.ComposeProviders = getDefaultComposeProviders() // may vary across supported platforms
c.ComposeWarningLogs = true
if path, ok := os.LookupEnv("CONTAINERS_STORAGE_CONF"); ok {
if err := types.SetDefaultConfigFilePath(path); err != nil {
@ -406,6 +418,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
"runsc",
"youki",
"krun",
"ocijail",
}
c.RuntimeSupportsNoCgroups = []string{"crun", "krun"}
c.RuntimeSupportsKVM = []string{"kata", "kata-runtime", "kata-qemu", "kata-fc", "krun"}

View File

@ -20,3 +20,14 @@ func getDefaultMachineVolumes() []string {
"/var/folders:/var/folders",
}
}
func getDefaultComposeProviders() []string {
return []string{
"docker-compose",
"$HOME/.docker/cli-plugins/docker-compose",
"/opt/homebrew/bin/docker-compose",
"/usr/local/bin/docker-compose",
"/Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose",
"podman-compose",
}
}

View File

@ -26,3 +26,7 @@ func getLibpodTmpDir() string {
func getDefaultMachineVolumes() []string {
return []string{"$HOME:$HOME"}
}
func getDefaultComposeProviders() []string {
return defaultUnixComposeProviders
}

View File

@ -74,3 +74,7 @@ func getLibpodTmpDir() string {
func getDefaultMachineVolumes() []string {
return []string{"$HOME:$HOME"}
}
func getDefaultComposeProviders() []string {
return defaultUnixComposeProviders
}

View File

@ -49,3 +49,8 @@ func getLibpodTmpDir() string {
func getDefaultMachineVolumes() []string {
return []string{}
}
func getDefaultComposeProviders() []string {
// Rely on os.LookPath to do the trick on Windows.
return []string{"docker-compose", "podman-compose"}
}

View File

@ -27,7 +27,16 @@ func queryPackageVersion(cmdArg ...string) string {
cmd := exec.Command(cmdArg[0], cmdArg[1:]...)
if outp, err := cmd.Output(); err == nil {
output = string(outp)
if cmdArg[0] == "/usr/bin/dpkg" {
deb := false
if cmdArg[0] == "/usr/bin/dlocate" {
// can return multiple matches
l := strings.Split(output, "\n")
output = l[0]
deb = true
} else if cmdArg[0] == "/usr/bin/dpkg" {
deb = true
}
if deb {
r := strings.Split(output, ": ")
queryFormat := `${Package}_${Version}_${Architecture}`
cmd = exec.Command("/usr/bin/dpkg-query", "-f", queryFormat, "-W", r[0])
@ -47,9 +56,14 @@ func queryPackageVersion(cmdArg ...string) string {
// Note: This function is copied from containers/podman libpod/util.go
// Please see https://github.com/containers/common/pull/1460
func PackageVersion(program string) string { // program is full path
_, err := os.Stat(program)
if err != nil {
return UnknownPackage
}
packagers := [][]string{
{"/usr/bin/rpm", "-q", "-f"},
{"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu
{"/usr/bin/dlocate", "-F"}, // Debian, Ubuntu (quick)
{"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu (slow)
{"/usr/bin/pacman", "-Qo"}, // Arch
{"/usr/bin/qfile", "-qv"}, // Gentoo (quick)
{"/usr/bin/equery", "b"}, // Gentoo (slow)