mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00
Merge pull request #19297 from vrothberg/RUN-1865
add a podman-compose command
This commit is contained in:
302
cmd/podman/compose.go
Normal file
302
cmd/podman/compose.go
Normal file
@ -0,0 +1,302 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
cmdMachine "github.com/containers/podman/v4/cmd/podman/machine"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var composeCommand = &cobra.Command{
|
||||
Use: "compose [options]",
|
||||
Short: "Run compose workloads via an external provider such as docker-compose or podman-compose",
|
||||
Long: `This command 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 PODMAN_COMPOSE_PROVIDER environment variable.`,
|
||||
RunE: composeMain,
|
||||
ValidArgsFunction: composeCompletion,
|
||||
Example: `podman compose -f nginx.yaml up --detach
|
||||
podman --log-level=debug compose -f many-images.yaml pull`,
|
||||
DisableFlagParsing: true,
|
||||
Annotations: map[string]string{registry.ParentNSRequired: ""}, // don't join user NS for SSH to work correctly
|
||||
}
|
||||
|
||||
func init() {
|
||||
// NOTE: we need to fully disable flag parsing and manually parse the
|
||||
// flags in composeMain. cobra's FParseErrWhitelist will strip off
|
||||
// unknown flags _before_ the first argument. So `--unknown argument`
|
||||
// will show as `argument`.
|
||||
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{Command: composeCommand})
|
||||
}
|
||||
|
||||
func composeCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
var stdout strings.Builder
|
||||
|
||||
args = append(args, toComplete)
|
||||
args = append([]string{"__complete"}, args...)
|
||||
if err := composeProviderExec(args, &stdout, io.Discard, false); err != nil {
|
||||
// Ignore errors since some providers may not expose a __complete command.
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
var num int
|
||||
output := strings.Split(strings.TrimRight(stdout.String(), "\n"), "\n")
|
||||
if len(output) >= 1 {
|
||||
if lastLine := output[len(output)-1]; strings.HasPrefix(lastLine, ":") {
|
||||
var err error
|
||||
if num, err = strconv.Atoi(lastLine[1:]); err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
output = output[:len(output)-1]
|
||||
}
|
||||
}
|
||||
return output, cobra.ShellCompDirective(num)
|
||||
}
|
||||
|
||||
// composeProvider provides the name of or absolute path to the compose
|
||||
// provider (i.e., the external binary such as docker-compose).
|
||||
func composeProvider() (string, error) {
|
||||
if value, ok := os.LookupEnv("PODMAN_COMPOSE_PROVIDER"); ok {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
candidates := registry.PodmanConfig().ContainersConfDefaultsRO.Engine.ComposeProviders
|
||||
if len(candidates) == 0 {
|
||||
return "", errors.New("no compose provider specified, please refer to `man podman-compose` for details")
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
path, err := exec.LookPath(os.ExpandEnv(candidate))
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
// First specified provider "candidate" wins.
|
||||
logrus.Debugf("Found compose provider %q", path)
|
||||
return path, nil
|
||||
}
|
||||
|
||||
return "", errors.New("no configured compose provider found on system, please refer to the documentation for details")
|
||||
}
|
||||
|
||||
// composeDockerHost returns the value to be set in the DOCKER_HOST environment
|
||||
// variable.
|
||||
func composeDockerHost() (string, error) {
|
||||
if value, ok := os.LookupEnv("DOCKER_HOST"); ok {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// For local clients (Linux/FreeBSD), use the default API
|
||||
// address.
|
||||
if !registry.IsRemote() {
|
||||
return registry.DefaultAPIAddress(), nil
|
||||
}
|
||||
|
||||
cfg, err := config.ReadCustomConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// NOTE: podman --connection=foo and --url=... are injected
|
||||
// into the default connection below in `root.go`.
|
||||
defaultConnection := cfg.Engine.ActiveService
|
||||
if defaultConnection == "" {
|
||||
switch runtime.GOOS {
|
||||
// If no default connection is set on Linux or FreeBSD,
|
||||
// we just use the local socket by default - just as
|
||||
// the remote client does.
|
||||
case "linux", "freebsd":
|
||||
return registry.DefaultAPIAddress(), nil
|
||||
// If there is no default connection on Windows or Mac
|
||||
// OS, we can safely assume that something went wrong.
|
||||
// A `podman machine init` will set the connection.
|
||||
default:
|
||||
return "", fmt.Errorf("cannot connect to a socket or via SSH: no default connection found: consider running `podman machine init`")
|
||||
}
|
||||
}
|
||||
|
||||
connection, ok := cfg.Engine.ServiceDestinations[defaultConnection]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("internal error: default connection %q not found in database", defaultConnection)
|
||||
}
|
||||
parsedConnection, err := url.Parse(connection.URI)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("preparing connection to remote machine: %w", err)
|
||||
}
|
||||
|
||||
// If the default connection does not point to a `podman
|
||||
// machine`, we cannot use a local path and need to use SSH.
|
||||
if !connection.IsMachine {
|
||||
// Compose doesn't like paths, so we optimistically
|
||||
// assume the presence of a Docker socket on the remote
|
||||
// machine which is the case for podman machines.
|
||||
return strings.TrimSuffix(connection.URI, parsedConnection.Path), nil
|
||||
}
|
||||
|
||||
machineProvider, err := cmdMachine.GetSystemProvider()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting machine provider: %w", err)
|
||||
}
|
||||
machineList, err := machineProvider.List(machine.ListOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("listing machines: %w", err)
|
||||
}
|
||||
|
||||
// Now we know that the connection points to a machine and we
|
||||
// can find the machine by looking for the one with the
|
||||
// matching port.
|
||||
connectionPort, err := strconv.Atoi(parsedConnection.Port())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing connection port: %w", err)
|
||||
}
|
||||
for _, item := range machineList {
|
||||
if connectionPort != item.Port {
|
||||
continue
|
||||
}
|
||||
|
||||
vm, err := machineProvider.LoadVMByName(item.Name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("loading machine: %w", err)
|
||||
}
|
||||
info, err := vm.Inspect()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("inspecting machine: %w", err)
|
||||
}
|
||||
if info.ConnectionInfo.PodmanSocket == nil {
|
||||
return "", errors.New("socket of machine is not set")
|
||||
}
|
||||
if info.State != machine.Running {
|
||||
return "", fmt.Errorf("machine %s is not running but in state %s", item.Name, info.State)
|
||||
}
|
||||
return "unix://" + info.ConnectionInfo.PodmanSocket.Path, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not find a matching machine for connection %q", connection.URI)
|
||||
}
|
||||
|
||||
// composeEnv returns the compose-specific environment variables.
|
||||
func composeEnv() ([]string, error) {
|
||||
hostValue, err := composeDockerHost()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []string{
|
||||
"DOCKER_HOST=" + hostValue,
|
||||
// Podman doesn't support all buildkit features and since it's
|
||||
// a continuous catch-up game, disable buildkit on the client
|
||||
// side.
|
||||
//
|
||||
// See https://github.com/containers/podman/issues/18617#issuecomment-1600495841
|
||||
"DOCKER_BUILDKIT=0",
|
||||
// FIXME: DOCKER_CONFIG is limited by containers/podman/issues/18617
|
||||
// and it remains unclear which default path should be set
|
||||
// w.r.t. Docker compatibility and a smooth experience of podman-login
|
||||
// working with podman-compose _by default_.
|
||||
"DOCKER_CONFIG=" + os.Getenv("DOCKER_CONFIG"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// underline uses ANSI codes to underline the specified string.
|
||||
func underline(str string) string {
|
||||
return "\033[4m" + str + "\033[0m"
|
||||
}
|
||||
|
||||
// composeProviderExec executes the compose provider with the specified arguments.
|
||||
func composeProviderExec(args []string, stdout io.Writer, stderr io.Writer, warn bool) error {
|
||||
provider, err := composeProvider()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env, err := composeEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stdout == nil {
|
||||
stdout = os.Stdout
|
||||
}
|
||||
if stderr == nil {
|
||||
stderr = os.Stderr
|
||||
}
|
||||
|
||||
cmd := exec.Command(provider, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.Env = append(os.Environ(), env...)
|
||||
logrus.Debugf("Executing compose provider (%s %s) with additional env %s", provider, strings.Join(args, " "), strings.Join(env, " "))
|
||||
|
||||
if warn {
|
||||
fmt.Fprint(os.Stderr, underline(fmt.Sprintf(">>>> Executing external compose provider %q. Please refer to the documentation for details. <<<<\n\n", provider)))
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// Make sure podman returns with the same exit code as the compose provider.
|
||||
if exitErr, isExit := err.(*exec.ExitError); isExit {
|
||||
registry.SetExitCode(exitErr.ExitCode())
|
||||
}
|
||||
// Format the error to make it explicit that error did not come
|
||||
// from podman but from the executed compose provider.
|
||||
return fmt.Errorf("executing %s %s: %w", provider, strings.Join(args, " "), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// composeHelp is a custom help function to display the help message of the
|
||||
// configured compose-provider.
|
||||
func composeHelp(cmd *cobra.Command) error {
|
||||
tmpl, err := template.New("help_template").Parse(helpTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tmpl.Execute(os.Stdout, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return composeProviderExec([]string{"--help"}, nil, nil, registry.PodmanConfig().ContainersConfDefaultsRO.Engine.ComposeWarningLogs)
|
||||
}
|
||||
|
||||
// composeMain is the main function of the compose command.
|
||||
func composeMain(cmd *cobra.Command, args []string) error {
|
||||
// We have to manually parse the flags here to make sure all arguments
|
||||
// after `podman compose [ARGS]` are passed to the compose provider.
|
||||
// For now, we only look for the --help flag.
|
||||
fs := pflag.NewFlagSet("args", pflag.ContinueOnError)
|
||||
fs.ParseErrorsWhitelist.UnknownFlags = true
|
||||
fs.SetInterspersed(false)
|
||||
fs.BoolP("help", "h", false, "")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return fmt.Errorf("parsing arguments: %w", err)
|
||||
}
|
||||
|
||||
if len(args) == 0 || fs.Lookup("help").Changed {
|
||||
return composeHelp(cmd)
|
||||
}
|
||||
|
||||
return composeProviderExec(args, nil, nil, registry.PodmanConfig().ContainersConfDefaultsRO.Engine.ComposeWarningLogs)
|
||||
}
|
1
docs/source/markdown/.gitignore
vendored
1
docs/source/markdown/.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
podman-attach.1.md
|
||||
podman-auto-update.1.md
|
||||
podman-build.1.md
|
||||
podman-compose.1.md
|
||||
podman-container-clone.1.md
|
||||
podman-container-diff.1.md
|
||||
podman-container-inspect.1.md
|
||||
|
21
docs/source/markdown/podman-compose.1.md.in
Normal file
21
docs/source/markdown/podman-compose.1.md.in
Normal file
@ -0,0 +1,21 @@
|
||||
% podman-compose 1
|
||||
|
||||
## NAME
|
||||
podman\-compose - Run Compose workloads via an external compose provider
|
||||
|
||||
## SYNOPSIS
|
||||
**podman compose** [*options*] [*command* [*arg* ...]]
|
||||
|
||||
## DESCRIPTION
|
||||
**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.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
To see supported options of the installed compose provider, please run `podman compose --help`.
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**
|
@ -315,6 +315,7 @@ the exit codes follow the `chroot` standard, see below:
|
||||
| [podman-build(1)](podman-build.1.md) | Build a container image using a Containerfile. |
|
||||
| [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. |
|
||||
| [podman-completion(1)](podman-completion.1.md) | Generate shell completion scripts |
|
||||
| [podman-compose(1)](podman-compose.1.md) | Run Compose workloads via an external compose provider. |
|
||||
| [podman-container(1)](podman-container.1.md) | Manage containers. |
|
||||
| [podman-cp(1)](podman-cp.1.md) | Copy files/folders between a container and the local filesystem. |
|
||||
| [podman-create(1)](podman-create.1.md) | Create a new container. |
|
||||
|
4
go.mod
4
go.mod
@ -13,7 +13,7 @@ require (
|
||||
github.com/containernetworking/cni v1.1.2
|
||||
github.com/containernetworking/plugins v1.3.0
|
||||
github.com/containers/buildah v1.31.1-0.20230722114901-5ece066f82c6
|
||||
github.com/containers/common v0.55.1-0.20230721175448-664d013a6ae2
|
||||
github.com/containers/common v0.55.1-0.20230724161016-2966c705a7a3
|
||||
github.com/containers/conmon v2.0.20+incompatible
|
||||
github.com/containers/image/v5 v5.26.1-0.20230721194716-30c87d4a5b8d
|
||||
github.com/containers/libhvee v0.4.0
|
||||
@ -48,7 +48,7 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0-rc4
|
||||
github.com/opencontainers/runc v1.1.8
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.3
|
||||
github.com/opencontainers/runtime-spec v1.1.0
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69
|
||||
github.com/opencontainers/selinux v1.11.0
|
||||
github.com/openshift/imagebuilder v1.2.5
|
||||
|
8
go.sum
8
go.sum
@ -247,8 +247,8 @@ github.com/containernetworking/plugins v1.3.0 h1:QVNXMT6XloyMUoO2wUOqWTC1hWFV62Q
|
||||
github.com/containernetworking/plugins v1.3.0/go.mod h1:Pc2wcedTQQCVuROOOaLBPPxrEXqqXBFt3cZ+/yVg6l0=
|
||||
github.com/containers/buildah v1.31.1-0.20230722114901-5ece066f82c6 h1:K/S8SFQsnnNTF0Ws58SrBD9L0EuClzAG8Zp08d7+6AA=
|
||||
github.com/containers/buildah v1.31.1-0.20230722114901-5ece066f82c6/go.mod h1:0sptTFBBtSznLqoTh80DfvMOCNbdRsNRgVOKhBhrupA=
|
||||
github.com/containers/common v0.55.1-0.20230721175448-664d013a6ae2 h1:4B42HUIAghFGSqej5RADTNf0WlOBFiGGzmGjNa3Do78=
|
||||
github.com/containers/common v0.55.1-0.20230721175448-664d013a6ae2/go.mod h1:O/JSRY1dLfwgBxVvn3yJfKvF63KEjbNJcJAtjpNvO90=
|
||||
github.com/containers/common v0.55.1-0.20230724161016-2966c705a7a3 h1:0fHDAdLNfOs5AuBizE7TOECDA4gCLoCgE6geR3k/H78=
|
||||
github.com/containers/common v0.55.1-0.20230724161016-2966c705a7a3/go.mod h1:SUX+gHoElocPp664K79AEt+GGHwngBJLrKdwNIRW0tQ=
|
||||
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
|
||||
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
|
||||
github.com/containers/image/v5 v5.26.1-0.20230721194716-30c87d4a5b8d h1:g6DFcXXEMd1OwSVtbrUolGzmkMNyQDyc4OKHOFxbNeE=
|
||||
@ -820,8 +820,8 @@ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.m
|
||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.3 h1:l04uafi6kxByhbxev7OWiuUv0LZxEsYUfDWZ6bztAuU=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.3/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
|
||||
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69 h1:NL4xDvl68WWqQ+8WPMM3l5PsZTxaT7Z4K3VSKDRuAGs=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69/go.mod h1:bNpfuSHA3DZRtD0TPWO8LzgtLpFPTVA/3jDkzD/OPyk=
|
||||
|
@ -78,6 +78,11 @@ function compare_usage() {
|
||||
local cmd="$1"
|
||||
local from_man="$2"
|
||||
|
||||
# Special case: this is an external call to docker
|
||||
if [[ $cmd = "podman compose" ]] || [[ $cmd = "podmansh" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Sometimes in CI we run before podman gets built.
|
||||
test -x ../../../bin/podman || return
|
||||
|
||||
|
@ -95,6 +95,13 @@ my %Format_Option_Is_Special = map { $_ => 1 } (
|
||||
'inspect', # ambiguous (container/image)
|
||||
);
|
||||
|
||||
# Do not cross-reference these.
|
||||
my %Skip_Subcommand = map { $_ => 1 } (
|
||||
"help", # has no man page
|
||||
"completion", # internal (hidden) subcommand
|
||||
"compose", # external tool, outside of our control
|
||||
);
|
||||
|
||||
# END user-customizable section
|
||||
###############################################################################
|
||||
|
||||
@ -279,8 +286,8 @@ sub xref_by_man {
|
||||
|
||||
next if "@subcommand" eq 'system' && $k eq 'service';
|
||||
|
||||
# Special case: podman completion is a hidden command
|
||||
next if $k eq 'completion';
|
||||
# Special case for hidden or external commands
|
||||
next if $Skip_Subcommand{$k};
|
||||
|
||||
warn "$ME: 'podman @subcommand': $k in $man, but not --help\n";
|
||||
++$Errs;
|
||||
@ -346,7 +353,7 @@ sub podman_help {
|
||||
}
|
||||
|
||||
$help{$subcommand} = podman_help(@_, $subcommand)
|
||||
unless $subcommand eq 'help'; # 'help' not in man
|
||||
unless $Skip_Subcommand{$subcommand};
|
||||
}
|
||||
}
|
||||
elsif ($section eq 'options') {
|
||||
|
@ -387,6 +387,7 @@ can_use_shortcut (char **argv)
|
||||
|| strcmp (argv[argc], "version") == 0
|
||||
|| strcmp (argv[argc], "context") == 0
|
||||
|| strcmp (argv[argc], "search") == 0
|
||||
|| strcmp (argv[argc], "compose") == 0
|
||||
|| (strcmp (argv[argc], "system") == 0 && argv[argc+1] && strcmp (argv[argc+1], "service") != 0))
|
||||
{
|
||||
ret = false;
|
||||
|
@ -276,11 +276,12 @@ done
|
||||
# When rootless use a socket path accessible by the rootless user
|
||||
if is_rootless; then
|
||||
DOCKER_SOCK="$WORKDIR/docker.sock"
|
||||
DOCKER_HOST="unix://$DOCKER_SOCK"
|
||||
# export DOCKER_HOST docker-compose will use it
|
||||
export DOCKER_HOST
|
||||
fi
|
||||
|
||||
# export DOCKER_HOST docker-compose will use it
|
||||
DOCKER_HOST="unix://$DOCKER_SOCK"
|
||||
export DOCKER_HOST
|
||||
|
||||
# Identify the tests to run. If called with args, use those as globs.
|
||||
tests_to_run=()
|
||||
if [ -n "$*" ]; then
|
||||
@ -331,12 +332,12 @@ for t in "${tests_to_run[@]}"; do
|
||||
trap '. teardown.sh' 0
|
||||
fi
|
||||
|
||||
docker-compose up -d &> $logfile
|
||||
podman compose up -d &> $logfile
|
||||
docker_compose_rc=$?
|
||||
if [[ $docker_compose_rc -ne 0 ]]; then
|
||||
_show_ok 0 "$testname - up" "[ok]" "status=$docker_compose_rc"
|
||||
sed -e 's/^/# /' <$logfile
|
||||
docker-compose down >>$logfile 2>&1 # No status check here
|
||||
podman compose down >>$logfile 2>&1 # No status check here
|
||||
exit 1
|
||||
fi
|
||||
_show_ok 1 "$testname - up"
|
||||
@ -354,7 +355,7 @@ for t in "${tests_to_run[@]}"; do
|
||||
fi
|
||||
|
||||
# Done. Clean up.
|
||||
docker-compose down &>> $logfile
|
||||
podman compose down &>> $logfile
|
||||
rc=$?
|
||||
if [[ $rc -eq 0 ]]; then
|
||||
_show_ok 1 "$testname - down"
|
||||
|
@ -17,6 +17,11 @@ function check_help() {
|
||||
local -A found
|
||||
|
||||
for cmd in $(_podman_commands "$@"); do
|
||||
# Skip the compose command which is calling `docker-compose --help`
|
||||
# and hence won't match the assumptions made below.
|
||||
if [[ "$cmd" == "compose" ]]; then
|
||||
continue
|
||||
fi
|
||||
# Human-readable podman command string, with multiple spaces collapsed
|
||||
command_string="podman $* $cmd"
|
||||
command_string=${command_string// / } # 'podman x' -> 'podman x'
|
||||
|
@ -37,6 +37,11 @@ function check_shell_completion() {
|
||||
"
|
||||
|
||||
for cmd in $(_podman_commands "$@"); do
|
||||
# Skip the compose command which is calling `docker-compose --help`
|
||||
# and hence won't match the assumptions made below.
|
||||
if [[ "$cmd" == "compose" ]]; then
|
||||
continue
|
||||
fi
|
||||
# Human-readable podman command string, with multiple spaces collapsed
|
||||
name="podman"
|
||||
if is_remote; then
|
||||
|
@ -55,6 +55,12 @@ can_run_stats=
|
||||
# > run the command with --format '{{"\n"}}' and make sure it passes
|
||||
function check_subcommand() {
|
||||
for cmd in $(_podman_commands "$@"); do
|
||||
# Skip the compose command which is calling `docker-compose --help`
|
||||
# and hence won't match the assumptions made below.
|
||||
if [[ "$cmd" == "compose" ]]; then
|
||||
continue
|
||||
fi
|
||||
# Human-readable podman command string, with multiple spaces collapsed
|
||||
# Special case: 'podman machine' can only be run under ideal conditions
|
||||
if [[ "$cmd" = "machine" ]] && [[ -z "$can_run_podman_machine" ]]; then
|
||||
continue
|
||||
|
67
test/system/850-compose.bats
Normal file
67
test/system/850-compose.bats
Normal file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bats -*- bats -*-
|
||||
#
|
||||
# Smoke tests for the podman-compose command. test/compose takes care of functional tests.
|
||||
#
|
||||
|
||||
load helpers
|
||||
|
||||
@test "podman compose - smoke tests" {
|
||||
fake_compose_bin="$PODMAN_TMPDIR/fake_compose"
|
||||
cat >$fake_compose_bin <<EOF
|
||||
#!/bin/bash
|
||||
if [[ "\$@" == "fail" ]]; then
|
||||
exit 42
|
||||
fi
|
||||
if [[ "\$@" == "env" ]]; then
|
||||
printenv DOCKER_HOST DOCKER_BUILDKIT DOCKER_CONFIG
|
||||
exit 0
|
||||
fi
|
||||
echo "arguments: \$@"
|
||||
EOF
|
||||
|
||||
compose_conf="$PODMAN_TMPDIR/compose.conf"
|
||||
cat >$compose_conf <<EOF
|
||||
[engine]
|
||||
compose_providers = ["$fake_compose_bin"]
|
||||
compose_warning_logs = false
|
||||
EOF
|
||||
|
||||
random_data="--foo=bar --random=$(random_string 15) -f /path/to/file ignore me"
|
||||
|
||||
# Make sure that the fake compose binary is used and that error reporting works
|
||||
PODMAN_COMPOSE_PROVIDER=$fake_compose_bin run_podman 125 compose --help
|
||||
is "$output" ".*executing $fake_compose_bin --help: .*: permission denied"
|
||||
|
||||
# Make the fake one executable and check the --help output
|
||||
chmod +x $fake_compose_bin
|
||||
PODMAN_COMPOSE_PROVIDER=$fake_compose_bin run_podman compose --help
|
||||
is "$output" "Run compose workloads via an external provider .*arguments: --help"
|
||||
|
||||
# No argument yields the help message as well
|
||||
PODMAN_COMPOSE_PROVIDER=$fake_compose_bin run_podman compose
|
||||
is "$output" "Run compose workloads via an external provider .*arguments: "
|
||||
|
||||
# Make sure that the provider can be specified via containers.conf and that
|
||||
# the warning logs can be turned off
|
||||
CONTAINERS_CONF_OVERRIDE=$compose_conf run_podman compose --help
|
||||
is "$output" "Run compose workloads via an external provider .*arguments: --help"
|
||||
assert "$output" !~ ".*Executing external compose provider.*"
|
||||
|
||||
# Run with bogus arguments and make sure they're being returned
|
||||
CONTAINERS_CONF_OVERRIDE=$compose_conf run_podman compose $random_data
|
||||
is "$output" "arguments: $random_data"
|
||||
|
||||
# Make sure Podman returns the exit code of the compose provider
|
||||
CONTAINERS_CONF_OVERRIDE=$compose_conf run_podman 42 compose fail
|
||||
|
||||
# Make sure the three env variables are set (and parsed)
|
||||
CONTAINERS_CONF_OVERRIDE=$compose_conf run_podman compose env
|
||||
is "${lines[0]}" ".*/podman.sock"
|
||||
is "${lines[1]}" "0"
|
||||
is "${lines[2]}" ""
|
||||
|
||||
DOCKER_HOST="$random_data" DOCKER_CONFIG="$random_data" CONTAINERS_CONF_OVERRIDE=$compose_conf run_podman compose env
|
||||
is "${lines[0]}" "$random_data"
|
||||
is "${lines[1]}" "0"
|
||||
is "${lines[2]}" "$random_data"
|
||||
}
|
11
vendor/github.com/containers/common/pkg/config/config.go
generated
vendored
11
vendor/github.com/containers/common/pkg/config/config.go
generated
vendored
@ -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"`
|
||||
|
||||
|
4
vendor/github.com/containers/common/pkg/config/config_local.go
generated
vendored
4
vendor/github.com/containers/common/pkg/config/config_local.go
generated
vendored
@ -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)
|
||||
|
14
vendor/github.com/containers/common/pkg/config/containers.conf
generated
vendored
14
vendor/github.com/containers/common/pkg/config/containers.conf
generated
vendored
@ -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`.
|
||||
#
|
||||
|
2
vendor/github.com/containers/common/pkg/config/containers.conf-freebsd
generated
vendored
2
vendor/github.com/containers/common/pkg/config/containers.conf-freebsd
generated
vendored
@ -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.
|
||||
#
|
||||
|
13
vendor/github.com/containers/common/pkg/config/default.go
generated
vendored
13
vendor/github.com/containers/common/pkg/config/default.go
generated
vendored
@ -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"}
|
||||
|
11
vendor/github.com/containers/common/pkg/config/default_darwin.go
generated
vendored
11
vendor/github.com/containers/common/pkg/config/default_darwin.go
generated
vendored
@ -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",
|
||||
}
|
||||
}
|
||||
|
4
vendor/github.com/containers/common/pkg/config/default_freebsd.go
generated
vendored
4
vendor/github.com/containers/common/pkg/config/default_freebsd.go
generated
vendored
@ -26,3 +26,7 @@ func getLibpodTmpDir() string {
|
||||
func getDefaultMachineVolumes() []string {
|
||||
return []string{"$HOME:$HOME"}
|
||||
}
|
||||
|
||||
func getDefaultComposeProviders() []string {
|
||||
return defaultUnixComposeProviders
|
||||
}
|
||||
|
4
vendor/github.com/containers/common/pkg/config/default_linux.go
generated
vendored
4
vendor/github.com/containers/common/pkg/config/default_linux.go
generated
vendored
@ -74,3 +74,7 @@ func getLibpodTmpDir() string {
|
||||
func getDefaultMachineVolumes() []string {
|
||||
return []string{"$HOME:$HOME"}
|
||||
}
|
||||
|
||||
func getDefaultComposeProviders() []string {
|
||||
return defaultUnixComposeProviders
|
||||
}
|
||||
|
5
vendor/github.com/containers/common/pkg/config/default_windows.go
generated
vendored
5
vendor/github.com/containers/common/pkg/config/default_windows.go
generated
vendored
@ -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"}
|
||||
}
|
||||
|
18
vendor/github.com/containers/common/pkg/util/util.go
generated
vendored
18
vendor/github.com/containers/common/pkg/util/util.go
generated
vendored
@ -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)
|
||||
|
2
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
2
vendor/github.com/opencontainers/runtime-spec/specs-go/version.go
generated
vendored
@ -11,7 +11,7 @@ const (
|
||||
VersionPatch = 0
|
||||
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev = "-rc.3"
|
||||
VersionDev = ""
|
||||
)
|
||||
|
||||
// Version is the specification version that the package types support.
|
||||
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -157,7 +157,7 @@ github.com/containers/buildah/pkg/rusage
|
||||
github.com/containers/buildah/pkg/sshagent
|
||||
github.com/containers/buildah/pkg/util
|
||||
github.com/containers/buildah/util
|
||||
# github.com/containers/common v0.55.1-0.20230721175448-664d013a6ae2
|
||||
# github.com/containers/common v0.55.1-0.20230724161016-2966c705a7a3
|
||||
## explicit; go 1.18
|
||||
github.com/containers/common/libimage
|
||||
github.com/containers/common/libimage/define
|
||||
@ -815,7 +815,7 @@ github.com/opencontainers/runc/libcontainer/devices
|
||||
github.com/opencontainers/runc/libcontainer/user
|
||||
github.com/opencontainers/runc/libcontainer/userns
|
||||
github.com/opencontainers/runc/libcontainer/utils
|
||||
# github.com/opencontainers/runtime-spec v1.1.0-rc.3
|
||||
# github.com/opencontainers/runtime-spec v1.1.0
|
||||
## explicit
|
||||
github.com/opencontainers/runtime-spec/specs-go
|
||||
# github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69
|
||||
|
Reference in New Issue
Block a user