summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/containers.go20
-rw-r--r--pkg/adapter/containers_remote.go6
-rw-r--r--pkg/adapter/errors.go2
-rw-r--r--pkg/adapter/images_remote.go2
-rw-r--r--pkg/adapter/info_remote.go4
-rw-r--r--pkg/adapter/network.go6
-rw-r--r--pkg/adapter/pods.go13
-rw-r--r--pkg/adapter/pods_remote.go2
-rw-r--r--pkg/adapter/reset_remote.go2
-rw-r--r--pkg/adapter/runtime.go35
-rw-r--r--pkg/adapter/runtime_remote.go52
-rw-r--r--pkg/adapter/terminal_linux.go5
-rw-r--r--pkg/adapter/terminal_unsupported.go2
-rw-r--r--pkg/api/handlers/compat/containers_create.go13
-rw-r--r--pkg/api/handlers/compat/containers_export.go42
-rw-r--r--pkg/api/handlers/compat/exec.go107
-rw-r--r--pkg/api/handlers/compat/images.go7
-rw-r--r--pkg/api/handlers/compat/images_push.go80
-rw-r--r--pkg/api/handlers/compat/images_remove.go22
-rw-r--r--pkg/api/handlers/compat/info.go8
-rw-r--r--pkg/api/handlers/libpod/containers.go131
-rw-r--r--pkg/api/handlers/libpod/containers_create.go7
-rw-r--r--pkg/api/handlers/libpod/images.go226
-rw-r--r--pkg/api/handlers/libpod/manifests.go6
-rw-r--r--pkg/api/handlers/libpod/networks.go2
-rw-r--r--pkg/api/handlers/libpod/pods.go232
-rw-r--r--pkg/api/handlers/libpod/swagger.go50
-rw-r--r--pkg/api/handlers/libpod/volumes.go9
-rw-r--r--pkg/api/handlers/swagger.go23
-rw-r--r--pkg/api/handlers/types.go16
-rw-r--r--pkg/api/handlers/utils/handler.go7
-rw-r--r--pkg/api/handlers/utils/images.go41
-rw-r--r--pkg/api/handlers/utils/pods.go54
-rw-r--r--pkg/api/server/register_containers.go144
-rw-r--r--pkg/api/server/register_exec.go16
-rw-r--r--pkg/api/server/register_images.go108
-rw-r--r--pkg/api/server/register_pods.go61
-rw-r--r--pkg/api/server/swagger.go3
-rw-r--r--pkg/apparmor/apparmor.go5
-rw-r--r--pkg/bindings/containers/checkpoint.go79
-rw-r--r--pkg/bindings/containers/containers.go62
-rw-r--r--pkg/bindings/containers/exec.go71
-rw-r--r--pkg/bindings/containers/healthcheck.go6
-rw-r--r--pkg/bindings/images/images.go120
-rw-r--r--pkg/bindings/pods/pods.go153
-rw-r--r--pkg/bindings/test/containers_test.go34
-rw-r--r--pkg/bindings/test/exec_test.go77
-rw-r--r--pkg/bindings/test/images_test.go65
-rw-r--r--pkg/bindings/test/pods_test.go95
-rw-r--r--pkg/checkpoint/checkpoint_restore.go (renamed from pkg/adapter/checkpoint_restore.go)10
-rw-r--r--pkg/domain/entities/containers.go126
-rw-r--r--pkg/domain/entities/engine.go4
-rw-r--r--pkg/domain/entities/engine_container.go33
-rw-r--r--pkg/domain/entities/engine_image.go11
-rw-r--r--pkg/domain/entities/healthcheck.go3
-rw-r--r--pkg/domain/entities/images.go143
-rw-r--r--pkg/domain/entities/pods.go178
-rw-r--r--pkg/domain/entities/types.go34
-rw-r--r--pkg/domain/infra/abi/containers.go394
-rw-r--r--pkg/domain/infra/abi/healthcheck.go26
-rw-r--r--pkg/domain/infra/abi/images.go343
-rw-r--r--pkg/domain/infra/abi/pods.go337
-rw-r--r--pkg/domain/infra/abi/terminal/sigproxy_linux.go47
-rw-r--r--pkg/domain/infra/abi/terminal/terminal.go103
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go123
-rw-r--r--pkg/domain/infra/abi/volumes.go19
-rw-r--r--pkg/domain/infra/runtime_libpod.go3
-rw-r--r--pkg/domain/infra/tunnel/containers.go179
-rw-r--r--pkg/domain/infra/tunnel/healthcheck.go13
-rw-r--r--pkg/domain/infra/tunnel/helpers.go34
-rw-r--r--pkg/domain/infra/tunnel/images.go201
-rw-r--r--pkg/domain/infra/tunnel/pods.go196
-rw-r--r--pkg/domain/infra/tunnel/system.go1
-rw-r--r--pkg/namespaces/namespaces.go6
-rw-r--r--pkg/spec/createconfig.go23
-rw-r--r--pkg/spec/spec.go70
-rw-r--r--pkg/spec/storage.go80
-rw-r--r--pkg/specgen/config_linux.go93
-rw-r--r--pkg/specgen/config_linux_cgo.go1
-rw-r--r--pkg/specgen/container_validate.go (renamed from pkg/specgen/validate.go)20
-rw-r--r--pkg/specgen/generate/container.go168
-rw-r--r--pkg/specgen/generate/container_create.go (renamed from pkg/specgen/create.go)44
-rw-r--r--pkg/specgen/namespaces.go7
-rw-r--r--pkg/specgen/oci.go6
-rw-r--r--pkg/specgen/pod_create.go83
-rw-r--r--pkg/specgen/pod_validate.go104
-rw-r--r--pkg/specgen/podspecgen.go (renamed from pkg/specgen/pod.go)13
-rw-r--r--pkg/specgen/security.go165
-rw-r--r--pkg/specgen/specgen.go19
-rw-r--r--pkg/specgen/storage.go885
-rw-r--r--pkg/systemd/generate/systemdgen.go2
-rw-r--r--pkg/util/mountOpts.go24
-rw-r--r--pkg/util/mountOpts_linux.go23
-rw-r--r--pkg/util/mountOpts_other.go7
-rw-r--r--pkg/util/utils.go43
-rw-r--r--pkg/util/utils_test.go12
-rw-r--r--pkg/varlink/generate.go3
-rw-r--r--pkg/varlink/io.podman.varlink1398
-rw-r--r--pkg/varlinkapi/attach.go12
-rw-r--r--pkg/varlinkapi/config.go2
-rw-r--r--pkg/varlinkapi/containers.go4
-rw-r--r--pkg/varlinkapi/containers_create.go2
-rw-r--r--pkg/varlinkapi/events.go2
-rw-r--r--pkg/varlinkapi/generate.go2
-rw-r--r--pkg/varlinkapi/images.go28
-rw-r--r--pkg/varlinkapi/mount.go4
-rw-r--r--pkg/varlinkapi/pods.go2
-rw-r--r--pkg/varlinkapi/remote_client.go2
-rw-r--r--pkg/varlinkapi/system.go2
-rw-r--r--pkg/varlinkapi/transfers.go2
-rw-r--r--pkg/varlinkapi/util.go2
-rw-r--r--pkg/varlinkapi/volumes.go10
112 files changed, 7608 insertions, 661 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 0d2ca1a64..ecadbd2f9 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -16,6 +16,7 @@ import (
"time"
"github.com/containers/buildah"
+ cfg "github.com/containers/common/pkg/config"
"github.com/containers/image/v5/manifest"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
@@ -25,6 +26,7 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/adapter/shortcuts"
+ "github.com/containers/libpod/pkg/checkpoint"
envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/systemd/generate"
"github.com/containers/storage"
@@ -380,11 +382,11 @@ func (r *LocalRuntime) selectDetachKeys(flagValue string) (string, error) {
if err != nil {
return "", errors.Wrapf(err, "unable to retrieve runtime config")
}
- if config.DetachKeys != "" {
- return config.DetachKeys, nil
+ if config.Engine.DetachKeys != "" {
+ return config.Engine.DetachKeys, nil
}
- return define.DefaultDetachKeys, nil
+ return cfg.DefaultDetachKeys, nil
}
// Run a libpod container
@@ -624,7 +626,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
switch {
case c.Import != "":
- containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
+ containers, err = checkpoint.CRImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
case c.All:
containers, err = r.GetContainers(filterFuncs...)
default:
@@ -1002,7 +1004,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
}
env = envLib.Join(env, cliEnv)
- streams := new(libpod.AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = os.Stdout
streams.ErrorStream = os.Stderr
if cli.Interactive {
@@ -1212,8 +1214,8 @@ func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSyst
return nil, false, err
}
- timeout := int(ctr.StopTimeout())
- if c.StopTimeout >= 0 {
+ timeout := ctr.StopTimeout()
+ if c.Flags().Changed("timeout") || c.Flags().Changed("time") {
timeout = c.StopTimeout
}
@@ -1369,9 +1371,9 @@ func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, co
return "", err
}
- sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
coptions := buildah.CommitOptions{
- SignaturePolicyPath: rtc.SignaturePolicyPath,
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
ReportWriter: writer,
SystemContext: sc,
PreferredManifestType: mimeType,
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 32a84b60d..fc8b524d6 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -15,11 +15,11 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
envLib "github.com/containers/libpod/pkg/env"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/libpod/pkg/varlinkapi/virtwriter"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/docker/pkg/term"
@@ -32,12 +32,12 @@ import (
)
// Inspect returns an inspect struct from varlink
-func (c *Container) Inspect(size bool) (*libpod.InspectContainerData, error) {
+func (c *Container) Inspect(size bool) (*define.InspectContainerData, error) {
reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID(), size)
if err != nil {
return nil, err
}
- data := libpod.InspectContainerData{}
+ data := define.InspectContainerData{}
if err := json.Unmarshal([]byte(reply), &data); err != nil {
return nil, err
}
diff --git a/pkg/adapter/errors.go b/pkg/adapter/errors.go
index ede3d4b1a..012d01d39 100644
--- a/pkg/adapter/errors.go
+++ b/pkg/adapter/errors.go
@@ -3,8 +3,8 @@
package adapter
import (
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod/define"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/pkg/errors"
)
diff --git a/pkg/adapter/images_remote.go b/pkg/adapter/images_remote.go
index e7b38dccc..2df0ffcde 100644
--- a/pkg/adapter/images_remote.go
+++ b/pkg/adapter/images_remote.go
@@ -6,8 +6,8 @@ import (
"context"
"encoding/json"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/pkg/inspect"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// Inspect returns returns an ImageData struct from over a varlink connection
diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go
index c55d1f6ef..0e8fb06d1 100644
--- a/pkg/adapter/info_remote.go
+++ b/pkg/adapter/info_remote.go
@@ -4,9 +4,9 @@ package adapter
import (
"encoding/json"
- "github.com/containers/libpod/libpod/define"
- "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod/define"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// Info returns information for the host system and its components
diff --git a/pkg/adapter/network.go b/pkg/adapter/network.go
index b25f54a13..577ffe19f 100644
--- a/pkg/adapter/network.go
+++ b/pkg/adapter/network.go
@@ -23,9 +23,9 @@ func getCNIConfDir(r *LocalRuntime) (string, error) {
if err != nil {
return "", err
}
- configPath := config.CNIConfigDir
+ configPath := config.Network.NetworkConfigDir
- if len(config.CNIConfigDir) < 1 {
+ if len(config.Network.NetworkConfigDir) < 1 {
configPath = network.CNIConfigDir
}
return configPath, nil
@@ -211,7 +211,7 @@ func (r *LocalRuntime) NetworkCreateBridge(cli *cliconfig.NetworkCreateValues) (
plugins = append(plugins, network.NewPortMapPlugin())
plugins = append(plugins, network.NewFirewallPlugin())
// if we find the dnsname plugin, we add configuration for it
- if network.HasDNSNamePlugin(runtimeConfig.CNIPluginDir) && !cli.DisableDNS {
+ if network.HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !cli.DisableDNS {
// Note: in the future we might like to allow for dynamic domain names
plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 64ebd3954..7c2a84cc7 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -784,6 +784,12 @@ func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
var infraPorts []ocicni.PortMapping
for _, container := range containers {
for _, p := range container.Ports {
+ if p.HostPort != 0 && p.ContainerPort == 0 {
+ p.ContainerPort = p.HostPort
+ }
+ if p.Protocol == "" {
+ p.Protocol = "tcp"
+ }
portBinding := ocicni.PortMapping{
HostPort: p.HostPort,
ContainerPort: p.ContainerPort,
@@ -792,7 +798,12 @@ func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
if p.HostIP != "" {
logrus.Debug("HostIP on port bindings is not supported")
}
- infraPorts = append(infraPorts, portBinding)
+ // only hostPort is utilized in podman context, all container ports
+ // are accessible inside the shared network namespace
+ if p.HostPort != 0 {
+ infraPorts = append(infraPorts, portBinding)
+ }
+
}
}
return infraPorts
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
index 6b8f22f15..ebd10a92a 100644
--- a/pkg/adapter/pods_remote.go
+++ b/pkg/adapter/pods_remote.go
@@ -10,9 +10,9 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/libpod/pkg/varlinkapi"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
diff --git a/pkg/adapter/reset_remote.go b/pkg/adapter/reset_remote.go
index 663fab639..284b54a17 100644
--- a/pkg/adapter/reset_remote.go
+++ b/pkg/adapter/reset_remote.go
@@ -3,7 +3,7 @@
package adapter
import (
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// Info returns information for the host system and its components
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 7817a1f98..7a181e7e5 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
"github.com/containers/buildah/pkg/formats"
- "github.com/containers/buildah/pkg/parse"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
@@ -296,37 +295,13 @@ func libpodVolumeToVolume(volumes []*libpod.Volume) []*Volume {
// Build is the wrapper to build images
func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, options imagebuildah.BuildOptions, dockerfiles []string) (string, reference.Canonical, error) {
- namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c.PodmanCommand.Command)
- if err != nil {
- return "", nil, errors.Wrapf(err, "error parsing namespace-related options")
- }
- usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command, options.Isolation)
- if err != nil {
- return "", nil, errors.Wrapf(err, "error parsing ID mapping options")
- }
- namespaceOptions.AddOrReplace(usernsOption...)
-
- systemContext, err := parse.SystemContextFromOptions(c.PodmanCommand.Command)
- if err != nil {
- return "", nil, errors.Wrapf(err, "error building system context")
- }
authfile := c.Authfile
if len(c.Authfile) == 0 {
authfile = os.Getenv("REGISTRY_AUTH_FILE")
}
- systemContext.AuthFilePath = authfile
- commonOpts, err := parse.CommonBuildOptions(c.PodmanCommand.Command)
- if err != nil {
- return "", nil, err
- }
-
- options.NamespaceOptions = namespaceOptions
- options.ConfigureNetwork = networkPolicy
- options.IDMappingOptions = idmappingOptions
- options.CommonBuildOpts = commonOpts
- options.SystemContext = systemContext
+ options.SystemContext.AuthFilePath = authfile
if c.GlobalFlags.Runtime != "" {
options.Runtime = c.GlobalFlags.Runtime
@@ -356,11 +331,11 @@ func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
errs = append(errs, err)
return vids, errs
}
- for _, r := range reports {
- if r.Err == nil {
- vids = append(vids, r.Id)
+ for k, v := range reports {
+ if v == nil {
+ vids = append(vids, k)
} else {
- errs = append(errs, r.Err)
+ errs = append(errs, v)
}
}
return vids, errs
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index fc396eddb..a4ac660ea 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -17,16 +17,17 @@ import (
"github.com/containers/buildah/imagebuildah"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/remoteclientconfig"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
"github.com/opencontainers/go-digest"
@@ -113,15 +114,20 @@ func (r RemoteRuntime) DeferredShutdown(force bool) {
}
}
-// RuntimeConfig is a bogus wrapper for compat with the libpod runtime
-type RuntimeConfig struct {
+// Containers is a bogus wrapper for compat with the libpod runtime
+type ContainersConfig struct {
// CGroupManager is the CGroup Manager to use
// Valid values are "cgroupfs" and "systemd"
CgroupManager string
}
+// RuntimeConfig is a bogus wrapper for compat with the libpod runtime
+type RuntimeConfig struct {
+ Containers ContainersConfig
+}
+
// Shutdown is a bogus wrapper for compat with the libpod runtime
-func (r *RemoteRuntime) GetConfig() (*RuntimeConfig, error) {
+func (r *RemoteRuntime) GetConfig() (*config.Config, error) {
return nil, nil
}
@@ -535,32 +541,40 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti
Ulimit: options.CommonBuildOpts.Ulimit,
Volume: options.CommonBuildOpts.Volumes,
}
-
buildinfo := iopodman.BuildInfo{
- AdditionalTags: options.AdditionalTags,
- Annotations: options.Annotations,
- BuildArgs: options.Args,
- BuildOptions: buildOptions,
- CniConfigDir: options.CNIConfigDir,
- CniPluginDir: options.CNIPluginPath,
- Compression: string(options.Compression),
- DefaultsMountFilePath: options.DefaultMountsFilePath,
- Dockerfiles: dockerfiles,
// Err: string(options.Err),
+ // Out:
+ // ReportWriter:
+ Architecture: options.Architecture,
+ AddCapabilities: options.AddCapabilities,
+ AdditionalTags: options.AdditionalTags,
+ Annotations: options.Annotations,
+ BuildArgs: options.Args,
+ BuildOptions: buildOptions,
+ CniConfigDir: options.CNIConfigDir,
+ CniPluginDir: options.CNIPluginPath,
+ Compression: string(options.Compression),
+ Devices: options.Devices,
+ DefaultsMountFilePath: options.DefaultMountsFilePath,
+ Dockerfiles: dockerfiles,
+ DropCapabilities: options.DropCapabilities,
ForceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
Iidfile: options.IIDFile,
Label: options.Labels,
Layers: options.Layers,
- Nocache: options.NoCache,
- // Out:
+ // NamespaceOptions: options.NamespaceOptions,
+ Nocache: options.NoCache,
+ Os: options.OS,
Output: options.Output,
OutputFormat: options.OutputFormat,
PullPolicy: options.PullPolicy.String(),
Quiet: options.Quiet,
RemoteIntermediateCtrs: options.RemoveIntermediateCtrs,
- // ReportWriter:
- RuntimeArgs: options.RuntimeArgs,
- Squash: options.Squash,
+ RuntimeArgs: options.RuntimeArgs,
+ SignBy: options.SignBy,
+ Squash: options.Squash,
+ Target: options.Target,
+ TransientMounts: options.TransientMounts,
}
// tar the file
outputFile, err := ioutil.TempFile("", "varlink_tar_send")
diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go
index ef5a6f926..a56704be6 100644
--- a/pkg/adapter/terminal_linux.go
+++ b/pkg/adapter/terminal_linux.go
@@ -7,6 +7,7 @@ import (
"os"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
@@ -14,7 +15,7 @@ import (
)
// ExecAttachCtr execs and attaches to a container
-func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
resize := make(chan remotecommand.TerminalSize)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
@@ -69,7 +70,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
defer cancel()
}
- streams := new(libpod.AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = stdout
streams.ErrorStream = stderr
streams.InputStream = bufio.NewReader(stdin)
diff --git a/pkg/adapter/terminal_unsupported.go b/pkg/adapter/terminal_unsupported.go
index 3009f0a38..9067757a1 100644
--- a/pkg/adapter/terminal_unsupported.go
+++ b/pkg/adapter/terminal_unsupported.go
@@ -11,7 +11,7 @@ import (
)
// ExecAttachCtr execs and attaches to a container
-func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
return -1, define.ErrNotImplemented
}
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 6b8440fc2..12af40876 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -6,8 +6,8 @@ import (
"net/http"
"strings"
+ "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/define"
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
@@ -46,7 +46,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "NewFromLocal()"))
return
}
- cc, err := makeCreateConfig(input, newImage)
+ defaultContainerConfig, err := runtime.GetConfig()
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "GetConfig()"))
+ return
+ }
+ cc, err := makeCreateConfig(defaultContainerConfig, input, newImage)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "makeCreatConfig()"))
return
@@ -55,7 +60,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.CreateContainer(r.Context(), w, runtime, &cc)
}
-func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
+func makeCreateConfig(defaultContainerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
var (
err error
init bool
@@ -76,7 +81,7 @@ func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Ima
workDir = input.WorkingDir
}
- stopTimeout := uint(define.CtrRemoveTimeout)
+ stopTimeout := defaultContainerConfig.Engine.StopTimeout
if input.StopTimeout != nil {
stopTimeout = uint(*input.StopTimeout)
}
diff --git a/pkg/api/handlers/compat/containers_export.go b/pkg/api/handlers/compat/containers_export.go
new file mode 100644
index 000000000..37b9fbf2b
--- /dev/null
+++ b/pkg/api/handlers/compat/containers_export.go
@@ -0,0 +1,42 @@
+package compat
+
+import (
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/pkg/errors"
+)
+
+func ExportContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+ con, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "unable to create tarball tempfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "unable to close tempfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ if err := con.Export(tmpfile.Name()); err != nil {
+ utils.Error(w, "failed to save the image", http.StatusInternalServerError, errors.Wrap(err, "failed to save image"))
+ return
+ }
+ rdr, err := os.Open(tmpfile.Name())
+ if err != nil {
+ utils.Error(w, "failed to read temp tarball", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+}
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
new file mode 100644
index 000000000..ec1a8ac96
--- /dev/null
+++ b/pkg/api/handlers/compat/exec.go
@@ -0,0 +1,107 @@
+package compat
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/mux"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// ExecCreateHandler creates an exec session for a given container.
+func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ input := new(handlers.ExecCreateConfig)
+ if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error decoding request body as JSON"))
+ return
+ }
+
+ ctrName := utils.GetName(r)
+ ctr, err := runtime.LookupContainer(ctrName)
+ if err != nil {
+ utils.ContainerNotFound(w, ctrName, err)
+ return
+ }
+
+ libpodConfig := new(libpod.ExecConfig)
+ libpodConfig.Command = input.Cmd
+ libpodConfig.Terminal = input.Tty
+ libpodConfig.AttachStdin = input.AttachStdin
+ libpodConfig.AttachStderr = input.AttachStderr
+ libpodConfig.AttachStdout = input.AttachStdout
+ if input.DetachKeys != "" {
+ libpodConfig.DetachKeys = &input.DetachKeys
+ }
+ libpodConfig.Environment = make(map[string]string)
+ for _, envStr := range input.Env {
+ split := strings.SplitN(envStr, "=", 2)
+ if len(split) != 2 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, errors.Errorf("environment variable %q badly formed, must be key=value", envStr))
+ return
+ }
+ libpodConfig.Environment[split[0]] = split[1]
+ }
+ libpodConfig.WorkDir = input.WorkingDir
+ libpodConfig.Privileged = input.Privileged
+ libpodConfig.User = input.User
+
+ sessID, err := ctr.ExecCreate(libpodConfig)
+ if err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid {
+ // Check if the container is paused. If so, return a 409
+ state, err := ctr.State()
+ if err == nil {
+ // Ignore the error != nil case. We're already
+ // throwing an InternalServerError below.
+ if state == define.ContainerStatePaused {
+ utils.Error(w, "Container is paused", http.StatusConflict, errors.Errorf("cannot create exec session as container %s is paused", ctr.ID()))
+ return
+ }
+ }
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ resp := new(handlers.ExecCreateResponse)
+ resp.ID = sessID
+
+ utils.WriteResponse(w, http.StatusCreated, resp)
+}
+
+// ExecInspectHandler inspects a given exec session.
+func ExecInspectHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ sessionID := mux.Vars(r)["id"]
+ sessionCtr, err := runtime.GetExecSessionContainer(sessionID)
+ if err != nil {
+ utils.Error(w, fmt.Sprintf("No such exec session: %s", sessionID), http.StatusNotFound, err)
+ return
+ }
+
+ logrus.Debugf("Inspecting exec session %s of container %s", sessionID, sessionCtr.ID())
+
+ session, err := sessionCtr.ExecSession(sessionID)
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error retrieving exec session %s from container %s", sessionID, sessionCtr.ID()))
+ return
+ }
+
+ inspectOut, err := session.Inspect()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, inspectOut)
+}
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index cce718f54..ea9cbd691 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -64,6 +64,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
+ All bool
Filters map[string][]string `schema:"filters"`
}{
// This is where you can override the golang default value for one of fields
@@ -80,7 +81,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
- pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), false, filters)
+ pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters)
if err != nil {
utils.InternalServerError(w, err)
return
@@ -128,13 +129,13 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
- sc := image2.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
tag := "latest"
options := libpod.ContainerCommitOptions{
Pause: true,
}
options.CommitOptions = buildah.CommitOptions{
- SignaturePolicyPath: rtc.SignaturePolicyPath,
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
ReportWriter: os.Stderr,
SystemContext: sc,
PreferredManifestType: manifest.DockerV2Schema2MediaType,
diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go
new file mode 100644
index 000000000..2260d5557
--- /dev/null
+++ b/pkg/api/handlers/compat/images_push.go
@@ -0,0 +1,80 @@
+package compat
+
+import (
+ "context"
+ "net/http"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+// PushImage is the handler for the compat http endpoint for pushing images.
+func PushImage(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Tag string `schema:"tag"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ // Note that Docker's docs state "Image name or ID" to be in the path
+ // parameter but it really must be a name as Docker does not allow for
+ // pushing an image by ID.
+ imageName := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path
+ if query.Tag != "" {
+ imageName += ":" + query.Tag
+ }
+ if _, err := utils.ParseStorageReference(imageName); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", imageName))
+ return
+ }
+
+ newImage, err := runtime.ImageRuntime().NewFromLocal(imageName)
+ if err != nil {
+ utils.ImageNotFound(w, imageName, errors.Wrapf(err, "Failed to find image %s", imageName))
+ return
+ }
+
+ // TODO: the X-Registry-Auth header is not checked yet here nor in any other
+ // endpoint. Pushing does NOT work with authentication at the moment.
+ dockerRegistryOptions := &image.DockerRegistryOptions{}
+ authfile := ""
+ if sys := runtime.SystemContext(); sys != nil {
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ authfile = sys.AuthFilePath
+ }
+
+ err = newImage.PushImageToHeuristicDestination(
+ context.Background(),
+ imageName,
+ "", // manifest type
+ authfile,
+ "", // digest file
+ "", // signature policy
+ os.Stderr,
+ false, // force compression
+ image.SigningOptions{},
+ dockerRegistryOptions,
+ nil, // additional tags
+ )
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Error pushing image %q", imageName))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, "")
+
+}
diff --git a/pkg/api/handlers/compat/images_remove.go b/pkg/api/handlers/compat/images_remove.go
index 3d346543e..ed0153529 100644
--- a/pkg/api/handlers/compat/images_remove.go
+++ b/pkg/api/handlers/compat/images_remove.go
@@ -36,17 +36,23 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
return
}
- _, err = runtime.RemoveImage(r.Context(), newImage, query.Force)
+ results, err := runtime.RemoveImage(r.Context(), newImage, query.Force)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
- // TODO
- // This will need to be fixed for proper response, like Deleted: and Untagged:
- m := make(map[string]string)
- m["Deleted"] = newImage.ID()
- foo := []map[string]string{}
- foo = append(foo, m)
- utils.WriteResponse(w, http.StatusOK, foo)
+
+ response := make([]map[string]string, 0, len(results.Untagged)+1)
+ deleted := make(map[string]string, 1)
+ deleted["Deleted"] = results.Deleted
+ response = append(response, deleted)
+
+ for _, u := range results.Untagged {
+ untagged := make(map[string]string, 1)
+ untagged["Untagged"] = u
+ response = append(response, untagged)
+ }
+
+ utils.WriteResponse(w, http.StatusOK, response)
}
diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go
index 30b49948d..104d0793b 100644
--- a/pkg/api/handlers/compat/info.go
+++ b/pkg/api/handlers/compat/info.go
@@ -9,8 +9,8 @@ import (
"strings"
"time"
+ "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/config"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
@@ -60,7 +60,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
CPUCfsQuota: sysInfo.CPUCfsQuota,
CPUSet: sysInfo.Cpuset,
CPUShares: sysInfo.CPUShares,
- CgroupDriver: configInfo.CgroupManager,
+ CgroupDriver: configInfo.Engine.CgroupManager,
ClusterAdvertise: "",
ClusterStore: "",
ContainerdCommit: docker.Commit{},
@@ -69,7 +69,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
ContainersRunning: stateInfo[define.ContainerStateRunning],
ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited],
Debug: log.IsLevelEnabled(log.DebugLevel),
- DefaultRuntime: configInfo.OCIRuntime,
+ DefaultRuntime: configInfo.Engine.OCIRuntime,
DockerRootDir: storeInfo["GraphRoot"].(string),
Driver: storeInfo["GraphDriverName"].(string),
DriverStatus: getGraphStatus(storeInfo),
@@ -152,7 +152,7 @@ func getSecOpts(sysInfo *sysinfo.SysInfo) []string {
func getRuntimes(configInfo *config.Config) map[string]docker.Runtime {
var runtimes = map[string]docker.Runtime{}
- for name, paths := range configInfo.OCIRuntimes {
+ for name, paths := range configInfo.Engine.OCIRuntimes {
runtimes[name] = docker.Runtime{
Path: paths[0],
Args: nil,
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index cdc34004f..fde72552b 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -1,16 +1,21 @@
package libpod
import (
+ "io/ioutil"
"net/http"
+ "os"
"path/filepath"
"sort"
"strconv"
"time"
+ "github.com/containers/libpod/pkg/api/handlers/compat"
+
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -325,3 +330,129 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts shared.P
}
return ps, nil
}
+
+func Checkpoint(w http.ResponseWriter, r *http.Request) {
+ var targetFile string
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Keep bool `schema:"keep"`
+ LeaveRunning bool `schema:"leaveRunning"`
+ TCPEstablished bool `schema:"tcpEstablished"`
+ Export bool `schema:"export"`
+ IgnoreRootFS bool `schema:"ignoreRootFS"`
+ }{
+ // override any golang type defaults
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ name := utils.GetName(r)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ if query.Export {
+ tmpFile, err := ioutil.TempFile("", "checkpoint")
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer os.Remove(tmpFile.Name())
+ if err := tmpFile.Close(); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ targetFile = tmpFile.Name()
+ }
+ options := libpod.ContainerCheckpointOptions{
+ Keep: query.Keep,
+ KeepRunning: query.LeaveRunning,
+ TCPEstablished: query.TCPEstablished,
+ IgnoreRootfs: query.IgnoreRootFS,
+ }
+ if query.Export {
+ options.TargetFile = targetFile
+ }
+ err = ctr.Checkpoint(r.Context(), options)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if query.Export {
+ f, err := os.Open(targetFile)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer f.Close()
+ utils.WriteResponse(w, http.StatusOK, f)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()})
+}
+
+func Restore(w http.ResponseWriter, r *http.Request) {
+ var (
+ targetFile string
+ )
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Keep bool `schema:"keep"`
+ TCPEstablished bool `schema:"tcpEstablished"`
+ Import bool `schema:"import"`
+ Name string `schema:"name"`
+ IgnoreRootFS bool `schema:"ignoreRootFS"`
+ IgnoreStaticIP bool `schema:"ignoreStaticIP"`
+ IgnoreStaticMAC bool `schema:"ignoreStaticMAC"`
+ }{
+ // override any golang type defaults
+ }
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ name := utils.GetName(r)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ if query.Import {
+ t, err := ioutil.TempFile("", "restore")
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer t.Close()
+ if err := compat.SaveFromBody(t, r); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ targetFile = t.Name()
+ }
+
+ options := libpod.ContainerCheckpointOptions{
+ Keep: query.Keep,
+ TCPEstablished: query.TCPEstablished,
+ IgnoreRootfs: query.IgnoreRootFS,
+ IgnoreStaticIP: query.IgnoreStaticIP,
+ IgnoreStaticMAC: query.IgnoreStaticMAC,
+ }
+ if query.Import {
+ options.TargetFile = targetFile
+ options.Name = query.Name
+ }
+ err = ctr.Restore(r.Context(), options)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()})
+}
diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go
index ebca41151..38a341a89 100644
--- a/pkg/api/handlers/libpod/containers_create.go
+++ b/pkg/api/handlers/libpod/containers_create.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/specgen/generate"
"github.com/pkg/errors"
)
@@ -19,7 +20,11 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
- ctr, err := sg.MakeContainer(runtime)
+ if err := generate.CompleteSpec(r.Context(), runtime, &sg); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ ctr, err := generate.MakeContainer(runtime, &sg)
if err != nil {
utils.InternalServerError(w, err)
return
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index f8e666451..850de4598 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -14,15 +14,16 @@ import (
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
- "github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/util"
+ utils2 "github.com/containers/libpod/utils"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -119,12 +120,12 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
func PruneImages(w http.ResponseWriter, r *http.Request) {
var (
- all bool
err error
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
+ All bool `schema:"all"`
Filters map[string][]string `schema:"filters"`
}{
// override any golang type defaults
@@ -140,7 +141,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
if _, found := r.URL.Query()["filters"]; found {
dangling := query.Filters["all"]
if len(dangling) > 0 {
- all, err = strconv.ParseBool(query.Filters["all"][0])
+ query.All, err = strconv.ParseBool(query.Filters["all"][0])
if err != nil {
utils.InternalServerError(w, err)
return
@@ -152,7 +153,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
}
}
- cids, err := runtime.ImageRuntime().PruneImages(r.Context(), all, libpodFilters)
+
+ cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
@@ -161,13 +163,16 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
}
func ExportImage(w http.ResponseWriter, r *http.Request) {
+ var (
+ output string
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Compress bool `schema:"compress"`
Format string `schema:"format"`
}{
- Format: "docker-archive",
+ Format: define.OCIArchive,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
@@ -175,14 +180,27 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
-
- tmpfile, err := ioutil.TempFile("", "api.tar")
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
- return
- }
- if err := tmpfile.Close(); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ switch query.Format {
+ case define.OCIArchive, define.V2s2Archive:
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "unable to create tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ output = tmpfile.Name()
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "unable to close tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ case define.OCIManifestDir, define.V2s2ManifestDir:
+ tmpdir, err := ioutil.TempDir("", "save")
+ if err != nil {
+ utils.Error(w, "unable to create tmpdir", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempdir"))
+ return
+ }
+ output = tmpdir
+ default:
+ utils.Error(w, "unknown format", http.StatusInternalServerError, errors.Errorf("unknown format %q", query.Format))
return
}
name := utils.GetName(r)
@@ -192,17 +210,28 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
return
}
- if err := newImage.Save(r.Context(), name, query.Format, tmpfile.Name(), []string{}, false, query.Compress); err != nil {
+ if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
return
}
- rdr, err := os.Open(tmpfile.Name())
+ defer os.RemoveAll(output)
+ // if dir format, we need to tar it
+ if query.Format == "oci-dir" || query.Format == "docker-dir" {
+ rdr, err := utils2.Tar(output)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+ return
+ }
+ rdr, err := os.Open(output)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
return
}
defer rdr.Close()
- defer os.Remove(tmpfile.Name())
utils.WriteResponse(w, http.StatusOK, rdr)
}
@@ -253,7 +282,7 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) {
return
}
}
- utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesLoadReport{ID: loadedImage})
+ utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Name: loadedImage})
}
func ImagesImport(w http.ResponseWriter, r *http.Request) {
@@ -299,9 +328,13 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) {
return
}
- utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesImportReport{ID: importedImage})
+ utils.WriteResponse(w, http.StatusOK, entities.ImageImportReport{Id: importedImage})
}
+// ImagesPull is the v2 libpod endpoint for pulling images. Note that the
+// mandatory `reference` must be a reference to a registry (i.e., of docker
+// transport or be normalized to one). Other transports are rejected as they
+// do not make sense in a remote context.
func ImagesPull(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -326,36 +359,27 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, errors.New("reference parameter cannot be empty"))
return
}
- // Enforce the docker transport. This is just a precaution as some callers
- // might accustomed to using the "transport:reference" notation. Using
- // another than the "docker://" transport does not really make sense for a
- // remote case. For loading tarballs, the load and import endpoints should
- // be used.
- imageRef, err := alltransports.ParseImageName(query.Reference)
- if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+
+ imageRef, err := utils.ParseDockerReference(query.Reference)
+ if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Errorf("reference %q must be a docker reference", query.Reference))
+ errors.Wrapf(err, "image destination %q is not a docker-transport reference", query.Reference))
return
- } else if err != nil {
- origErr := err
- imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s://%s", docker.Transport.Name(), query.Reference))
- if err != nil {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(origErr, "reference %q must be a docker reference", query.Reference))
- return
- }
}
+ // Trim the docker-transport prefix.
+ rawImage := strings.TrimPrefix(query.Reference, fmt.Sprintf("%s://", docker.Transport.Name()))
+
// all-tags doesn't work with a tagged reference, so let's check early
- namedRef, err := reference.Parse(query.Reference)
+ namedRef, err := reference.Parse(rawImage)
if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "error parsing reference %q", query.Reference))
+ errors.Wrapf(err, "error parsing reference %q", rawImage))
return
}
if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Errorf("reference %q must not have a tag for all-tags", query.Reference))
+ errors.Errorf("reference %q must not have a tag for all-tags", rawImage))
return
}
@@ -376,7 +400,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
OSChoice: query.OverrideOS,
ArchitectureChoice: query.OverrideArch,
}
- if query.TLSVerify {
+ if _, found := r.URL.Query()["tlsVerify"]; found {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
}
@@ -399,13 +423,19 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
}
}
+ authfile := ""
+ if sys := runtime.SystemContext(); sys != nil {
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ authfile = sys.AuthFilePath
+ }
+
// Finally pull the images
for _, img := range imagesToPull {
newImage, err := runtime.ImageRuntime().New(
context.Background(),
img,
"",
- "",
+ authfile,
os.Stderr,
&dockerRegistryOptions,
image.SigningOptions{},
@@ -421,6 +451,94 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, res)
}
+// PushImage is the handler for the compat http endpoint for pushing images.
+func PushImage(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Credentials string `schema:"credentials"`
+ Destination string `schema:"destination"`
+ TLSVerify bool `schema:"tlsVerify"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ source := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path
+ if _, err := utils.ParseStorageReference(source); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", source))
+ return
+ }
+
+ destination := query.Destination
+ if destination == "" {
+ destination = source
+ }
+
+ if _, err := utils.ParseDockerReference(destination); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "image destination %q is not a docker-transport reference", destination))
+ return
+ }
+
+ newImage, err := runtime.ImageRuntime().NewFromLocal(source)
+ if err != nil {
+ utils.ImageNotFound(w, source, errors.Wrapf(err, "Failed to find image %s", source))
+ return
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if len(query.Credentials) != 0 {
+ creds, err := util.ParseRegistryCreds(query.Credentials)
+ if err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
+ return
+ }
+ registryCreds = creds
+ }
+
+ // TODO: the X-Registry-Auth header is not checked yet here nor in any other
+ // endpoint. Pushing does NOT work with authentication at the moment.
+ dockerRegistryOptions := &image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ }
+ authfile := ""
+ if sys := runtime.SystemContext(); sys != nil {
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ authfile = sys.AuthFilePath
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ err = newImage.PushImageToHeuristicDestination(
+ context.Background(),
+ destination,
+ "", // manifest type
+ authfile,
+ "", // digest file
+ "", // signature policy
+ os.Stderr,
+ false, // force compression
+ image.SigningOptions{},
+ dockerRegistryOptions,
+ nil, // additional tags
+ )
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Error pushing image %q", destination))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, "")
+}
+
func CommitContainer(w http.ResponseWriter, r *http.Request) {
var (
destImage string
@@ -451,7 +569,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config"))
return
}
- sc := image2.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
tag := "latest"
options := libpod.ContainerCommitOptions{
Pause: true,
@@ -470,7 +588,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
return
}
options.CommitOptions = buildah.CommitOptions{
- SignaturePolicyPath: rtc.SignaturePolicyPath,
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
ReportWriter: os.Stderr,
SystemContext: sc,
PreferredManifestType: mimeType,
@@ -501,3 +619,29 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint
}
+
+func UntagImage(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
+ return
+ }
+ tag := "latest"
+ if len(r.Form.Get("tag")) > 0 {
+ tag = r.Form.Get("tag")
+ }
+ if len(r.Form.Get("repo")) < 1 {
+ utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image"))
+ return
+ }
+ repo := r.Form.Get("repo")
+ tagName := fmt.Sprintf("%s:%s", repo, tag)
+ if err := newImage.UntagImage(tagName); err != nil {
+ utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusCreated, "")
+}
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index a3d2caba6..d87ed7eba 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -36,7 +36,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
manID, err := image.CreateManifestList(runtime.ImageRuntime(), *sc, query.Name, query.Image, query.All)
if err != nil {
utils.InternalServerError(w, err)
@@ -79,7 +79,7 @@ func ManifestAdd(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
newID, err := newImage.AddManifest(*sc, manifestInput)
if err != nil {
utils.InternalServerError(w, err)
@@ -149,7 +149,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
opts := manifests.PushOptions{
ImageListSelection: copy2.CopySpecificImages,
SystemContext: sc,
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index e3dbe3b35..e8a92e93e 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -18,7 +18,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- configDir := config.CNIConfigDir
+ configDir := config.Network.NetworkConfigDir
if len(configDir) < 1 {
configDir = network.CNIConfigDir
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 27ec64d89..a890169a1 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -6,12 +6,12 @@ import (
"net/http"
"strings"
- "github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -20,76 +20,14 @@ import (
func PodCreate(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- options []libpod.PodCreateOption
err error
)
- labels := make(map[string]string)
- input := handlers.PodCreateConfig{}
- if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ var psg specgen.PodSpecGenerator
+ if err := json.NewDecoder(r.Body).Decode(&psg); err != nil {
+ utils.Error(w, "Failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
return
}
- if len(input.InfraCommand) > 0 || len(input.InfraImage) > 0 {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError,
- errors.New("infra-command and infra-image are not implemented yet"))
- return
- }
- // TODO long term we should break the following out of adapter and into libpod proper
- // so that the cli and api can share the creation of a pod with the same options
- if len(input.CGroupParent) > 0 {
- options = append(options, libpod.WithPodCgroupParent(input.CGroupParent))
- }
-
- if len(input.Labels) > 0 {
- labels, err = parse.GetAllLabels([]string{}, input.Labels)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- }
-
- if len(labels) != 0 {
- options = append(options, libpod.WithPodLabels(labels))
- }
-
- if len(input.Name) > 0 {
- options = append(options, libpod.WithPodName(input.Name))
- }
-
- if len(input.Hostname) > 0 {
- options = append(options, libpod.WithPodHostname(input.Hostname))
- }
-
- if input.Infra {
- // TODO infra-image and infra-command are not supported in the libpod API yet. Will fix
- // when implemented in libpod
- options = append(options, libpod.WithInfraContainer())
- sharedNamespaces := shared.DefaultKernelNamespaces
- if len(input.Share) > 0 {
- sharedNamespaces = input.Share
- }
- nsOptions, err := shared.GetNamespaceOptions(strings.Split(sharedNamespaces, ","))
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, nsOptions...)
- }
-
- if len(input.Publish) > 0 {
- portBindings, err := shared.CreatePortBindings(input.Publish)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, libpod.WithInfraContainerPorts(portBindings))
-
- }
- // always have containers use pod cgroups
- // User Opt out is not yet supported
- options = append(options, libpod.WithPodCgroups())
-
- pod, err := runtime.NewPod(r.Context(), options...)
+ pod, err := psg.MakePod(runtime)
if err != nil {
http_code := http.StatusInternalServerError
if errors.Cause(err) == define.ErrPodExists {
@@ -102,9 +40,6 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
}
func Pods(w http.ResponseWriter, r *http.Request) {
- var (
- podInspectData []*libpod.PodInspect
- )
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Filters map[string][]string `schema:"filters"`
@@ -118,20 +53,11 @@ func Pods(w http.ResponseWriter, r *http.Request) {
}
pods, err := utils.GetPods(w, r)
-
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- for _, pod := range pods {
- data, err := pod.Inspect()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- podInspectData = append(podInspectData, data)
- }
- utils.WriteResponse(w, http.StatusOK, podInspectData)
+ utils.WriteResponse(w, http.StatusOK, pods)
}
func PodInspect(w http.ResponseWriter, r *http.Request) {
@@ -147,7 +73,10 @@ func PodInspect(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, podData)
+ report := entities.PodInspectReport{
+ PodInspect: podData,
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodStop(w http.ResponseWriter, r *http.Request) {
@@ -155,6 +84,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
stopError error
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
+ responses map[string]error
+ errs []error
)
query := struct {
Timeout int `schema:"t"`
@@ -185,18 +116,28 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
}
if query.Timeout > 0 {
- _, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
+ responses, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
} else {
- _, stopError = pod.Stop(r.Context(), false)
+ responses, stopError = pod.Stop(r.Context(), false)
}
if stopError != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodStopReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodStart(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -213,11 +154,19 @@ func PodStart(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusNotModified, "")
return
}
- if _, err := pod.Start(r.Context()); err != nil {
+ responses, err := pod.Start(r.Context())
+ if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodStartReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodDelete(w http.ResponseWriter, r *http.Request) {
@@ -246,10 +195,16 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ report := entities.PodRmReport{
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodRestart(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -257,12 +212,19 @@ func PodRestart(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Restart(r.Context())
+ responses, err := pod.Restart(r.Context())
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodRestartReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodPrune(w http.ResponseWriter, r *http.Request) {
@@ -278,6 +240,9 @@ func PodPrune(w http.ResponseWriter, r *http.Request) {
}
func PodPause(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -285,15 +250,25 @@ func PodPause(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Pause()
+ responses, err := pod.Pause()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ for _, v := range responses {
+ errs = append(errs, v)
+ }
+ report := entities.PodPauseReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodUnpause(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -301,12 +276,61 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Unpause()
+ responses, err := pod.Unpause()
if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err)
+ return
+ }
+ for _, v := range responses {
+ errs = append(errs, v)
+ }
+ report := entities.PodUnpauseReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, &report)
+}
+
+func PodTop(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+
+ query := struct {
+ PsArgs string `schema:"ps_args"`
+ }{
+ PsArgs: "",
+ }
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+
+ name := utils.GetName(r)
+ pod, err := runtime.LookupPod(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+
+ args := []string{}
+ if query.PsArgs != "" {
+ args = append(args, query.PsArgs)
+ }
+ output, err := pod.GetPodPidInformation(args)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ var body = handlers.PodTopOKBody{}
+ if len(output) > 0 {
+ body.Titles = strings.Split(output[0], "\t")
+ for _, line := range output[1:] {
+ body.Processes = append(body.Processes, strings.Split(line, "\t"))
+ }
+ }
+ utils.WriteJSON(w, http.StatusOK, body)
}
func PodKill(w http.ResponseWriter, r *http.Request) {
@@ -314,6 +338,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
signal = "SIGKILL"
+ errs []error
)
query := struct {
Signal string `schema:"signal"`
@@ -356,12 +381,23 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID()))
return
}
- _, err = pod.Kill(uint(sig))
+
+ responses, err := pod.Kill(uint(sig))
if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+
+ for _, v := range responses {
+ if v != nil {
+ errs = append(errs, v)
+ }
+ }
+ report := &entities.PodKillReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodExists(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index 149fa10dc..1fad2dd1a 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
)
@@ -26,6 +27,55 @@ type swagInspectManifestResponse struct {
Body manifest.List
}
+// Kill Pod
+// swagger:response PodKillReport
+type swagKillPodResponse struct {
+ // in:body
+ Body entities.PodKillReport
+}
+
+// Pause pod
+// swagger:response PodPauseReport
+type swagPausePodResponse struct {
+ // in:body
+ Body entities.PodPauseReport
+}
+
+// Unpause pod
+// swagger:response PodUnpauseReport
+type swagUnpausePodResponse struct {
+ // in:body
+ Body entities.PodUnpauseReport
+}
+
+// Stop pod
+// swagger:response PodStopReport
+type swagStopPodResponse struct {
+ // in:body
+ Body entities.PodStopReport
+}
+
+// Restart pod
+// swagger:response PodRestartReport
+type swagRestartPodResponse struct {
+ // in:body
+ Body entities.PodRestartReport
+}
+
+// Start pod
+// swagger:response PodStartReport
+type swagStartPodResponse struct {
+ // in:body
+ Body entities.PodStartReport
+}
+
+// Rm pod
+// swagger:response PodRmReport
+type swagRmPodResponse struct {
+ // in:body
+ Body entities.PodRmReport
+}
+
func ServeSwagger(w http.ResponseWriter, r *http.Request) {
path := DefaultPodmanSwaggerSpec
if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index e61d272f4..5a6fc021e 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -149,13 +149,20 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
func PruneVolumes(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ reports []*entities.VolumePruneReport
)
pruned, err := runtime.PruneVolumes(r.Context())
if err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, pruned)
+ for k, v := range pruned {
+ reports = append(reports, &entities.VolumePruneReport{
+ Err: v,
+ Id: k,
+ })
+ }
+ utils.WriteResponse(w, http.StatusOK, reports)
}
func RemoveVolume(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index 4ba123ba9..33a9fdd58 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -2,7 +2,9 @@ package handlers
import (
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/inspect"
"github.com/docker/docker/api/types"
)
@@ -29,14 +31,14 @@ type swagImageInspect struct {
// swagger:response DocsLibpodImagesLoadResponse
type swagLibpodImagesLoadResponse struct {
// in:body
- Body []LibpodImagesLoadReport
+ Body entities.ImageLoadReport
}
// Import response
// swagger:response DocsLibpodImagesImportResponse
type swagLibpodImagesImportResponse struct {
// in:body
- Body LibpodImagesImportReport
+ Body entities.ImageImportReport
}
// Pull response
@@ -95,20 +97,29 @@ type swagContainerInspectResponse struct {
}
// List processes in container
-// swagger:response DockerTopResponse
-type swagDockerTopResponse struct {
+// swagger:response DocsContainerTopResponse
+type swagContainerTopResponse struct {
// in:body
Body struct {
ContainerTopOKBody
}
}
+// List processes in pod
+// swagger:response DocsPodTopResponse
+type swagPodTopResponse struct {
+ // in:body
+ Body struct {
+ PodTopOKBody
+ }
+}
+
// Inspect container
// swagger:response LibpodInspectContainerResponse
type swagLibpodInspectContainerResponse struct {
// in:body
Body struct {
- libpod.InspectContainerData
+ define.InspectContainerData
}
}
@@ -116,7 +127,7 @@ type swagLibpodInspectContainerResponse struct {
// swagger:response ListPodsResponse
type swagListPodsResponse struct {
// in:body
- Body []libpod.PodInspect
+ Body []entities.ListPodsReport
}
// Inspect pod
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 84ca0fbed..496512f2e 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -38,10 +38,6 @@ type LibpodImagesLoadReport struct {
ID string `json:"id"`
}
-type LibpodImagesImportReport struct {
- ID string `json:"id"`
-}
-
type LibpodImagesPullReport struct {
ID string `json:"id"`
}
@@ -133,6 +129,10 @@ type ContainerTopOKBody struct {
dockerContainer.ContainerTopOKBody
}
+type PodTopOKBody struct {
+ dockerContainer.ContainerTopOKBody
+}
+
// swagger:model PodCreateConfig
type PodCreateConfig struct {
Name string `json:"name"`
@@ -172,6 +172,14 @@ type ImageTreeResponse struct {
Layers []ImageLayer `json:"layers"`
}
+type ExecCreateConfig struct {
+ docker.ExecConfig
+}
+
+type ExecCreateResponse struct {
+ docker.IDResponse
+}
+
func EventToApiEvent(e *events.Event) *Event {
return &Event{dockerEvents.Message{
Type: e.Type.String(),
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 32b8c5b0a..b5bd488fb 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -46,6 +46,13 @@ func WriteResponse(w http.ResponseWriter, code int, value interface{}) {
if _, err := io.Copy(w, v); err != nil {
logrus.Errorf("unable to copy to response: %q", err)
}
+ case io.Reader:
+ w.Header().Set("Content-Type", "application/x-tar")
+ w.WriteHeader(code)
+
+ if _, err := io.Copy(w, v); err != nil {
+ logrus.Errorf("unable to copy to response: %q", err)
+ }
default:
WriteJSON(w, code, value)
}
diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go
index 696d5f745..1c67de9db 100644
--- a/pkg/api/handlers/utils/images.go
+++ b/pkg/api/handlers/utils/images.go
@@ -4,11 +4,52 @@ import (
"fmt"
"net/http"
+ "github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/storage"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/gorilla/schema"
+ "github.com/pkg/errors"
)
+// ParseDockerReference parses the specified image name to a
+// `types.ImageReference` and enforces it to refer to a docker-transport
+// reference.
+func ParseDockerReference(name string) (types.ImageReference, error) {
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
+ imageRef, err := alltransports.ParseImageName(name)
+ if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+ return nil, errors.Errorf("reference %q must be a docker reference", name)
+ } else if err != nil {
+ origErr := err
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name))
+ if err != nil {
+ return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name)
+ }
+ }
+ return imageRef, nil
+}
+
+// ParseStorageReference parses the specified image name to a
+// `types.ImageReference` and enforces it to refer to a
+// containers-storage-transport reference.
+func ParseStorageReference(name string) (types.ImageReference, error) {
+ storagePrefix := fmt.Sprintf("%s:", storage.Transport.Name())
+ imageRef, err := alltransports.ParseImageName(name)
+ if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+ return nil, errors.Errorf("reference %q must be a storage reference", name)
+ } else if err != nil {
+ origErr := err
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", storagePrefix, name))
+ if err != nil {
+ return nil, errors.Wrapf(origErr, "reference %q must be a storage reference", name)
+ }
+ }
+ return imageRef, nil
+}
+
// GetImages is a common function used to get images for libpod and other compatibility
// mechanisms
func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go
index 266ad9a4b..d47053eda 100644
--- a/pkg/api/handlers/utils/pods.go
+++ b/pkg/api/handlers/utils/pods.go
@@ -6,10 +6,16 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/gorilla/schema"
)
-func GetPods(w http.ResponseWriter, r *http.Request) ([]*libpod.Pod, error) {
+func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) {
+ var (
+ lps []*entities.ListPodsReport
+ pods []*libpod.Pod
+ podErr error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -37,9 +43,47 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*libpod.Pod, error) {
if err != nil {
return nil, err
}
- return shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...)
+ pods, podErr = shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...)
+ } else {
+ pods, podErr = runtime.GetAllPods()
}
-
- return runtime.GetAllPods()
-
+ if podErr != nil {
+ return nil, podErr
+ }
+ for _, pod := range pods {
+ status, err := pod.GetPodStatus()
+ if err != nil {
+ return nil, err
+ }
+ ctrs, err := pod.AllContainers()
+ if err != nil {
+ return nil, err
+ }
+ infraId, err := pod.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
+ lp := entities.ListPodsReport{
+ Cgroup: pod.CgroupParent(),
+ Created: pod.CreatedTime(),
+ Id: pod.ID(),
+ Name: pod.Name(),
+ Namespace: pod.Namespace(),
+ Status: status,
+ InfraId: infraId,
+ }
+ for _, ctr := range ctrs {
+ state, err := ctr.State()
+ if err != nil {
+ return nil, err
+ }
+ lp.Containers = append(lp.Containers, &entities.ListPodContainer{
+ Id: ctr.ID(),
+ Names: ctr.Name(),
+ Status: state.String(),
+ })
+ }
+ lps = append(lps, &lp)
+ }
+ return lps, nil
}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 2656d1d89..f126112d0 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -429,7 +429,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DockerTopResponse"
+ // $ref: "#/responses/DocsContainerTopResponse"
// 404:
// $ref: "#/responses/NoSuchContainer"
// 500:
@@ -587,6 +587,29 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
r.HandleFunc(VersionedPath("/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
r.HandleFunc("/containers/{name}/resize", s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /containers/{name}/export compat exportContainer
+ // ---
+ // tags:
+ // - containers (compat)
+ // summary: Export a container
+ // description: Export the contents of a container as a tarball.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
+ r.HandleFunc("/containers/{name}/export", s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
/*
libpod endpoints
@@ -1041,7 +1064,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DockerTopResponse"
+ // $ref: "#/responses/DocsContainerTopResponse"
// 404:
// $ref: "#/responses/NoSuchContainer"
// 500:
@@ -1237,5 +1260,122 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/containers/{name}/export libpod libpodExportContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Export a container
+ // description: Export the contents of a container as a tarball.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/containers/{name}/checkout libpod libpodCheckpointContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Checkpoint a container
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: keep
+ // type: boolean
+ // description: keep all temporary checkpoint files
+ // - in: query
+ // name: leaveRunning
+ // type: boolean
+ // description: leave the container running after writing checkpoint to disk
+ // - in: query
+ // name: tcpEstablished
+ // type: boolean
+ // description: checkpoint a container with established TCP connections
+ // - in: query
+ // name: export
+ // type: boolean
+ // description: export the checkpoint image to a tar.gz
+ // - in: query
+ // name: ignoreRootFS
+ // type: boolean
+ // description: do not include root file-system changes when exporting
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body if exported
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/checkpoint"), s.APIHandler(libpod.Checkpoint)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/containers/{name} restore libpod libpodRestoreContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Restore a container
+ // description: Restore a container from a checkpoint.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or id of the container
+ // - in: query
+ // name: name
+ // type: string
+ // description: the name of the container when restored from a tar. can only be used with import
+ // - in: query
+ // name: keep
+ // type: boolean
+ // description: keep all temporary checkpoint files
+ // - in: query
+ // name: leaveRunning
+ // type: boolean
+ // description: leave the container running after writing checkpoint to disk
+ // - in: query
+ // name: tcpEstablished
+ // type: boolean
+ // description: checkpoint a container with established TCP connections
+ // - in: query
+ // name: import
+ // type: boolean
+ // description: import the restore from a checkpoint tar.gz
+ // - in: query
+ // name: ignoreRootFS
+ // type: boolean
+ // description: do not include root file-system changes when exporting
+ // - in: query
+ // name: ignoreStaticIP
+ // type: boolean
+ // description: ignore IP address if set statically
+ // - in: query
+ // name: ignoreStaticMAC
+ // type: boolean
+ // description: ignore MAC address if set statically
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body if exported
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_exec.go b/pkg/api/server/register_exec.go
index d27d21a04..71fb50307 100644
--- a/pkg/api/server/register_exec.go
+++ b/pkg/api/server/register_exec.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerExecHandlers(r *mux.Router) error {
- // swagger:operation POST /containers/{name}/create compat createExec
+ // swagger:operation POST /containers/{name}/exec compat createExec
// ---
// tags:
// - exec (compat)
@@ -74,9 +74,9 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// description: container is paused
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/containers/{name}/create"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/containers/{name}/exec"), s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/containers/{name}/create", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle("/containers/{name}/exec", s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// swagger:operation POST /exec/{id}/start compat startExec
// ---
// tags:
@@ -169,15 +169,15 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/exec/{id}/json", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle("/exec/{id}/json", s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
/*
libpod api follows
*/
- // swagger:operation POST /libpod/containers/{name}/create libpod libpodCreateExec
+ // swagger:operation POST /libpod/containers/{name}/exec libpod libpodCreateExec
// ---
// tags:
// - exec
@@ -243,7 +243,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// description: container is paused
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/containers/{name}/create"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/libpod/containers/{name}/exec"), s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// swagger:operation POST /libpod/exec/{id}/start libpod libpodStartExec
// ---
// tags:
@@ -332,6 +332,6 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index e8dfe2fa8..d45423096 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -211,6 +211,41 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}"), s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}", s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
+ // swagger:operation POST /images/{name:.*}/push compat pushImage
+ // ---
+ // tags:
+ // - images (compat)
+ // summary: Push Image
+ // description: Push an image to a container registry
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: Name of image to push.
+ // - in: query
+ // name: tag
+ // type: string
+ // description: The tag to associate with the image on the registry.
+ // - in: header
+ // name: X-Registry-Auth
+ // type: string
+ // description: A base64-encoded auth configuration.
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/images/{name:.*}/push"), s.APIHandler(compat.PushImage)).Methods(http.MethodPost)
+ // Added non version path to URI to support docker non versioned paths
+ r.Handle("/images/{name:.*}/push", s.APIHandler(compat.PushImage)).Methods(http.MethodPost)
// swagger:operation GET /images/{name:.*}/get compat exportImage
// ---
// tags:
@@ -583,6 +618,43 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
libpod endpoints
*/
+ // swagger:operation POST /libpod/images/{name:.*}/push libpod libpodPushImage
+ // ---
+ // tags:
+ // - images (libpod)
+ // summary: Push Image
+ // description: Push an image to a container registry
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: Name of image to push.
+ // - in: query
+ // name: tag
+ // type: string
+ // description: The tag to associate with the image on the registry.
+ // - in: query
+ // name: credentials
+ // description: username:password for the registry.
+ // type: string
+ // - in: header
+ // name: X-Registry-Auth
+ // type: string
+ // description: A base64-encoded auth configuration.
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/{name:.*}/push"), s.APIHandler(libpod.PushImage)).Methods(http.MethodPost)
// swagger:operation GET /libpod/images/{name:.*}/exists libpod libpodImageExists
// ---
// tags:
@@ -883,7 +955,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// tags:
// - images
// summary: Export an image
- // description: Export an image as a tarball
+ // description: Export an image
// parameters:
// - in: path
// name: name:.*
@@ -1019,5 +1091,39 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/commit"), s.APIHandler(libpod.CommitContainer)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/images/{name:.*}/untag libpod libpodUntagImage
+ // ---
+ // tags:
+ // - images
+ // summary: Untag an image
+ // description: Untag an image
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: repo
+ // type: string
+ // description: the repository to untag
+ // - in: query
+ // name: tag
+ // type: string
+ // description: the name of the tag to untag
+ // produces:
+ // - application/json
+ // responses:
+ // 201:
+ // description: no error
+ // 400:
+ // $ref: '#/responses/BadParamError'
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 409:
+ // $ref: '#/responses/ConflictError'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/{name:.*}/untag"), s.APIHandler(libpod.UntagImage)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index af2330665..a49bf1f63 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -37,7 +37,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// description: attributes for creating a pod
// schema:
// type: object
- // $ref: "#/definitions/PodCreateConfig"
+ // $ref: "#/definitions/PodSpecGenerator"
// responses:
// 200:
// $ref: "#/definitions/IdResponse"
@@ -81,8 +81,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// type: boolean
// description : force removal of a running pod by first stopping all containers, then removing all containers in the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodRmReport'
// 400:
// $ref: "#/responses/BadParamError"
// 404:
@@ -146,8 +146,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// description: signal to be sent to pod
// default: SIGKILL
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: "#/responses/PodKillReport"
// 400:
// $ref: "#/responses/BadParamError"
// 404:
@@ -170,8 +170,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodPauseReport'
// 404:
// $ref: "#/responses/NoSuchPod"
// 500:
@@ -189,8 +189,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodRestartReport'
// 404:
// $ref: "#/responses/NoSuchPod"
// 500:
@@ -208,8 +208,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodStartReport'
// 304:
// $ref: "#/responses/PodAlreadyStartedError"
// 404:
@@ -233,8 +233,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// type: integer
// description: timeout
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodStopReport'
// 304:
// $ref: "#/responses/PodAlreadyStoppedError"
// 400:
@@ -256,12 +256,43 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodUnpauseReport'
// 404:
// $ref: "#/responses/NoSuchPod"
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name}/unpause"), s.APIHandler(libpod.PodUnpause)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/pods/{name}/top pods topPod
+ // ---
+ // summary: List processes
+ // description: List processes running inside a pod
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: |
+ // Name of pod to query for processes
+ // - in: query
+ // name: stream
+ // type: boolean
+ // default: true
+ // description: Stream the output
+ // - in: query
+ // name: ps_args
+ // type: string
+ // default: -ef
+ // description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
+ // responses:
+ // 200:
+ // $ref: "#/responses/DocsPodTopResponse"
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/pods/{name}/top"), s.APIHandler(libpod.PodTop)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index 9156f3f8a..2433a6a05 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -2,6 +2,7 @@ package server
import (
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
)
@@ -178,6 +179,6 @@ type swagVolumeListResponse struct {
type swagHealthCheckRunResponse struct {
// in:body
Body struct {
- libpod.HealthCheckResults
+ define.HealthCheckResults
}
}
diff --git a/pkg/apparmor/apparmor.go b/pkg/apparmor/apparmor.go
index 1e824550d..8e17361cb 100644
--- a/pkg/apparmor/apparmor.go
+++ b/pkg/apparmor/apparmor.go
@@ -3,14 +3,15 @@ package apparmor
import (
"errors"
+ "github.com/containers/common/pkg/config"
libpodVersion "github.com/containers/libpod/version"
)
var (
// DefaultLipodProfilePrefix is used for version-independent presence checks.
- DefaultLipodProfilePrefix = "libpod-default" + "-"
+ DefaultLipodProfilePrefix = config.DefaultApparmorProfile
// DefaultLibpodProfile is the name of default libpod AppArmor profile.
- DefaultLibpodProfile = DefaultLipodProfilePrefix + libpodVersion.Version
+ DefaultLibpodProfile = DefaultLipodProfilePrefix + "-" + libpodVersion.Version
// ErrApparmorUnsupported indicates that AppArmor support is not supported.
ErrApparmorUnsupported = errors.New("AppArmor is not supported")
// ErrApparmorRootless indicates that AppArmor support is not supported in rootless mode.
diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go
new file mode 100644
index 000000000..84924587b
--- /dev/null
+++ b/pkg/bindings/containers/checkpoint.go
@@ -0,0 +1,79 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+ "net/url"
+ "strconv"
+
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+// Checkpoint checkpoints the given container (identified by nameOrId). All additional
+// options are options and allow for more fine grained control of the checkpoint process.
+func Checkpoint(ctx context.Context, nameOrId string, keep, leaveRunning, tcpEstablished, ignoreRootFS *bool, export *string) (*entities.CheckpointReport, error) {
+ var report entities.CheckpointReport
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ if keep != nil {
+ params.Set("keep", strconv.FormatBool(*keep))
+ }
+ if leaveRunning != nil {
+ params.Set("leaveRunning", strconv.FormatBool(*leaveRunning))
+ }
+ if tcpEstablished != nil {
+ params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished))
+ }
+ if ignoreRootFS != nil {
+ params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS))
+ }
+ if export != nil {
+ params.Set("export", *export)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ return &report, response.Process(&report)
+}
+
+// Restore restores a checkpointed container to running. The container is identified by the nameOrId option. All
+// additional options are optional and allow finer control of the restore processs.
+func Restore(ctx context.Context, nameOrId string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) {
+ var report entities.RestoreReport
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ if keep != nil {
+ params.Set("keep", strconv.FormatBool(*keep))
+ }
+ if tcpEstablished != nil {
+ params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished))
+ }
+ if ignoreRootFS != nil {
+ params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS))
+ }
+ if ignoreStaticIP != nil {
+ params.Set("ignoreStaticIP", strconv.FormatBool(*ignoreStaticIP))
+ }
+ if ignoreStaticMAC != nil {
+ params.Set("ignoreStaticMAC", strconv.FormatBool(*ignoreStaticMAC))
+ }
+ if name != nil {
+ params.Set("name", *name)
+ }
+ if importArchive != nil {
+ params.Set("import", *importArchive)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ return &report, response.Process(&report)
+}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 231b6f232..49a2dfd58 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -2,12 +2,14 @@ package containers
import (
"context"
+ "io"
"net/http"
"net/url"
"strconv"
+ "strings"
- "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers"
lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
)
@@ -106,7 +108,7 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error {
// or a partial/full ID. The size bool determines whether the size of the container's root filesystem
// should be calculated. Calculating the size of a container requires extra work from the filesystem and
// is therefore slower.
-func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectContainerData, error) {
+func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectContainerData, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
@@ -119,7 +121,7 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectC
if err != nil {
return nil, err
}
- inspect := libpod.InspectContainerData{}
+ inspect := define.InspectContainerData{}
return &inspect, response.Process(&inspect)
}
@@ -194,7 +196,40 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error {
}
func Stats() {}
-func Top() {}
+
+// Top gathers statistics about the running processes in a container. The nameOrID can be a container name
+// or a partial/full ID. The descriptors allow for specifying which data to collect from the process.
+func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+
+ if len(descriptors) > 0 {
+ // flatten the slice into one string
+ params.Set("ps_args", strings.Join(descriptors, ","))
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+
+ body := handlers.ContainerTopOKBody{}
+ if err = response.Process(&body); err != nil {
+ return nil, err
+ }
+
+ // handlers.ContainerTopOKBody{} returns a slice of slices where each cell in the top table is an item.
+ // In libpod land, we're just using a slice with cells being split by tabs, which allows for an idiomatic
+ // usage of the tabwriter.
+ topOutput := []string{strings.Join(body.Titles, "\t")}
+ for _, out := range body.Processes {
+ topOutput = append(topOutput, strings.Join(out, "\t"))
+ }
+
+ return topOutput, err
+}
// Unpause resumes the given paused container. The nameOrID can be a container name
// or a partial/full ID.
@@ -262,3 +297,22 @@ func Stop(ctx context.Context, nameOrID string, timeout *uint) error {
}
return response.Process(nil)
}
+
+// Export creates a tarball of the given name or ID of a container. It
+// requires an io.Writer be provided to write the tarball.
+func Export(ctx context.Context, nameOrID string, w io.Writer) error {
+ params := url.Values{}
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/export", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ if response.StatusCode/100 == 2 {
+ _, err = io.Copy(w, response.Body)
+ return err
+ }
+ return response.Process(nil)
+}
diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go
new file mode 100644
index 000000000..48f9ed697
--- /dev/null
+++ b/pkg/bindings/containers/exec.go
@@ -0,0 +1,71 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+ "strings"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+var json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+// ExecCreate creates a new exec session in an existing container.
+// The exec session will not be started; that is done with ExecStart.
+// Returns ID of new exec session, or an error if one occurred.
+func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreateConfig) (string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ if config == nil {
+ return "", errors.Errorf("must provide a configuration for exec session")
+ }
+
+ requestJSON, err := json.Marshal(config)
+ if err != nil {
+ return "", errors.Wrapf(err, "error marshalling exec config to JSON")
+ }
+ jsonReader := strings.NewReader(string(requestJSON))
+
+ resp, err := conn.DoRequest(jsonReader, http.MethodPost, "/containers/%s/exec", nil, nameOrID)
+ if err != nil {
+ return "", err
+ }
+
+ respStruct := new(handlers.ExecCreateResponse)
+ if err := resp.Process(respStruct); err != nil {
+ return "", err
+ }
+
+ return respStruct.ID, nil
+}
+
+// ExecInspect inspects an existing exec session, returning detailed information
+// about it.
+func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSession, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ logrus.Debugf("Inspecting session ID %q", sessionID)
+
+ resp, err := conn.DoRequest(nil, http.MethodGet, "/exec/%s/json", nil, sessionID)
+ if err != nil {
+ return nil, err
+ }
+
+ respStruct := new(define.InspectExecSession)
+ if err := resp.Process(respStruct); err != nil {
+ return nil, err
+ }
+
+ return respStruct, nil
+}
diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go
index 85cc2814c..2b783ac73 100644
--- a/pkg/bindings/containers/healthcheck.go
+++ b/pkg/bindings/containers/healthcheck.go
@@ -4,19 +4,19 @@ import (
"context"
"net/http"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings"
)
// RunHealthCheck executes the container's healthcheck and returns the health status of the
// container.
-func RunHealthCheck(ctx context.Context, nameOrID string) (*libpod.HealthCheckResults, error) {
+func RunHealthCheck(ctx context.Context, nameOrID string) (*define.HealthCheckResults, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
var (
- status libpod.HealthCheckResults
+ status define.HealthCheckResults
)
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/healthcheck", nil, nameOrID)
if err != nil {
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index e67965042..1b3df609b 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -3,15 +3,16 @@ package images
import (
"context"
"errors"
+ "fmt"
"io"
"net/http"
"net/url"
"strconv"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/inspect"
)
// Exists a lightweight way to determine if an image exists in local storage. It returns a
@@ -56,7 +57,7 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entit
// Get performs an image inspect. To have the on-disk size of the image calculated, you can
// use the optional size parameter.
-func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageData, error) {
+func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.ImageData, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
@@ -65,7 +66,7 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageD
if size != nil {
params.Set("size", strconv.FormatBool(*size))
}
- inspectedData := inspect.ImageData{}
+ inspectedData := entities.ImageData{}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID)
if err != nil {
return &inspectedData, err
@@ -91,11 +92,11 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse,
return history, response.Process(&history)
}
-func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
- var id handlers.IDResponse
+func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadReport, error) {
+ var report entities.ImageLoadReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return "", err
+ return nil, err
}
params := url.Values{}
if name != nil {
@@ -103,9 +104,9 @@ func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
}
response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params)
if err != nil {
- return "", err
+ return nil, err
}
- return id.ID, response.Process(&id)
+ return &report, response.Process(&report)
}
// Remove deletes an image from local storage. The optional force parameter will forcibly remove
@@ -145,16 +146,17 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c
if err != nil {
return err
}
- if err := response.Process(nil); err != nil {
+
+ if response.StatusCode/100 == 2 || response.StatusCode/100 == 3 {
+ _, err = io.Copy(w, response.Body)
return err
}
- _, err = io.Copy(w, response.Body)
- return err
+ return nil
}
// Prune removes unused images from local storage. The optional filters can be used to further
// define which images should be pruned.
-func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
+func Prune(ctx context.Context, all *bool, filters map[string][]string) ([]string, error) {
var (
deleted []string
)
@@ -163,6 +165,9 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
return nil, err
}
params := url.Values{}
+ if all != nil {
+ params.Set("all", strconv.FormatBool(*all))
+ }
if filters != nil {
stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
@@ -174,7 +179,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
if err != nil {
return deleted, err
}
- return deleted, response.Process(nil)
+ return deleted, response.Process(&deleted)
}
// Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required.
@@ -193,19 +198,35 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error {
return response.Process(nil)
}
+// Untag removes a name from locally-stored image. Both the tag and repo parameters are required.
+func Untag(ctx context.Context, nameOrID, tag, repo string) error {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ params := url.Values{}
+ params.Set("tag", tag)
+ params.Set("repo", repo)
+ response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/untag", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
func Build(nameOrId string) {}
// Imports adds the given image to the local image store. This can be done by file and the given reader
// or via the url parameter. Additional metadata can be associated with the image by using the changes and
// message parameters. The image can also be tagged given a reference. One of url OR r must be provided.
-func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (string, error) {
- var id handlers.IDResponse
+func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (*entities.ImageImportReport, error) {
+ var report entities.ImageImportReport
if r != nil && u != nil {
- return "", errors.New("url and r parameters cannot be used together")
+ return nil, errors.New("url and r parameters cannot be used together")
}
conn, err := bindings.GetClient(ctx)
if err != nil {
- return "", err
+ return nil, err
}
params := url.Values{}
for _, change := range changes {
@@ -222,7 +243,68 @@ func Import(ctx context.Context, changes []string, message, reference, u *string
}
response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params)
if err != nil {
- return "", err
+ return nil, err
}
- return id.ID, response.Process(&id)
+ return &report, response.Process(&report)
+}
+
+// Pull is the binding for libpod's v2 endpoints for pulling images. Note that
+// `rawImage` must be a reference to a registry (i.e., of docker transport or be
+// normalized to one). Other transports are rejected as they do not make sense
+// in a remote context.
+func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ params.Set("reference", rawImage)
+ params.Set("credentials", options.Credentials)
+ params.Set("overrideArch", options.OverrideArch)
+ params.Set("overrideOS", options.OverrideOS)
+ if options.TLSVerify != types.OptionalBoolUndefined {
+ val := bool(options.TLSVerify == types.OptionalBoolTrue)
+ params.Set("tlsVerify", strconv.FormatBool(val))
+ }
+ params.Set("allTags", strconv.FormatBool(options.AllTags))
+
+ response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params)
+ if err != nil {
+ return nil, err
+ }
+
+ reports := []handlers.LibpodImagesPullReport{}
+ if err := response.Process(&reports); err != nil {
+ return nil, err
+ }
+
+ pulledImages := []string{}
+ for _, r := range reports {
+ pulledImages = append(pulledImages, r.ID)
+ }
+
+ return pulledImages, nil
+}
+
+// Push is the binding for libpod's v2 endpoints for push images. Note that
+// `source` must be a refering to an image in the remote's container storage.
+// The destination must be a reference to a registry (i.e., of docker transport
+// or be normalized to one). Other transports are rejected as they do not make
+// sense in a remote context.
+func Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ params := url.Values{}
+ params.Set("credentials", options.Credentials)
+ params.Set("destination", destination)
+ if options.TLSVerify != types.OptionalBoolUndefined {
+ val := bool(options.TLSVerify == types.OptionalBoolTrue)
+ params.Set("tlsVerify", strconv.FormatBool(val))
+ }
+
+ path := fmt.Sprintf("/images/%s/push", source)
+ _, err = conn.DoRequest(nil, http.MethodPost, path, params)
+ return err
}
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index 1a8c31be1..83847614a 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -5,14 +5,33 @@ import (
"net/http"
"net/url"
"strconv"
+ "strings"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
+ jsoniter "github.com/json-iterator/go"
)
-func CreatePod() error {
- // TODO
- return bindings.ErrNotImplemented
+func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator) (*entities.PodCreateReport, error) {
+ var (
+ pcr entities.PodCreateReport
+ )
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ specgenString, err := jsoniter.MarshalToString(s)
+ if err != nil {
+ return nil, err
+ }
+ stringReader := strings.NewReader(specgenString)
+ response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil)
+ if err != nil {
+ return nil, err
+ }
+ return &pcr, response.Process(&pcr)
}
// Exists is a lightweight method to determine if a pod exists in local storage
@@ -29,25 +48,30 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) {
}
// Inspect returns low-level information about the given pod.
-func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) {
+func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, error) {
+ var (
+ report entities.PodInspectReport
+ )
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
- inspect := libpod.PodInspect{}
response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nameOrID)
if err != nil {
- return &inspect, err
+ return nil, err
}
- return &inspect, response.Process(&inspect)
+ return &report, response.Process(&report)
}
// Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter
// can be used to override SIGTERM.
-func Kill(ctx context.Context, nameOrID string, signal *string) error {
+func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKillReport, error) {
+ var (
+ report entities.PodKillReport
+ )
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
params := url.Values{}
if signal != nil {
@@ -55,22 +79,23 @@ func Kill(ctx context.Context, nameOrID string, signal *string) error {
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ return &report, response.Process(&report)
}
// Pause pauses all running containers in a given pod.
-func Pause(ctx context.Context, nameOrID string) error {
+func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, error) {
+ var report entities.PodPauseReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ return &report, response.Process(&report)
}
// Prune removes all non-running pods in local storage.
@@ -88,9 +113,9 @@ func Prune(ctx context.Context) error {
// List returns all pods in local storage. The optional filters parameter can
// be used to refine which pods should be listed.
-func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspect, error) {
+func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPodsReport, error) {
var (
- inspect []*libpod.PodInspect
+ podsReports []*entities.ListPodsReport
)
conn, err := bindings.GetClient(ctx)
if err != nil {
@@ -106,30 +131,32 @@ func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspec
}
response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params)
if err != nil {
- return inspect, err
+ return podsReports, err
}
- return inspect, response.Process(&inspect)
+ return podsReports, response.Process(&podsReports)
}
// Restart restarts all containers in a pod.
-func Restart(ctx context.Context, nameOrID string) error {
+func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, error) {
+ var report entities.PodRestartReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ return &report, response.Process(&report)
}
// Remove deletes a Pod from from local storage. The optional force parameter denotes
// that the Pod can be removed even if in a running state.
-func Remove(ctx context.Context, nameOrID string, force *bool) error {
+func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmReport, error) {
+ var report entities.PodRmReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
params := url.Values{}
if force != nil {
@@ -137,22 +164,27 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error {
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ return &report, response.Process(&report)
}
// Start starts all containers in a pod.
-func Start(ctx context.Context, nameOrID string) error {
+func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, error) {
+ var report entities.PodStartReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/start", nil, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ if response.StatusCode == http.StatusNotModified {
+ report.Id = nameOrID
+ return &report, nil
+ }
+ return &report, response.Process(&report)
}
func Stats() error {
@@ -162,10 +194,11 @@ func Stats() error {
// Stop stops all containers in a Pod. The optional timeout parameter can be
// used to override the timeout before the container is killed.
-func Stop(ctx context.Context, nameOrID string, timeout *int) error {
+func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStopReport, error) {
+ var report entities.PodStopReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
params := url.Values{}
if timeout != nil {
@@ -173,25 +206,59 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) error {
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ if response.StatusCode == http.StatusNotModified {
+ report.Id = nameOrID
+ return &report, nil
+ }
+ return &report, response.Process(&report)
}
-func Top() error {
- // TODO
- return bindings.ErrNotImplemented // nolint:typecheck
+// Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name
+// or a partial/full ID. The descriptors allow for specifying which data to collect from each process.
+func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+
+ if len(descriptors) > 0 {
+ // flatten the slice into one string
+ params.Set("ps_args", strings.Join(descriptors, ","))
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/top", params, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+
+ body := handlers.PodTopOKBody{}
+ if err = response.Process(&body); err != nil {
+ return nil, err
+ }
+
+ // handlers.PodTopOKBody{} returns a slice of slices where each cell in the top table is an item.
+ // In libpod land, we're just using a slice with cells being split by tabs, which allows for an idiomatic
+ // usage of the tabwriter.
+ topOutput := []string{strings.Join(body.Titles, "\t")}
+ for _, out := range body.Processes {
+ topOutput = append(topOutput, strings.Join(out, "\t"))
+ }
+
+ return topOutput, err
}
// Unpause unpauses all paused containers in a Pod.
-func Unpause(ctx context.Context, nameOrID string) error {
+func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, error) {
+ var report entities.PodUnpauseReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nameOrID)
if err != nil {
- return err
+ return nil, err
}
- return response.Process(nil)
+ return &report, response.Process(&report)
}
diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go
index f5465c803..a31181958 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -1,12 +1,12 @@
package test_bindings
import (
- "github.com/containers/libpod/libpod/define"
"net/http"
"strconv"
"strings"
"time"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/specgen"
@@ -34,7 +34,7 @@ var _ = Describe("Podman containers ", func() {
AfterEach(func() {
s.Kill()
- //bt.cleanup()
+ bt.cleanup()
})
It("podman pause a bogus container", func() {
@@ -380,4 +380,34 @@ var _ = Describe("Podman containers ", func() {
_, err = time.Parse(time.RFC1123Z, o)
Expect(err).To(BeNil())
})
+
+ It("podman top", func() {
+ var name = "top"
+ cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ // By name
+ _, err = containers.Top(bt.conn, name, nil)
+ Expect(err).To(BeNil())
+
+ // By id
+ _, err = containers.Top(bt.conn, cid, nil)
+ Expect(err).To(BeNil())
+
+ // With descriptors
+ output, err := containers.Top(bt.conn, cid, []string{"user,pid,hpid"})
+ Expect(err).To(BeNil())
+ header := strings.Split(output[0], "\t")
+ for _, d := range []string{"USER", "PID", "HPID"} {
+ Expect(d).To(BeElementOf(header))
+ }
+
+ // With bogus ID
+ _, err = containers.Top(bt.conn, "IdoNotExist", nil)
+ Expect(err).ToNot(BeNil())
+
+ // With bogus descriptors
+ _, err = containers.Top(bt.conn, cid, []string{"Me,Neither"})
+ Expect(err).To(BeNil())
+ })
})
diff --git a/pkg/bindings/test/exec_test.go b/pkg/bindings/test/exec_test.go
new file mode 100644
index 000000000..1ef2197b6
--- /dev/null
+++ b/pkg/bindings/test/exec_test.go
@@ -0,0 +1,77 @@
+package test_bindings
+
+import (
+ "time"
+
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/bindings/containers"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Podman containers exec", func() {
+ var (
+ bt *bindingTest
+ s *gexec.Session
+ )
+
+ BeforeEach(func() {
+ bt = newBindingTest()
+ bt.RestoreImagesFromCache()
+ s = bt.startAPIService()
+ time.Sleep(1 * time.Second)
+ err := bt.NewConnection()
+ Expect(err).To(BeNil())
+ })
+
+ AfterEach(func() {
+ s.Kill()
+ bt.cleanup()
+ })
+
+ It("Podman exec create makes an exec session", func() {
+ name := "testCtr"
+ cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ execConfig := new(handlers.ExecCreateConfig)
+ execConfig.Cmd = []string{"echo", "hello world"}
+
+ sessionID, err := containers.ExecCreate(bt.conn, name, execConfig)
+ Expect(err).To(BeNil())
+ Expect(sessionID).To(Not(Equal("")))
+
+ inspectOut, err := containers.ExecInspect(bt.conn, sessionID)
+ Expect(err).To(BeNil())
+ Expect(inspectOut.ContainerID).To(Equal(cid))
+ Expect(inspectOut.ProcessConfig.Entrypoint).To(Equal("echo"))
+ Expect(len(inspectOut.ProcessConfig.Arguments)).To(Equal(1))
+ Expect(inspectOut.ProcessConfig.Arguments[0]).To(Equal("hello world"))
+ })
+
+ It("Podman exec create with bad command fails", func() {
+ name := "testCtr"
+ _, err := bt.RunTopContainer(&name, &bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ execConfig := new(handlers.ExecCreateConfig)
+
+ _, err = containers.ExecCreate(bt.conn, name, execConfig)
+ Expect(err).To(Not(BeNil()))
+ })
+
+ It("Podman exec create with invalid container fails", func() {
+ execConfig := new(handlers.ExecCreateConfig)
+ execConfig.Cmd = []string{"echo", "hello world"}
+
+ _, err := containers.ExecCreate(bt.conn, "doesnotexist", execConfig)
+ Expect(err).To(Not(BeNil()))
+ })
+
+ It("Podman exec inspect on invalid session fails", func() {
+ _, err := containers.ExecInspect(bt.conn, "0000000000000000000000000000000000000000000000000000000000000000")
+ Expect(err).To(Not(BeNil()))
+ })
+})
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 5e4cfe7be..992720196 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/bindings/images"
+ "github.com/containers/libpod/pkg/domain/entities"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -16,22 +17,22 @@ import (
var _ = Describe("Podman images", func() {
var (
- //tempdir string
- //err error
- //podmanTest *PodmanTestIntegration
+ // tempdir string
+ // err error
+ // podmanTest *PodmanTestIntegration
bt *bindingTest
s *gexec.Session
err error
)
BeforeEach(func() {
- //tempdir, err = CreateTempDirInTempDir()
- //if err != nil {
+ // tempdir, err = CreateTempDirInTempDir()
+ // if err != nil {
// os.Exit(1)
- //}
- //podmanTest = PodmanTestCreate(tempdir)
- //podmanTest.Setup()
- //podmanTest.SeedImages()
+ // }
+ // podmanTest = PodmanTestCreate(tempdir)
+ // podmanTest.Setup()
+ // podmanTest.SeedImages()
bt = newBindingTest()
bt.RestoreImagesFromCache()
s = bt.startAPIService()
@@ -41,12 +42,13 @@ var _ = Describe("Podman images", func() {
})
AfterEach(func() {
- //podmanTest.Cleanup()
- //f := CurrentGinkgoTestDescription()
- //processTestResult(f)
+ // podmanTest.Cleanup()
+ // f := CurrentGinkgoTestDescription()
+ // processTestResult(f)
s.Kill()
bt.cleanup()
})
+
It("inspect image", func() {
// Inspect invalid image be 404
_, err = images.GetImage(bt.conn, "foobar5000", nil)
@@ -71,7 +73,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
// TODO it looks like the images API alwaays returns size regardless
// of bool or not. What should we do ?
- //Expect(data.Size).To(BeZero())
+ // Expect(data.Size).To(BeZero())
// Enabling the size parameter should result in size being populated
data, err = images.GetImage(bt.conn, alpine.name, &bindings.PTrue)
@@ -142,7 +144,7 @@ var _ = Describe("Podman images", func() {
err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName)
Expect(err).To(BeNil())
- //Validates if name updates when the image is retagged.
+ // Validates if name updates when the image is retagged.
_, err := images.GetImage(bt.conn, "alpine:demo", nil)
Expect(err).To(BeNil())
@@ -165,7 +167,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
Expect(len(imageSummary)).To(Equal(3))
- //Validate the image names.
+ // Validate the image names.
var names []string
for _, i := range imageSummary {
names = append(names, i.RepoTags...)
@@ -217,7 +219,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
names, err := images.Load(bt.conn, f, nil)
Expect(err).To(BeNil())
- Expect(names).To(Equal(alpine.name))
+ Expect(names.Name).To(Equal(alpine.name))
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
Expect(exists).To(BeTrue())
@@ -233,7 +235,7 @@ var _ = Describe("Podman images", func() {
newName := "quay.io/newname:fizzle"
names, err = images.Load(bt.conn, f, &newName)
Expect(err).To(BeNil())
- Expect(names).To(Equal(alpine.name))
+ Expect(names.Name).To(Equal(alpine.name))
exists, err = images.Exists(bt.conn, newName)
Expect(err).To(BeNil())
Expect(exists).To(BeTrue())
@@ -289,6 +291,7 @@ var _ = Describe("Podman images", func() {
Expect(data.Comment).To(Equal(testMessage))
})
+
It("History Image", func() {
// a bogus name should return a 404
_, err := images.History(bt.conn, "foobar")
@@ -343,4 +346,32 @@ var _ = Describe("Podman images", func() {
Expect(len(imgs)).To(BeNumerically(">=", 1))
})
+ It("Prune images", func() {
+ trueBoxed := true
+ results, err := images.Prune(bt.conn, &trueBoxed, nil)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(results)).To(BeNumerically(">", 0))
+ Expect(results).To(ContainElement("docker.io/library/alpine:latest"))
+ })
+
+ // TODO: we really need to extent to pull tests once we have a more sophisticated CI.
+ It("Image Pull", func() {
+ rawImage := "docker.io/library/busybox:latest"
+
+ pulledImages, err := images.Pull(bt.conn, rawImage, entities.ImagePullOptions{})
+ Expect(err).To(BeNil())
+ Expect(len(pulledImages)).To(Equal(1))
+
+ exists, err := images.Exists(bt.conn, rawImage)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+
+ // Make sure the normalization AND the full-transport reference works.
+ _, err = images.Pull(bt.conn, "docker://"+rawImage, entities.ImagePullOptions{})
+ Expect(err).To(BeNil())
+
+ // The v2 endpoint only supports the docker transport. Let's see if that's really true.
+ _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", entities.ImagePullOptions{})
+ Expect(err).To(Not(BeNil()))
+ })
})
diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go
index e94048a9c..2599ec7ef 100644
--- a/pkg/bindings/test/pods_test.go
+++ b/pkg/bindings/test/pods_test.go
@@ -2,11 +2,13 @@ package test_bindings
import (
"net/http"
+ "strings"
"time"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/pods"
+ "github.com/containers/libpod/pkg/specgen"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -71,7 +73,7 @@ var _ = Describe("Podman pods", func() {
Expect(len(podSummary)).To(Equal(2))
var names []string
for _, i := range podSummary {
- names = append(names, i.Config.Name)
+ names = append(names, i.Name)
}
Expect(StringInSlice(newpod, names)).To(BeTrue())
Expect(StringInSlice("newpod2", names)).To(BeTrue())
@@ -79,9 +81,7 @@ var _ = Describe("Podman pods", func() {
// The test validates the list pod endpoint with passing filters as the params.
It("List pods with filters", func() {
- var (
- newpod2 string = "newpod2"
- )
+ newpod2 := "newpod2"
bt.Podcreate(&newpod2)
_, err = bt.RunTopContainer(nil, &bindings.PTrue, &newpod)
Expect(err).To(BeNil())
@@ -109,13 +109,14 @@ var _ = Describe("Podman pods", func() {
Expect(len(filteredPods)).To(BeNumerically("==", 1))
var names []string
for _, i := range filteredPods {
- names = append(names, i.Config.Name)
+ names = append(names, i.Name)
}
Expect(StringInSlice("newpod2", names)).To(BeTrue())
// Validate list pod with id filter
filters = make(map[string][]string)
response, err := pods.Inspect(bt.conn, newpod)
+ Expect(err).To(BeNil())
id := response.Config.ID
filters["id"] = []string{id}
filteredPods, err = pods.List(bt.conn, filters)
@@ -123,7 +124,7 @@ var _ = Describe("Podman pods", func() {
Expect(len(filteredPods)).To(BeNumerically("==", 1))
names = names[:0]
for _, i := range filteredPods {
- names = append(names, i.Config.Name)
+ names = append(names, i.Name)
}
Expect(StringInSlice("newpod", names)).To(BeTrue())
@@ -134,7 +135,7 @@ var _ = Describe("Podman pods", func() {
Expect(len(filteredPods)).To(BeNumerically("==", 1))
names = names[:0]
for _, i := range filteredPods {
- names = append(names, i.Config.Name)
+ names = append(names, i.Name)
}
Expect(StringInSlice("newpod", names)).To(BeTrue())
})
@@ -157,7 +158,7 @@ var _ = Describe("Podman pods", func() {
// TODO fix this
Skip("Pod behavior is jacked right now.")
// Pause invalid container
- err := pods.Pause(bt.conn, "dummyName")
+ _, err := pods.Pause(bt.conn, "dummyName")
Expect(err).ToNot(BeNil())
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusNotFound))
@@ -169,9 +170,10 @@ var _ = Describe("Podman pods", func() {
// Binding needs to be modified to inspect the pod state.
// Since we don't have a pod state we inspect the states of the containers within the pod.
// Pause a valid container
- err = pods.Pause(bt.conn, newpod)
+ _, err = pods.Pause(bt.conn, newpod)
Expect(err).To(BeNil())
response, err := pods.Inspect(bt.conn, newpod)
+ Expect(err).To(BeNil())
Expect(response.State.Status).To(Equal(define.PodStatePaused))
for _, i := range response.Containers {
Expect(define.StringToContainerStatus(i.State)).
@@ -179,9 +181,10 @@ var _ = Describe("Podman pods", func() {
}
// Unpause a valid container
- err = pods.Unpause(bt.conn, newpod)
+ _, err = pods.Unpause(bt.conn, newpod)
Expect(err).To(BeNil())
response, err = pods.Inspect(bt.conn, newpod)
+ Expect(err).To(BeNil())
Expect(response.State.Status).To(Equal(define.PodStateRunning))
for _, i := range response.Containers {
Expect(define.StringToContainerStatus(i.State)).
@@ -191,28 +194,29 @@ var _ = Describe("Podman pods", func() {
It("start stop restart pod", func() {
// Start an invalid pod
- err = pods.Start(bt.conn, "dummyName")
+ _, err = pods.Start(bt.conn, "dummyName")
Expect(err).ToNot(BeNil())
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// Stop an invalid pod
- err = pods.Stop(bt.conn, "dummyName", nil)
+ _, err = pods.Stop(bt.conn, "dummyName", nil)
Expect(err).ToNot(BeNil())
code, _ = bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// Restart an invalid pod
- err = pods.Restart(bt.conn, "dummyName")
+ _, err = pods.Restart(bt.conn, "dummyName")
Expect(err).ToNot(BeNil())
code, _ = bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// Start a valid pod and inspect status of each container
- err = pods.Start(bt.conn, newpod)
+ _, err = pods.Start(bt.conn, newpod)
Expect(err).To(BeNil())
response, err := pods.Inspect(bt.conn, newpod)
+ Expect(err).To(BeNil())
Expect(response.State.Status).To(Equal(define.PodStateRunning))
for _, i := range response.Containers {
Expect(define.StringToContainerStatus(i.State)).
@@ -220,11 +224,11 @@ var _ = Describe("Podman pods", func() {
}
// Start an already running pod
- err = pods.Start(bt.conn, newpod)
+ _, err = pods.Start(bt.conn, newpod)
Expect(err).To(BeNil())
// Stop the running pods
- err = pods.Stop(bt.conn, newpod, nil)
+ _, err = pods.Stop(bt.conn, newpod, nil)
Expect(err).To(BeNil())
response, _ = pods.Inspect(bt.conn, newpod)
Expect(response.State.Status).To(Equal(define.PodStateExited))
@@ -234,10 +238,10 @@ var _ = Describe("Podman pods", func() {
}
// Stop an already stopped pod
- err = pods.Stop(bt.conn, newpod, nil)
+ _, err = pods.Stop(bt.conn, newpod, nil)
Expect(err).To(BeNil())
- err = pods.Restart(bt.conn, newpod)
+ _, err = pods.Restart(bt.conn, newpod)
Expect(err).To(BeNil())
response, _ = pods.Inspect(bt.conn, newpod)
Expect(response.State.Status).To(Equal(define.PodStateRunning))
@@ -262,11 +266,12 @@ var _ = Describe("Podman pods", func() {
// Prune only one pod which is in exited state.
// Start then stop a pod.
// pod moves to exited state one pod should be pruned now.
- err = pods.Start(bt.conn, newpod)
+ _, err = pods.Start(bt.conn, newpod)
Expect(err).To(BeNil())
- err = pods.Stop(bt.conn, newpod, nil)
+ _, err = pods.Stop(bt.conn, newpod, nil)
Expect(err).To(BeNil())
response, err := pods.Inspect(bt.conn, newpod)
+ Expect(err).To(BeNil())
Expect(response.State.Status).To(Equal(define.PodStateExited))
err = pods.Prune(bt.conn)
Expect(err).To(BeNil())
@@ -276,21 +281,23 @@ var _ = Describe("Podman pods", func() {
// Test prune all pods in exited state.
bt.Podcreate(&newpod)
- err = pods.Start(bt.conn, newpod)
+ _, err = pods.Start(bt.conn, newpod)
Expect(err).To(BeNil())
- err = pods.Start(bt.conn, newpod2)
+ _, err = pods.Start(bt.conn, newpod2)
Expect(err).To(BeNil())
- err = pods.Stop(bt.conn, newpod, nil)
+ _, err = pods.Stop(bt.conn, newpod, nil)
Expect(err).To(BeNil())
response, err = pods.Inspect(bt.conn, newpod)
+ Expect(err).To(BeNil())
Expect(response.State.Status).To(Equal(define.PodStateExited))
for _, i := range response.Containers {
Expect(define.StringToContainerStatus(i.State)).
To(Equal(define.ContainerStateStopped))
}
- err = pods.Stop(bt.conn, newpod2, nil)
+ _, err = pods.Stop(bt.conn, newpod2, nil)
Expect(err).To(BeNil())
response, err = pods.Inspect(bt.conn, newpod2)
+ Expect(err).To(BeNil())
Expect(response.State.Status).To(Equal(define.PodStateExited))
for _, i := range response.Containers {
Expect(define.StringToContainerStatus(i.State)).
@@ -302,4 +309,44 @@ var _ = Describe("Podman pods", func() {
Expect(err).To(BeNil())
Expect(len(podSummary)).To(Equal(0))
})
+
+ It("simple create pod", func() {
+ ps := specgen.PodSpecGenerator{}
+ ps.Name = "foobar"
+ _, err := pods.CreatePodFromSpec(bt.conn, &ps)
+ Expect(err).To(BeNil())
+
+ exists, err := pods.Exists(bt.conn, "foobar")
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+ })
+
+ // Test validates the pod top bindings
+ It("pod top", func() {
+ var name string = "podA"
+
+ bt.Podcreate(&name)
+ _, err := pods.Start(bt.conn, name)
+ Expect(err).To(BeNil())
+
+ // By name
+ _, err = pods.Top(bt.conn, name, nil)
+ Expect(err).To(BeNil())
+
+ // With descriptors
+ output, err := pods.Top(bt.conn, name, []string{"user,pid,hpid"})
+ Expect(err).To(BeNil())
+ header := strings.Split(output[0], "\t")
+ for _, d := range []string{"USER", "PID", "HPID"} {
+ Expect(d).To(BeElementOf(header))
+ }
+
+ // With bogus ID
+ _, err = pods.Top(bt.conn, "IdoNotExist", nil)
+ Expect(err).ToNot(BeNil())
+
+ // With bogus descriptors
+ _, err = pods.Top(bt.conn, name, []string{"Me,Neither"})
+ Expect(err).ToNot(BeNil())
+ })
})
diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index 7f80b782a..78f592d32 100644
--- a/pkg/adapter/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -1,6 +1,4 @@
-// +build !remoteclient
-
-package adapter
+package checkpoint
import (
"context"
@@ -42,9 +40,9 @@ func crImportFromJSON(filePath string, v interface{}) error {
return nil
}
-// crImportCheckpoint it the function which imports the information
+// CRImportCheckpoint it the function which imports the information
// from checkpoint tarball and re-creates the container from that information
-func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) {
+func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) {
// First get the container definition from the
// tarball to a temporary directory
archiveFile, err := os.Open(input)
@@ -114,7 +112,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
return nil, err
}
- _, err = runtime.ImageRuntime().New(ctx, config.RootfsImageName, rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, nil, util.PullImageMissing)
+ _, err = runtime.ImageRuntime().New(ctx, config.RootfsImageName, rtc.Engine.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, nil, util.PullImageMissing)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 8b7406ae8..6b4bb9ba2 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -1,6 +1,8 @@
package entities
import (
+ "io"
+ "os"
"time"
"github.com/containers/libpod/libpod/define"
@@ -22,6 +24,11 @@ type BoolReport struct {
Value bool
}
+// StringSliceReport wraps a string slice.
+type StringSliceReport struct {
+ Value []string
+}
+
type PauseUnPauseOptions struct {
All bool
}
@@ -44,6 +51,16 @@ type StopReport struct {
Id string
}
+type TopOptions struct {
+ // CLI flags.
+ ListDescriptors bool
+ Latest bool
+
+ // Options for the API.
+ Descriptors []string
+ NameOrID string
+}
+
type KillOptions struct {
All bool
Latest bool
@@ -81,3 +98,112 @@ type RmReport struct {
Err error
Id string
}
+
+type ContainerInspectReport struct {
+ *define.InspectContainerData
+}
+
+type CommitOptions struct {
+ Author string
+ Changes []string
+ Format string
+ ImageName string
+ IncludeVolumes bool
+ Message string
+ Pause bool
+ Quiet bool
+ Writer io.Writer
+}
+
+type CommitReport struct {
+ Id string
+}
+
+type ContainerExportOptions struct {
+ Output string
+}
+
+type CheckpointOptions struct {
+ All bool
+ Export string
+ IgnoreRootFS bool
+ Keep bool
+ Latest bool
+ LeaveRuninng bool
+ TCPEstablished bool
+}
+
+type CheckpointReport struct {
+ Err error
+ Id string
+}
+
+type RestoreOptions struct {
+ All bool
+ IgnoreRootFS bool
+ IgnoreStaticIP bool
+ IgnoreStaticMAC bool
+ Import string
+ Keep bool
+ Latest bool
+ Name string
+ TCPEstablished bool
+}
+
+type RestoreReport struct {
+ Err error
+ Id string
+}
+
+type ContainerCreateReport struct {
+ Id string
+}
+
+// AttachOptions describes the cli and other values
+// needed to perform an attach
+type AttachOptions struct {
+ DetachKeys string
+ Latest bool
+ NoStdin bool
+ SigProxy bool
+ Stdin *os.File
+ Stdout *os.File
+ Stderr *os.File
+}
+
+// ExecOptions describes the cli values to exec into
+// a container
+type ExecOptions struct {
+ Cmd []string
+ DetachKeys string
+ Envs map[string]string
+ Interactive bool
+ Latest bool
+ PreserveFDs uint
+ Privileged bool
+ Streams define.AttachStreams
+ Tty bool
+ User string
+ WorkDir string
+}
+
+// ContainerStartOptions describes the val from the
+// CLI needed to start a container
+type ContainerStartOptions struct {
+ Attach bool
+ DetachKeys string
+ Interactive bool
+ Latest bool
+ SigProxy bool
+ Stdout *os.File
+ Stderr *os.File
+ Stdin *os.File
+}
+
+// ContainerStartReport describes the response from starting
+// containers from the cli
+type ContainerStartReport struct {
+ Id string
+ Err error
+ ExitCode int
+}
diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go
index 8553f5326..c14348529 100644
--- a/pkg/domain/entities/engine.go
+++ b/pkg/domain/entities/engine.go
@@ -4,7 +4,7 @@ import (
"os/user"
"path/filepath"
- "github.com/containers/libpod/libpod/define"
+ "github.com/containers/common/pkg/config"
"github.com/spf13/pflag"
)
@@ -60,7 +60,7 @@ type EngineOptions struct {
func NewEngineOptions() (EngineOptions, error) {
u, _ := user.Current()
return EngineOptions{
- CGroupManager: define.SystemdCgroupsManager,
+ CGroupManager: config.SystemdCgroupsManager,
CniConfigDir: "",
Config: "",
ConmonPath: filepath.Join("usr", "bin", "conmon"),
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 2efdbd602..264780771 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -2,21 +2,48 @@ package entities
import (
"context"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/specgen"
)
type ContainerEngine interface {
+ ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error
+ ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
+ ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
+ ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
+ ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
+ ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
+ ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
+ ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
- ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
+ ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
+ ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
+ ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
+ HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
+
+ PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
+ PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
+ PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error)
+ PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error)
+ PodRestart(ctx context.Context, namesOrIds []string, options PodRestartOptions) ([]*PodRestartReport, error)
+ PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error)
+ PodStart(ctx context.Context, namesOrIds []string, options PodStartOptions) ([]*PodStartReport, error)
+ PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
+ PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
+ PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
+ PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
+
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
- VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
- VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
+ VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
+ VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
}
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index d44fdaf53..a28bfc548 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -5,8 +5,17 @@ import (
)
type ImageEngine interface {
- Delete(ctx context.Context, nameOrId string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
+ Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
+ Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
+ Inspect(ctx context.Context, names []string, opts InspectOptions) (*ImageInspectReport, error)
List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error)
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
+ Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
+ Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
+ Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error
+ Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error)
+ Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error)
+ Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error
+ Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
}
diff --git a/pkg/domain/entities/healthcheck.go b/pkg/domain/entities/healthcheck.go
new file mode 100644
index 000000000..a880805f9
--- /dev/null
+++ b/pkg/domain/entities/healthcheck.go
@@ -0,0 +1,3 @@
+package entities
+
+type HealthCheckOptions struct{}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index f04317e37..bc8a34c13 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -4,6 +4,8 @@ import (
"net/url"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/pkg/inspect"
docker "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/opencontainers/go-digest"
@@ -11,7 +13,6 @@ import (
)
type Image struct {
- IdOrNamed
ID string `json:"Id"`
RepoTags []string `json:",omitempty"`
RepoDigests []string `json:",omitempty"`
@@ -81,14 +82,18 @@ func (i *ImageSummary) IsDangling() bool {
}
type ImageDeleteOptions struct {
+ All bool
Force bool
}
-// ImageDeleteResponse is the response for removing an image from storage and containers
-// what was untagged vs actually removed
+// ImageDeleteResponse is the response for removing one or more image(s) from storage
+// and containers what was untagged vs actually removed
type ImageDeleteReport struct {
- Untagged []string `json:"untagged"`
- Deleted string `json:"deleted"`
+ Untagged []string `json:",omitempty"`
+ Deleted []string `json:",omitempty"`
+ Errors []error
+ ImageNotFound error
+ ImageInUse error
}
type ImageHistoryOptions struct{}
@@ -106,29 +111,133 @@ type ImageHistoryReport struct {
Layers []ImageHistoryLayer
}
-type ImageInspectOptions struct {
- TypeObject string `json:",omitempty"`
- Format string `json:",omitempty"`
- Size bool `json:",omitempty"`
- Latest bool `json:",omitempty"`
+// ImagePullOptions are the arguments for pulling images.
+type ImagePullOptions struct {
+ // AllTags can be specified to pull all tags of the spiecifed image. Note
+ // that this only works if the specified image does not include a tag.
+ AllTags bool
+ // Authfile is the path to the authentication file. Ignored for remote
+ // calls.
+ Authfile string
+ // CertDir is the path to certificate directories. Ignored for remote
+ // calls.
+ CertDir string
+ // Credentials for authenticating against the registry in the format
+ // USERNAME:PASSWORD.
+ Credentials string
+ // OverrideArch will overwrite the local architecture for image pulls.
+ OverrideArch string
+ // OverrideOS will overwrite the local operating system (OS) for image
+ // pulls.
+ OverrideOS string
+ // Quiet can be specified to suppress pull progress when pulling. Ignored
+ // for remote calls.
+ Quiet bool
+ // SignaturePolicy to use when pulling. Ignored for remote calls.
+ SignaturePolicy string
+ // TLSVerify to enable/disable HTTPS and certificate verification.
+ TLSVerify types.OptionalBool
+}
+
+// ImagePullReport is the response from pulling one or more images.
+type ImagePullReport struct {
+ Images []string
+}
+
+// ImagePushOptions are the arguments for pushing images.
+type ImagePushOptions struct {
+ // Authfile is the path to the authentication file. Ignored for remote
+ // calls.
+ Authfile string
+ // CertDir is the path to certificate directories. Ignored for remote
+ // calls.
+ CertDir string
+ // Compress tarball image layers when pushing to a directory using the 'dir'
+ // transport. Default is same compression type as source. Ignored for remote
+ // calls.
+ Compress bool
+ // Credentials for authenticating against the registry in the format
+ // USERNAME:PASSWORD.
+ Credentials string
+ // DigestFile, after copying the image, write the digest of the resulting
+ // image to the file. Ignored for remote calls.
+ DigestFile string
+ // Format is the Manifest type (oci, v2s1, or v2s2) to use when pushing an
+ // image using the 'dir' transport. Default is manifest type of source.
+ // Ignored for remote calls.
+ Format string
+ // Quiet can be specified to suppress pull progress when pulling. Ignored
+ // for remote calls.
+ Quiet bool
+ // RemoveSignatures, discard any pre-existing signatures in the image.
+ // Ignored for remote calls.
+ RemoveSignatures bool
+ // SignaturePolicy to use when pulling. Ignored for remote calls.
+ SignaturePolicy string
+ // SignBy adds a signature at the destination using the specified key.
+ // Ignored for remote calls.
+ SignBy string
+ // TLSVerify to enable/disable HTTPS and certificate verification.
+ TLSVerify types.OptionalBool
}
type ImageListOptions struct {
All bool `json:"all" schema:"all"`
- Filter []string `json:",omitempty"`
+ Filter []string `json:"Filter,omitempty"`
Filters url.Values `json:"filters" schema:"filters"`
}
-// type ImageListReport struct {
-// Images []ImageSummary
-// }
-
type ImagePruneOptions struct {
- All bool
- Filter ImageFilter
+ All bool `json:"all" schema:"all"`
+ Filter []string `json:"filter" schema:"filter"`
+ Filters url.Values `json:"filters" schema:"filters"`
}
type ImagePruneReport struct {
Report Report
Size int64
}
+
+type ImageTagOptions struct{}
+type ImageUntagOptions struct{}
+
+type ImageData struct {
+ *inspect.ImageData
+}
+
+type ImageInspectReport struct {
+ Images []*ImageData
+ Errors map[string]error
+}
+
+type ImageLoadOptions struct {
+ Name string
+ Tag string
+ Input string
+ Quiet bool
+ SignaturePolicy string
+}
+
+type ImageLoadReport struct {
+ Name string
+}
+
+type ImageImportOptions struct {
+ Changes []string
+ Message string
+ Quiet bool
+ Reference string
+ Source string
+ SourceIsURL bool
+}
+
+type ImageImportReport struct {
+ Id string
+}
+
+type ImageSaveOptions struct {
+ Compress bool
+ Format string
+ Output string
+ Quiet bool
+}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
new file mode 100644
index 000000000..cd2e79961
--- /dev/null
+++ b/pkg/domain/entities/pods.go
@@ -0,0 +1,178 @@
+package entities
+
+import (
+ "time"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/specgen"
+)
+
+type PodKillOptions struct {
+ All bool
+ Latest bool
+ Signal string
+}
+
+type PodKillReport struct {
+ Errs []error
+ Id string
+}
+
+type ListPodsReport struct {
+ Cgroup string
+ Containers []*ListPodContainer
+ Created time.Time
+ Id string
+ InfraId string
+ Name string
+ Namespace string
+ Status string
+}
+
+type ListPodContainer struct {
+ Id string
+ Names string
+ Status string
+}
+
+type PodPauseOptions struct {
+ All bool
+ Latest bool
+}
+
+type PodPauseReport struct {
+ Errs []error
+ Id string
+}
+
+type PodunpauseOptions struct {
+ All bool
+ Latest bool
+}
+
+type PodUnpauseReport struct {
+ Errs []error
+ Id string
+}
+
+type PodStopOptions struct {
+ All bool
+ Ignore bool
+ Latest bool
+ Timeout int
+}
+
+type PodStopReport struct {
+ Errs []error
+ Id string
+}
+
+type PodRestartOptions struct {
+ All bool
+ Latest bool
+}
+
+type PodRestartReport struct {
+ Errs []error
+ Id string
+}
+
+type PodStartOptions struct {
+ All bool
+ Latest bool
+}
+
+type PodStartReport struct {
+ Errs []error
+ Id string
+}
+
+type PodRmOptions struct {
+ All bool
+ Force bool
+ Ignore bool
+ Latest bool
+}
+
+type PodRmReport struct {
+ Err error
+ Id string
+}
+
+type PodCreateOptions struct {
+ CGroupParent string
+ Hostname string
+ Infra bool
+ InfraImage string
+ InfraCommand string
+ Labels map[string]string
+ Name string
+ Net *NetOptions
+ Share []string
+}
+
+type PodCreateReport struct {
+ Id string
+}
+
+func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
+ // Basic Config
+ s.Name = p.Name
+ s.Hostname = p.Hostname
+ s.Labels = p.Labels
+ s.NoInfra = !p.Infra
+ s.InfraCommand = []string{p.InfraCommand}
+ s.InfraImage = p.InfraImage
+ s.SharedNamespaces = p.Share
+
+ // Networking config
+ s.NetNS = p.Net.Network
+ s.StaticIP = p.Net.StaticIP
+ s.StaticMAC = p.Net.StaticMAC
+ s.PortMappings = p.Net.PublishPorts
+ s.CNINetworks = p.Net.CNINetworks
+ if p.Net.DNSHost {
+ s.NoManageResolvConf = true
+ }
+ s.DNSServer = p.Net.DNSServers
+ s.DNSSearch = p.Net.DNSSearch
+ s.DNSOption = p.Net.DNSOptions
+ s.NoManageHosts = p.Net.NoHosts
+ s.HostAdd = p.Net.AddHosts
+
+ // Cgroup
+ s.CgroupParent = p.CGroupParent
+}
+
+type PodTopOptions struct {
+ // CLI flags.
+ ListDescriptors bool
+ Latest bool
+
+ // Options for the API.
+ Descriptors []string
+ NameOrID string
+}
+
+type PodPSOptions struct {
+ CtrNames bool
+ CtrIds bool
+ CtrStatus bool
+ Filters map[string][]string
+ Format string
+ Latest bool
+ Namespace bool
+ Quiet bool
+ Sort string
+}
+
+type PodInspectOptions struct {
+ Latest bool
+
+ // Options for the API.
+ NameOrID string
+}
+
+type PodInspectReport struct {
+ *libpod.PodInspect
+}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index e7757a74b..dd7aaa07f 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -1,5 +1,12 @@
package entities
+import (
+ "net"
+
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+)
+
type Container struct {
IdOrNamed
}
@@ -15,3 +22,30 @@ type Report struct {
type PodDeleteReport struct{ Report }
type PodPruneOptions struct{}
+
+type PodPruneReport struct{ Report }
+type VolumeDeleteOptions struct{}
+type VolumeDeleteReport struct{ Report }
+
+// NetOptions reflect the shared network options between
+// pods and containers
+type NetOptions struct {
+ AddHosts []string
+ CNINetworks []string
+ DNSHost bool
+ DNSOptions []string
+ DNSSearch []string
+ DNSServers []net.IP
+ Network specgen.Namespace
+ NoHosts bool
+ PublishPorts []ocicni.PortMapping
+ StaticIP *net.IP
+ StaticMAC *net.HardwareAddr
+}
+
+// All CLI inspect commands and inspect sub-commands use the same options
+type InspectOptions struct {
+ Format string `json:",omitempty"`
+ Latest bool `json:",omitempty"`
+ Size bool `json:",omitempty"`
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index a3da310c2..929c3f335 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -5,17 +5,54 @@ package abi
import (
"context"
"io/ioutil"
+ "strconv"
"strings"
+ "github.com/containers/buildah"
+ "github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
+ "github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/checkpoint"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi/terminal"
"github.com/containers/libpod/pkg/signal"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/specgen/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+// getContainersByContext gets pods whether all, latest, or a slice of names/ids
+// is specified.
+func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
+ var ctr *libpod.Container
+ ctrs = []*libpod.Container{}
+
+ switch {
+ case all:
+ ctrs, err = runtime.GetAllContainers()
+ case latest:
+ ctr, err = runtime.GetLatestContainer()
+ ctrs = append(ctrs, ctr)
+ default:
+ for _, n := range names {
+ ctr, e := runtime.LookupContainer(n)
+ if e != nil {
+ // Log all errors here, so callers don't need to.
+ logrus.Debugf("Error looking up container %q: %v", n, e)
+ if err == nil {
+ err = e
+ }
+ } else {
+ ctrs = append(ctrs, ctr)
+ }
+ }
+ }
+ return
+}
+
// TODO: Should return *entities.ContainerExistsReport, error
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupContainer(nameOrId)
@@ -29,7 +66,7 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin
var (
responses []entities.WaitReport
)
- ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -55,7 +92,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri
if options.All {
ctrs, err = ic.Libpod.GetAllContainers()
} else {
- ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod)
+ ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod)
}
if err != nil {
return nil, err
@@ -76,7 +113,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
if options.All {
ctrs, err = ic.Libpod.GetAllContainers()
} else {
- ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod)
+ ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod)
}
if err != nil {
return nil, err
@@ -100,7 +137,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
id := strings.Split(string(content), "\n")[0]
names = append(names, id)
}
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
return nil, err
}
@@ -136,7 +173,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
if err != nil {
return nil, err
}
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -152,7 +189,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st
var (
reports []*entities.RestartReport
)
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, err
}
@@ -194,7 +231,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
names = append(names, id)
}
- ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod)
+ ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
// Failed to get containers. If force is specified, get the containers ID
// and evict them
@@ -239,3 +276,344 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
}
return reports, nil
}
+
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
+ var reports []*entities.ContainerInspectReport
+ ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range ctrs {
+ data, err := c.Inspect(options.Size)
+ if err != nil {
+ return nil, err
+ }
+ reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: data})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
+ var (
+ container *libpod.Container
+ err error
+ )
+
+ // Look up the container.
+ if options.Latest {
+ container, err = ic.Libpod.GetLatestContainer()
+ } else {
+ container, err = ic.Libpod.LookupContainer(options.NameOrID)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to lookup requested container")
+ }
+
+ // Run Top.
+ report := &entities.StringSliceReport{}
+ report.Value, err = container.Top(options.Descriptors)
+ return report, err
+}
+
+func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, options entities.CommitOptions) (*entities.CommitReport, error) {
+ var (
+ mimeType string
+ )
+ ctr, err := ic.Libpod.LookupContainer(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ rtc, err := ic.Libpod.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ switch options.Format {
+ case "oci":
+ mimeType = buildah.OCIv1ImageManifest
+ if len(options.Message) > 0 {
+ return nil, errors.Errorf("messages are only compatible with the docker image format (-f docker)")
+ }
+ case "docker":
+ mimeType = manifest.DockerV2Schema2MediaType
+ default:
+ return nil, errors.Errorf("unrecognized image format %q", options.Format)
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ coptions := buildah.CommitOptions{
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
+ ReportWriter: options.Writer,
+ SystemContext: sc,
+ PreferredManifestType: mimeType,
+ }
+ opts := libpod.ContainerCommitOptions{
+ CommitOptions: coptions,
+ Pause: options.Pause,
+ IncludeVolumes: options.IncludeVolumes,
+ Message: options.Message,
+ Changes: options.Changes,
+ Author: options.Author,
+ }
+ newImage, err := ctr.Commit(ctx, options.ImageName, opts)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.CommitReport{Id: newImage.ID()}, nil
+}
+
+func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error {
+ ctr, err := ic.Libpod.LookupContainer(nameOrId)
+ if err != nil {
+ return err
+ }
+ return ctr.Export(options.Output)
+}
+
+func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
+ var (
+ err error
+ cons []*libpod.Container
+ reports []*entities.CheckpointReport
+ )
+ checkOpts := libpod.ContainerCheckpointOptions{
+ Keep: options.Keep,
+ TCPEstablished: options.TCPEstablished,
+ TargetFile: options.Export,
+ IgnoreRootfs: options.IgnoreRootFS,
+ }
+
+ if options.All {
+ running := func(c *libpod.Container) bool {
+ state, _ := c.State()
+ return state == define.ContainerStateRunning
+ }
+ cons, err = ic.Libpod.GetContainers(running)
+ } else {
+ cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ }
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range cons {
+ err = con.Checkpoint(ctx, checkOpts)
+ reports = append(reports, &entities.CheckpointReport{
+ Err: err,
+ Id: con.ID(),
+ })
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) {
+ var (
+ cons []*libpod.Container
+ err error
+ filterFuncs []libpod.ContainerFilter
+ reports []*entities.RestoreReport
+ )
+
+ restoreOptions := libpod.ContainerCheckpointOptions{
+ Keep: options.Keep,
+ TCPEstablished: options.TCPEstablished,
+ TargetFile: options.Import,
+ Name: options.Name,
+ IgnoreRootfs: options.IgnoreRootFS,
+ IgnoreStaticIP: options.IgnoreStaticIP,
+ IgnoreStaticMAC: options.IgnoreStaticMAC,
+ }
+
+ filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
+ state, _ := c.State()
+ return state == define.ContainerStateExited
+ })
+
+ switch {
+ case options.Import != "":
+ cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options.Import, options.Name)
+ case options.All:
+ cons, err = ic.Libpod.GetContainers(filterFuncs...)
+ default:
+ cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ }
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range cons {
+ err := con.Restore(ctx, restoreOptions)
+ reports = append(reports, &entities.RestoreReport{
+ Err: err,
+ Id: con.ID(),
+ })
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
+ if err := generate.CompleteSpec(ctx, ic.Libpod, s); err != nil {
+ return nil, err
+ }
+ ctr, err := generate.MakeContainer(ic.Libpod, s)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ContainerCreateReport{Id: ctr.ID()}, nil
+}
+
+func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
+ ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
+ if err != nil {
+ return err
+ }
+ ctr := ctrs[0]
+ conState, err := ctr.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to determine state of %s", ctr.ID())
+ }
+ if conState != define.ContainerStateRunning {
+ return errors.Errorf("you can only attach to running containers")
+ }
+
+ // If the container is in a pod, also set to recursively start dependencies
+ if err := terminal.StartAttachCtr(ctx, ctr, options.Stdin, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach {
+ return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
+ }
+ return nil
+}
+
+func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
+ ec := define.ExecErrorCodeGeneric
+ if options.PreserveFDs > 0 {
+ entries, err := ioutil.ReadDir("/proc/self/fd")
+ if err != nil {
+ return ec, errors.Wrapf(err, "unable to read /proc/self/fd")
+ }
+
+ m := make(map[int]bool)
+ for _, e := range entries {
+ i, err := strconv.Atoi(e.Name())
+ if err != nil {
+ return ec, errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name())
+ }
+ m[i] = true
+ }
+
+ for i := 3; i < 3+int(options.PreserveFDs); i++ {
+ if _, found := m[i]; !found {
+ return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available")
+ }
+ }
+ }
+ ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
+ if err != nil {
+ return ec, err
+ }
+ ctr := ctrs[0]
+ ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys)
+ return define.TranslateExecErrorToExitCode(ec, err), err
+}
+
+func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
+ var reports []*entities.ContainerStartReport
+ var exitCode = define.ExecErrorCodeGeneric
+ ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ // There can only be one container if attach was used
+ for _, ctr := range ctrs {
+ ctrState, err := ctr.State()
+ if err != nil {
+ return nil, err
+ }
+ ctrRunning := ctrState == define.ContainerStateRunning
+
+ if options.Attach {
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "")
+ if errors.Cause(err) == define.ErrDetach {
+ // User manually detached
+ // Exit cleanly immediately
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: nil,
+ ExitCode: 0,
+ })
+ return reports, nil
+ }
+
+ if errors.Cause(err) == define.ErrWillDeadlock {
+ logrus.Debugf("Deadlock error: %v", err)
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: err,
+ ExitCode: define.ExitCode(err),
+ })
+ return reports, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
+ }
+
+ if ctrRunning {
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: nil,
+ ExitCode: 0,
+ })
+ return reports, err
+ }
+
+ if err != nil {
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: err,
+ ExitCode: exitCode,
+ })
+ return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID())
+ }
+
+ if ecode, err := ctr.Wait(); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ // Check events
+ event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited)
+ if err != nil {
+ logrus.Errorf("Cannot get exit code: %v", err)
+ exitCode = define.ExecErrorCodeNotFound
+ } else {
+ exitCode = event.ContainerExitCode
+ }
+ }
+ } else {
+ exitCode = int(ecode)
+ }
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: err,
+ ExitCode: exitCode,
+ })
+ return reports, nil
+ } // end attach
+
+ // Start the container if it's not running already.
+ if !ctrRunning {
+ // Handle non-attach start
+ // If the container is in a pod, also set to recursively start dependencies
+ report := &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ ExitCode: 125,
+ }
+ if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
+ //if lastError != nil {
+ // fmt.Fprintln(os.Stderr, lastError)
+ //}
+ report.Err = err
+ if errors.Cause(err) == define.ErrWillDeadlock {
+ report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks")
+ reports = append(reports, report)
+ continue
+ }
+ report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID())
+ reports = append(reports, report)
+ continue
+ }
+ report.ExitCode = 0
+ reports = append(reports, report)
+ }
+ }
+ return reports, nil
+}
diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go
new file mode 100644
index 000000000..699483243
--- /dev/null
+++ b/pkg/domain/infra/abi/healthcheck.go
@@ -0,0 +1,26 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) {
+ status, err := ic.Libpod.HealthCheck(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ hcStatus := "unhealthy"
+ if status == libpod.HealthCheckSuccess {
+ hcStatus = "healthy"
+ }
+ report := define.HealthCheckResults{
+ Status: hcStatus,
+ }
+ return &report, nil
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 6e9d7f566..9d706a112 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -4,38 +4,116 @@ package abi
import (
"context"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "github.com/containers/image/v5/docker"
+ dockerarchive "github.com/containers/image/v5/docker/archive"
+ "github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod/image"
libpodImage "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/domain/utils"
+ domainUtils "github.com/containers/libpod/pkg/domain/utils"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/containers/storage"
+ imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
- if err != nil {
- return nil, err
+func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) {
+ if _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId); err != nil {
+ return &entities.BoolReport{}, nil
}
+ return &entities.BoolReport{Value: true}, nil
+}
- results, err := ir.Libpod.RemoveImage(ctx, image, opts.Force)
- if err != nil {
- return nil, err
+func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+ report := entities.ImageDeleteReport{}
+
+ if opts.All {
+ var previousTargets []*libpodImage.Image
+ repeatRun:
+ targets, err := ir.Libpod.ImageRuntime().GetRWImages()
+ if err != nil {
+ return &report, errors.Wrapf(err, "unable to query local images")
+ }
+
+ if len(targets) > 0 && len(targets) == len(previousTargets) {
+ return &report, errors.New("unable to delete all images; re-run the rmi command again.")
+ }
+ previousTargets = targets
+
+ for _, img := range targets {
+ isParent, err := img.IsParent(ctx)
+ if err != nil {
+ return &report, err
+ }
+ if isParent {
+ continue
+ }
+ err = ir.deleteImage(ctx, img, opts, report)
+ report.Errors = append(report.Errors, err)
+ }
+ if len(previousTargets) != 1 {
+ goto repeatRun
+ }
+ return &report, nil
}
- report := entities.ImageDeleteReport{}
- if err := utils.DeepCopy(&report, results); err != nil {
- return nil, err
+ for _, id := range nameOrId {
+ image, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
+ if err != nil {
+ return nil, err
+ }
+
+ err = ir.deleteImage(ctx, image, opts, report)
+ if err != nil {
+ return &report, err
+ }
}
return &report, nil
}
+func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image, opts entities.ImageDeleteOptions, report entities.ImageDeleteReport) error {
+ results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
+ switch errors.Cause(err) {
+ case nil:
+ break
+ case storage.ErrImageUsedByContainer:
+ report.ImageInUse = errors.New(
+ fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
+ return nil
+ case libpodImage.ErrNoSuchImage:
+ report.ImageNotFound = err
+ return nil
+ default:
+ return err
+ }
+
+ report.Deleted = append(report.Deleted, results.Deleted)
+ report.Untagged = append(report.Untagged, results.Untagged...)
+ return nil
+}
+
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, []string{})
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
if err != nil {
return nil, err
}
- report := entities.ImagePruneReport{}
- copy(report.Report.Id, results)
+ report := entities.ImagePruneReport{
+ Report: entities.Report{
+ Id: results,
+ Err: nil,
+ },
+ Size: 0,
+ }
return &report, nil
}
@@ -70,6 +148,178 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer
return l
}
+func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
+ var writer io.Writer
+ if !options.Quiet {
+ writer = os.Stderr
+ }
+
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
+ imageRef, err := alltransports.ParseImageName(rawImage)
+ if err != nil {
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, rawImage))
+ if err != nil {
+ return nil, errors.Errorf("invalid image reference %q", rawImage)
+ }
+ }
+
+ // Special-case for docker-archive which allows multiple tags.
+ if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
+ newImage, err := ir.Libpod.ImageRuntime().LoadFromArchiveReference(ctx, imageRef, options.SignaturePolicy, writer)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ }
+ return &entities.ImagePullReport{Images: []string{newImage[0].ID()}}, nil
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if options.Credentials != "" {
+ creds, err := util.ParseRegistryCreds(options.Credentials)
+ if err != nil {
+ return nil, err
+ }
+ registryCreds = creds
+ }
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: options.CertDir,
+ OSChoice: options.OverrideOS,
+ ArchitectureChoice: options.OverrideArch,
+ DockerInsecureSkipTLSVerify: options.TLSVerify,
+ }
+
+ if !options.AllTags {
+ newImage, err := ir.Libpod.ImageRuntime().New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ }
+ return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil
+ }
+
+ // --all-tags requires the docker transport
+ if imageRef.Transport().Name() != docker.Transport.Name() {
+ return nil, errors.New("--all-tags requires docker transport")
+ }
+
+ // Trim the docker-transport prefix.
+ rawImage = strings.TrimPrefix(rawImage, docker.Transport.Name())
+
+ // all-tags doesn't work with a tagged reference, so let's check early
+ namedRef, err := reference.Parse(rawImage)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing %q", rawImage)
+ }
+ if _, isTagged := namedRef.(reference.Tagged); isTagged {
+ return nil, errors.New("--all-tags requires a reference without a tag")
+
+ }
+
+ systemContext := image.GetSystemContext("", options.Authfile, false)
+ tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting repository tags")
+ }
+
+ var foundIDs []string
+ for _, tag := range tags {
+ name := rawImage + ":" + tag
+ newImage, err := ir.Libpod.ImageRuntime().New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
+ if err != nil {
+ logrus.Errorf("error pulling image %q", name)
+ continue
+ }
+ foundIDs = append(foundIDs, newImage.ID())
+ }
+
+ if len(tags) != len(foundIDs) {
+ return nil, errors.Errorf("error pulling image %q", rawImage)
+ }
+ return &entities.ImagePullReport{Images: foundIDs}, nil
+}
+
+func (ir *ImageEngine) Inspect(ctx context.Context, names []string, opts entities.InspectOptions) (*entities.ImageInspectReport, error) {
+ report := entities.ImageInspectReport{
+ Errors: make(map[string]error),
+ }
+
+ for _, id := range names {
+ img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
+ if err != nil {
+ report.Errors[id] = err
+ continue
+ }
+
+ results, err := img.Inspect(ctx)
+ if err != nil {
+ report.Errors[id] = err
+ continue
+ }
+
+ cookedResults := entities.ImageData{}
+ _ = domainUtils.DeepCopy(&cookedResults, results)
+ report.Images = append(report.Images, &cookedResults)
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
+ var writer io.Writer
+ if !options.Quiet {
+ writer = os.Stderr
+ }
+
+ var manifestType string
+ switch options.Format {
+ case "":
+ // Default
+ case "oci":
+ manifestType = imgspecv1.MediaTypeImageManifest
+ case "v2s1":
+ manifestType = manifest.DockerV2Schema1SignedMediaType
+ case "v2s2", "docker":
+ manifestType = manifest.DockerV2Schema2MediaType
+ default:
+ return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", options.Format)
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if options.Credentials != "" {
+ creds, err := util.ParseRegistryCreds(options.Credentials)
+ if err != nil {
+ return err
+ }
+ registryCreds = creds
+ }
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: options.CertDir,
+ DockerInsecureSkipTLSVerify: options.TLSVerify,
+ }
+
+ signOptions := image.SigningOptions{
+ RemoveSignatures: options.RemoveSignatures,
+ SignBy: options.SignBy,
+ }
+
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(source)
+ if err != nil {
+ return err
+ }
+
+ return newImage.PushImageToHeuristicDestination(
+ ctx,
+ destination,
+ manifestType,
+ options.Authfile,
+ options.DigestFile,
+ options.SignaturePolicy,
+ writer,
+ options.Compress,
+ signOptions,
+ &dockerRegistryOptions,
+ nil)
+}
+
// func (r *imageRuntime) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
// image, err := r.libpod.ImageEngine().NewFromLocal(nameOrId)
// if err != nil {
@@ -82,7 +332,7 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer
// }
//
// report := entities.ImageDeleteReport{}
-// if err := utils.DeepCopy(&report, results); err != nil {
+// if err := domainUtils.DeepCopy(&report, results); err != nil {
// return nil, err
// }
// return &report, nil
@@ -100,3 +350,66 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer
// copy(report.Report.Id, id)
// return &report, nil
// }
+
+func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return err
+ }
+ for _, tag := range tags {
+ if err := newImage.TagImage(tag); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return err
+ }
+ for _, tag := range tags {
+ if err := newImage.UntagImage(tag); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ var (
+ writer io.Writer
+ )
+ if !opts.Quiet {
+ writer = os.Stderr
+ }
+ name, err := ir.Libpod.LoadImage(ctx, opts.Name, opts.Input, writer, opts.SignaturePolicy)
+ if err != nil {
+ return nil, err
+ }
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return nil, errors.Wrap(err, "image loaded but no additional tags were created")
+ }
+ if err := newImage.TagImage(opts.Name); err != nil {
+ return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName)
+ }
+ return &entities.ImageLoadReport{Name: name}, nil
+}
+
+func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
+ id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.Changes, opts.Message, opts.Quiet)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ImageImportReport{Id: id}, nil
+}
+
+func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return err
+ }
+ return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress)
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index de22de68e..073cd8d5c 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -4,12 +4,48 @@ package abi
import (
"context"
- "github.com/pkg/errors"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/podfilters"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/signal"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
+// getPodsByContext returns a slice of pods. Note that all, latest and pods are
+// mutually exclusive arguments.
+func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) {
+ var outpods []*libpod.Pod
+ if all {
+ return runtime.GetAllPods()
+ }
+ if latest {
+ p, err := runtime.GetLatestPod()
+ if err != nil {
+ return nil, err
+ }
+ outpods = append(outpods, p)
+ return outpods, nil
+ }
+ var err error
+ for _, p := range pods {
+ pod, e := runtime.LookupPod(p)
+ if e != nil {
+ // Log all errors here, so callers don't need to.
+ logrus.Debugf("Error looking up pod %q: %v", p, e)
+ if err == nil {
+ err = e
+ }
+ } else {
+ outpods = append(outpods, pod)
+ }
+ }
+ return outpods, err
+}
+
func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupPod(nameOrId)
if err != nil && errors.Cause(err) != define.ErrNoSuchPod {
@@ -17,3 +53,302 @@ func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*ent
}
return &entities.BoolReport{Value: err == nil}, nil
}
+
+func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) {
+ var (
+ reports []*entities.PodKillReport
+ )
+ sig, err := signal.ParseSignalNameOrNumber(options.Signal)
+ if err != nil {
+ return nil, err
+ }
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, p := range pods {
+ report := entities.PodKillReport{Id: p.ID()}
+ conErrs, err := p.Kill(uint(sig))
+ if err != nil {
+ report.Errs = []error{err}
+ reports = append(reports, &report)
+ continue
+ }
+ if len(conErrs) > 0 {
+ for _, err := range conErrs {
+ report.Errs = append(report.Errs, err)
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) {
+ var (
+ reports []*entities.PodPauseReport
+ )
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pods {
+ report := entities.PodPauseReport{Id: p.ID()}
+ errs, err := p.Pause()
+ if err != nil {
+ report.Errs = []error{err}
+ continue
+ }
+ if len(errs) > 0 {
+ for _, v := range errs {
+ report.Errs = append(report.Errs, v)
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) {
+ var (
+ reports []*entities.PodUnpauseReport
+ )
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pods {
+ report := entities.PodUnpauseReport{Id: p.ID()}
+ errs, err := p.Unpause()
+ if err != nil {
+ report.Errs = []error{err}
+ continue
+ }
+ if len(errs) > 0 {
+ for _, v := range errs {
+ report.Errs = append(report.Errs, v)
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) {
+ var (
+ reports []*entities.PodStopReport
+ )
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pods {
+ report := entities.PodStopReport{Id: p.ID()}
+ errs, err := p.StopWithTimeout(ctx, false, options.Timeout)
+ if err != nil {
+ report.Errs = []error{err}
+ continue
+ }
+ if len(errs) > 0 {
+ for _, v := range errs {
+ report.Errs = append(report.Errs, v)
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) {
+ var (
+ reports []*entities.PodRestartReport
+ )
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pods {
+ report := entities.PodRestartReport{Id: p.ID()}
+ errs, err := p.Restart(ctx)
+ if err != nil {
+ report.Errs = []error{err}
+ continue
+ }
+ if len(errs) > 0 {
+ for _, v := range errs {
+ report.Errs = append(report.Errs, v)
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) {
+ var (
+ reports []*entities.PodStartReport
+ )
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pods {
+ report := entities.PodStartReport{Id: p.ID()}
+ errs, err := p.Start(ctx)
+ if err != nil {
+ report.Errs = []error{err}
+ continue
+ }
+ if len(errs) > 0 {
+ for _, v := range errs {
+ report.Errs = append(report.Errs, v)
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) {
+ var (
+ reports []*entities.PodRmReport
+ )
+ pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pods {
+ report := entities.PodRmReport{Id: p.ID()}
+ err := ic.Libpod.RemovePod(ctx, p, true, options.Force)
+ if err != nil {
+ report.Err = err
+ continue
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
+ podSpec := specgen.NewPodSpecGenerator()
+ opts.ToPodSpecGen(podSpec)
+ pod, err := podSpec.MakePod(ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.PodCreateReport{Id: pod.ID()}, nil
+}
+
+func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOptions) (*entities.StringSliceReport, error) {
+ var (
+ pod *libpod.Pod
+ err error
+ )
+
+ // Look up the pod.
+ if options.Latest {
+ pod, err = ic.Libpod.GetLatestPod()
+ } else {
+ pod, err = ic.Libpod.LookupPod(options.NameOrID)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to lookup requested container")
+ }
+
+ // Run Top.
+ report := &entities.StringSliceReport{}
+ report.Value, err = pod.GetPodPidInformation(options.Descriptors)
+ return report, err
+}
+
+func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
+ var (
+ filters []libpod.PodFilter
+ reports []*entities.ListPodsReport
+ )
+ for k, v := range options.Filters {
+ for _, filter := range v {
+ f, err := podfilters.GeneratePodFilterFunc(k, filter)
+ if err != nil {
+ return nil, err
+ }
+ filters = append(filters, f)
+
+ }
+ }
+ pds, err := ic.Libpod.Pods(filters...)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pds {
+ var lpcs []*entities.ListPodContainer
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return nil, err
+ }
+ cons, err := p.AllContainers()
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range cons {
+ state, err := c.State()
+ if err != nil {
+ return nil, err
+ }
+ lpcs = append(lpcs, &entities.ListPodContainer{
+ Id: c.ID(),
+ Names: c.Name(),
+ Status: state.String(),
+ })
+ }
+ infraId, err := p.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
+ reports = append(reports, &entities.ListPodsReport{
+ Cgroup: p.CgroupParent(),
+ Containers: lpcs,
+ Created: p.CreatedTime(),
+ Id: p.ID(),
+ InfraId: infraId,
+ Name: p.Name(),
+ Namespace: p.Namespace(),
+ Status: status,
+ })
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) {
+ var (
+ pod *libpod.Pod
+ err error
+ )
+ // Look up the pod.
+ if options.Latest {
+ pod, err = ic.Libpod.GetLatestPod()
+ } else {
+ pod, err = ic.Libpod.LookupPod(options.NameOrID)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to lookup requested container")
+ }
+ inspect, err := pod.Inspect()
+ if err != nil {
+ return nil, err
+ }
+ return &entities.PodInspectReport{PodInspect: inspect}, nil
+}
diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
new file mode 100644
index 000000000..d7f5853d8
--- /dev/null
+++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
@@ -0,0 +1,47 @@
+// +build ABISupport
+
+package terminal
+
+import (
+ "os"
+ "syscall"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/signal"
+ "github.com/sirupsen/logrus"
+)
+
+// ProxySignals ...
+func ProxySignals(ctr *libpod.Container) {
+ sigBuffer := make(chan os.Signal, 128)
+ signal.CatchAll(sigBuffer)
+
+ logrus.Debugf("Enabling signal proxying")
+
+ go func() {
+ for s := range sigBuffer {
+ // Ignore SIGCHLD and SIGPIPE - these are mostly likely
+ // intended for the podman command itself.
+ // SIGURG was added because of golang 1.14 and its preemptive changes
+ // causing more signals to "show up".
+ // https://github.com/containers/libpod/issues/5483
+ if s == syscall.SIGCHLD || s == syscall.SIGPIPE || s == syscall.SIGURG {
+ continue
+ }
+
+ if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil {
+ // If the container dies, and we find out here,
+ // we need to forward that one signal to
+ // ourselves so that it is not lost, and then
+ // we terminate the proxy and let the defaults
+ // play out.
+ logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err)
+ signal.StopCatch(sigBuffer)
+ if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil {
+ logrus.Errorf("failed to kill pid %d", syscall.Getpid())
+ }
+ return
+ }
+ }
+ }()
+}
diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go
new file mode 100644
index 000000000..f187bdd6b
--- /dev/null
+++ b/pkg/domain/infra/abi/terminal/terminal.go
@@ -0,0 +1,103 @@
+// +build ABISupport
+
+package terminal
+
+import (
+ "context"
+ "os"
+ "os/signal"
+
+ lsignal "github.com/containers/libpod/pkg/signal"
+ "github.com/docker/docker/pkg/term"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// RawTtyFormatter ...
+type RawTtyFormatter struct {
+}
+
+// getResize returns a TerminalSize command matching stdin's current
+// size on success, and nil on errors.
+func getResize() *remotecommand.TerminalSize {
+ winsize, err := term.GetWinsize(os.Stdin.Fd())
+ if err != nil {
+ logrus.Warnf("Could not get terminal size %v", err)
+ return nil
+ }
+ return &remotecommand.TerminalSize{
+ Width: winsize.Width,
+ Height: winsize.Height,
+ }
+}
+
+// Helper for prepareAttach - set up a goroutine to generate terminal resize events
+func resizeTty(ctx context.Context, resize chan remotecommand.TerminalSize) {
+ sigchan := make(chan os.Signal, 1)
+ signal.Notify(sigchan, lsignal.SIGWINCH)
+ go func() {
+ defer close(resize)
+ // Update the terminal size immediately without waiting
+ // for a SIGWINCH to get the correct initial size.
+ resizeEvent := getResize()
+ for {
+ if resizeEvent == nil {
+ select {
+ case <-ctx.Done():
+ return
+ case <-sigchan:
+ resizeEvent = getResize()
+ }
+ } else {
+ select {
+ case <-ctx.Done():
+ return
+ case <-sigchan:
+ resizeEvent = getResize()
+ case resize <- *resizeEvent:
+ resizeEvent = nil
+ }
+ }
+ }
+ }()
+}
+
+func restoreTerminal(state *term.State) error {
+ logrus.SetFormatter(&logrus.TextFormatter{})
+ return term.RestoreTerminal(os.Stdin.Fd(), state)
+}
+
+// Format ...
+func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ textFormatter := logrus.TextFormatter{}
+ bytes, err := textFormatter.Format(entry)
+
+ if err == nil {
+ bytes = append(bytes, '\r')
+ }
+
+ return bytes, err
+}
+
+func handleTerminalAttach(ctx context.Context, resize chan remotecommand.TerminalSize) (context.CancelFunc, *term.State, error) {
+ logrus.Debugf("Handling terminal attach")
+
+ subCtx, cancel := context.WithCancel(ctx)
+
+ resizeTty(subCtx, resize)
+
+ oldTermState, err := term.SaveState(os.Stdin.Fd())
+ if err != nil {
+ // allow caller to not have to do any cleaning up if we error here
+ cancel()
+ return nil, nil, errors.Wrapf(err, "unable to save terminal state")
+ }
+
+ logrus.SetFormatter(&RawTtyFormatter{})
+ if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil {
+ return cancel, nil, err
+ }
+
+ return cancel, oldTermState, nil
+}
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
new file mode 100644
index 000000000..664205df1
--- /dev/null
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -0,0 +1,123 @@
+// +build ABISupport
+
+package terminal
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh/terminal"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// ExecAttachCtr execs and attaches to a container
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+ resize := make(chan remotecommand.TerminalSize)
+ haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
+
+ // Check if we are attached to a terminal. If we are, generate resize
+ // events, and set the terminal to raw mode
+ if haveTerminal && tty {
+ cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
+ if err != nil {
+ return -1, err
+ }
+ defer cancel()
+ defer func() {
+ if err := restoreTerminal(oldTermState); err != nil {
+ logrus.Errorf("unable to restore terminal: %q", err)
+ }
+ }()
+ }
+
+ execConfig := new(libpod.ExecConfig)
+ execConfig.Command = cmd
+ execConfig.Terminal = tty
+ execConfig.Privileged = privileged
+ execConfig.Environment = env
+ execConfig.User = user
+ execConfig.WorkDir = workDir
+ execConfig.DetachKeys = &detachKeys
+ execConfig.PreserveFDs = preserveFDs
+
+ return ctr.Exec(execConfig, streams, resize)
+}
+
+// StartAttachCtr starts and (if required) attaches to a container
+// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
+// error. we may need to just lint disable this one.
+func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
+ resize := make(chan remotecommand.TerminalSize)
+
+ haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
+
+ // Check if we are attached to a terminal. If we are, generate resize
+ // events, and set the terminal to raw mode
+ if haveTerminal && ctr.Spec().Process.Terminal {
+ cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err := restoreTerminal(oldTermState); err != nil {
+ logrus.Errorf("unable to restore terminal: %q", err)
+ }
+ }()
+ defer cancel()
+ }
+
+ streams := new(define.AttachStreams)
+ streams.OutputStream = stdout
+ streams.ErrorStream = stderr
+ streams.InputStream = bufio.NewReader(stdin)
+ streams.AttachOutput = true
+ streams.AttachError = true
+ streams.AttachInput = true
+
+ if stdout == nil {
+ logrus.Debugf("Not attaching to stdout")
+ streams.AttachOutput = false
+ }
+ if stderr == nil {
+ logrus.Debugf("Not attaching to stderr")
+ streams.AttachError = false
+ }
+ if stdin == nil {
+ logrus.Debugf("Not attaching to stdin")
+ streams.AttachInput = false
+ }
+
+ if !startContainer {
+ if sigProxy {
+ ProxySignals(ctr)
+ }
+
+ return ctr.Attach(streams, detachKeys, resize)
+ }
+
+ attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive)
+ if err != nil {
+ return err
+ }
+
+ if sigProxy {
+ ProxySignals(ctr)
+ }
+
+ if stdout == nil && stderr == nil {
+ fmt.Printf("%s\n", ctr.ID())
+ }
+
+ err = <-attachChan
+ if err != nil {
+ return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
+ }
+
+ return nil
+}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 0cc20474e..bdae4359d 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -107,13 +107,26 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
UID: v.UID(),
GID: v.GID(),
}
- reports = append(reports, &entities.VolumeInspectReport{&config})
+ reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config})
}
return reports, nil
}
func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
- return ic.Libpod.PruneVolumes(ctx)
+ var (
+ reports []*entities.VolumePruneReport
+ )
+ pruned, err := ic.Libpod.PruneVolumes(ctx)
+ if err != nil {
+ return nil, err
+ }
+ for k, v := range pruned {
+ reports = append(reports, &entities.VolumePruneReport{
+ Err: v,
+ Id: k,
+ })
+ }
+ return reports, nil
}
func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
@@ -140,7 +153,7 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
UID: v.UID(),
GID: v.GID(),
}
- reports = append(reports, &entities.VolumeListReport{config})
+ reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config})
}
return reports, nil
}
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index 730ded2e0..d59759707 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -220,9 +220,6 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
if !opts.withFDS {
options = append(options, libpod.WithEnableSDNotify())
}
- if fs.Changed("config") {
- return libpod.NewRuntimeFromConfig(ctx, opts.flags.Config, options...)
- }
return libpod.NewRuntime(ctx, options...)
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index a8ecff41b..4101068ba 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -2,9 +2,16 @@ package tunnel
import (
"context"
+ "io"
+ "os"
+ "github.com/containers/image/v5/docker/reference"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
)
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
@@ -138,3 +145,175 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
}
return reports, nil
}
+
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
+ var (
+ reports []*entities.ContainerInspectReport
+ )
+ ctrs, err := getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range ctrs {
+ data, err := containers.Inspect(ic.ClientCxt, con.ID, &options.Size)
+ if err != nil {
+ return nil, err
+ }
+ reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: data})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
+ switch {
+ case options.Latest:
+ return nil, errors.New("latest is not supported")
+ case options.NameOrID == "":
+ return nil, errors.New("NameOrID must be specified")
+ }
+
+ topOutput, err := containers.Top(ic.ClientCxt, options.NameOrID, options.Descriptors)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.StringSliceReport{Value: topOutput}, nil
+}
+
+func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, options entities.CommitOptions) (*entities.CommitReport, error) {
+ var (
+ repo string
+ tag string = "latest"
+ )
+ if len(options.ImageName) > 0 {
+ ref, err := reference.Parse(options.ImageName)
+ if err != nil {
+ return nil, err
+ }
+ if t, ok := ref.(reference.Tagged); ok {
+ tag = t.Tag()
+ }
+ if r, ok := ref.(reference.Named); ok {
+ repo = r.Name()
+ }
+ if len(repo) < 1 {
+ return nil, errors.Errorf("invalid image name %q", options.ImageName)
+ }
+ }
+ commitOpts := containers.CommitOptions{
+ Author: &options.Author,
+ Changes: options.Changes,
+ Comment: &options.Message,
+ Format: &options.Format,
+ Pause: &options.Pause,
+ Repo: &repo,
+ Tag: &tag,
+ }
+ response, err := containers.Commit(ic.ClientCxt, nameOrId, commitOpts)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.CommitReport{Id: response.ID}, nil
+}
+
+func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error {
+ var (
+ err error
+ w io.Writer
+ )
+ if len(options.Output) > 0 {
+ w, err = os.Create(options.Output)
+ if err != nil {
+ return err
+ }
+ }
+ return containers.Export(ic.ClientCxt, nameOrId, w)
+}
+
+func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
+ var (
+ reports []*entities.CheckpointReport
+ err error
+ ctrs []libpod.ListContainer
+ )
+
+ if options.All {
+ allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
+ if err != nil {
+ return nil, err
+ }
+ // narrow the list to running only
+ for _, c := range allCtrs {
+ if c.State == define.ContainerStateRunning.String() {
+ ctrs = append(ctrs, c)
+ }
+ }
+
+ } else {
+ ctrs, err = getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ }
+ for _, c := range ctrs {
+ report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRuninng, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export)
+ if err != nil {
+ reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err})
+ }
+ reports = append(reports, report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) {
+ var (
+ reports []*entities.RestoreReport
+ err error
+ ctrs []libpod.ListContainer
+ )
+ if options.All {
+ allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
+ if err != nil {
+ return nil, err
+ }
+ // narrow the list to exited only
+ for _, c := range allCtrs {
+ if c.State == define.ContainerStateExited.String() {
+ ctrs = append(ctrs, c)
+ }
+ }
+
+ } else {
+ ctrs, err = getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ }
+ for _, c := range ctrs {
+ report, err := containers.Restore(ic.ClientCxt, c.ID, &options.Keep, &options.TCPEstablished, &options.IgnoreRootFS, &options.IgnoreStaticIP, &options.IgnoreStaticMAC, &options.Name, &options.Import)
+ if err != nil {
+ reports = append(reports, &entities.RestoreReport{Id: c.ID, Err: err})
+ }
+ reports = append(reports, report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
+ response, err := containers.CreateWithSpec(ic.ClientCxt, s)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ContainerCreateReport{Id: response.ID}, nil
+}
+
+func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
+ return errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
+ return 125, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
+ return nil, errors.New("not implemented")
+}
diff --git a/pkg/domain/infra/tunnel/healthcheck.go b/pkg/domain/infra/tunnel/healthcheck.go
new file mode 100644
index 000000000..e589489b3
--- /dev/null
+++ b/pkg/domain/infra/tunnel/healthcheck.go
@@ -0,0 +1,13 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/bindings/containers"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) {
+ return containers.RunHealthCheck(ic.ClientCxt, nameOrId)
+}
diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go
index 11fca5278..f9183c955 100644
--- a/pkg/domain/infra/tunnel/helpers.go
+++ b/pkg/domain/infra/tunnel/helpers.go
@@ -4,9 +4,12 @@ import (
"context"
"strings"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
+ "github.com/containers/libpod/pkg/bindings/pods"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
)
@@ -40,3 +43,34 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam
}
return cons, nil
}
+
+func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]*entities.ListPodsReport, error) {
+ var (
+ sPods []*entities.ListPodsReport
+ )
+ if all && len(namesOrIds) > 0 {
+ return nil, errors.New("cannot lookup specific pods and all")
+ }
+
+ fPods, err := pods.List(contextWithConnection, nil)
+ if err != nil {
+ return nil, err
+ }
+ if all {
+ return fPods, nil
+ }
+ for _, nameOrId := range namesOrIds {
+ var found bool
+ for _, f := range fPods {
+ if f.Name == nameOrId || strings.HasPrefix(f.Id, nameOrId) {
+ sPods = append(sPods, f)
+ found = true
+ break
+ }
+ }
+ if !found {
+ return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrId)
+ }
+ }
+ return sPods, nil
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 60df40498..516914a68 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,34 +2,41 @@ package tunnel
import (
"context"
- "net/url"
+ "io/ioutil"
+ "os"
+ "github.com/containers/image/v5/docker/reference"
images "github.com/containers/libpod/pkg/bindings/images"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/utils"
+ utils2 "github.com/containers/libpod/utils"
+ "github.com/pkg/errors"
)
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- results, err := images.Remove(ir.ClientCxt, nameOrId, &opts.Force)
- if err != nil {
- return nil, err
- }
+func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) {
+ found, err := images.Exists(ir.ClientCxt, nameOrId)
+ return &entities.BoolReport{Value: found}, err
+}
- report := entities.ImageDeleteReport{
- Untagged: nil,
- Deleted: "",
- }
+func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+ report := entities.ImageDeleteReport{}
- for _, e := range results {
- if a, ok := e["Deleted"]; ok {
- report.Deleted = a
+ for _, id := range nameOrId {
+ results, err := images.Remove(ir.ClientCxt, id, &opts.Force)
+ if err != nil {
+ return nil, err
}
+ for _, e := range results {
+ if a, ok := e["Deleted"]; ok {
+ report.Deleted = append(report.Deleted, a)
+ }
- if a, ok := e["Untagged"]; ok {
- report.Untagged = append(report.Untagged, a)
+ if a, ok := e["Untagged"]; ok {
+ report.Untagged = append(report.Untagged, a)
+ }
}
}
- return &report, err
+ return &report, nil
}
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
@@ -69,12 +76,168 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entiti
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- results, err := images.Prune(ir.ClientCxt, url.Values{})
+ results, err := images.Prune(ir.ClientCxt, &opts.All, opts.Filters)
+ if err != nil {
+ return nil, err
+ }
+
+ report := entities.ImagePruneReport{
+ Report: entities.Report{
+ Id: results,
+ Err: nil,
+ },
+ Size: 0,
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
+ pulledImages, err := images.Pull(ir.ClientCxt, rawImage, options)
if err != nil {
return nil, err
}
+ return &entities.ImagePullReport{Images: pulledImages}, nil
+}
+
+func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error {
+ for _, newTag := range tags {
+ var (
+ tag, repo string
+ )
+ ref, err := reference.Parse(newTag)
+ if err != nil {
+ return err
+ }
+ if t, ok := ref.(reference.Tagged); ok {
+ tag = t.Tag()
+ }
+ if r, ok := ref.(reference.Named); ok {
+ repo = r.Name()
+ }
+ if len(repo) < 1 {
+ return errors.Errorf("invalid image name %q", nameOrId)
+ }
+ if err := images.Tag(ir.ClientCxt, nameOrId, tag, repo); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error {
+ for _, newTag := range tags {
+ var (
+ tag, repo string
+ )
+ ref, err := reference.Parse(newTag)
+ if err != nil {
+ return err
+ }
+ if t, ok := ref.(reference.Tagged); ok {
+ tag = t.Tag()
+ }
+ if r, ok := ref.(reference.Named); ok {
+ repo = r.Name()
+ }
+ if len(repo) < 1 {
+ return errors.Errorf("invalid image name %q", nameOrId)
+ }
+ if err := images.Untag(ir.ClientCxt, nameOrId, tag, repo); err != nil {
+ return err
+ }
+ }
+ return nil
+}
- report := entities.ImagePruneReport{}
- copy(report.Report.Id, results)
+func (ir *ImageEngine) Inspect(_ context.Context, names []string, opts entities.InspectOptions) (*entities.ImageInspectReport, error) {
+ report := entities.ImageInspectReport{}
+ for _, id := range names {
+ r, err := images.GetImage(ir.ClientCxt, id, &opts.Size)
+ if err != nil {
+ report.Errors[id] = err
+ }
+ report.Images = append(report.Images, r)
+ }
return &report, nil
}
+
+func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ f, err := os.Open(opts.Input)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return images.Load(ir.ClientCxt, f, &opts.Name)
+}
+
+func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) {
+ var (
+ err error
+ sourceURL *string
+ f *os.File
+ )
+ if opts.SourceIsURL {
+ sourceURL = &opts.Source
+ } else {
+ f, err = os.Open(opts.Source)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return images.Import(ir.ClientCxt, opts.Changes, &opts.Message, &opts.Reference, sourceURL, f)
+}
+
+func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
+ return images.Push(ir.ClientCxt, source, destination, options)
+}
+
+func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error {
+ var (
+ f *os.File
+ err error
+ )
+
+ switch options.Format {
+ case "oci-dir", "docker-dir":
+ f, err = ioutil.TempFile("", "podman_save")
+ if err == nil {
+ defer func() { _ = os.Remove(f.Name()) }()
+ }
+ default:
+ f, err = os.Create(options.Output)
+ }
+ if err != nil {
+ return err
+ }
+
+ exErr := images.Export(ir.ClientCxt, nameOrId, f, &options.Format, &options.Compress)
+ if err := f.Close(); err != nil {
+ return err
+ }
+ if exErr != nil {
+ return exErr
+ }
+
+ if options.Format != "oci-dir" && options.Format != "docker-dir" {
+ return nil
+ }
+
+ f, err = os.Open(f.Name())
+ if err != nil {
+ return err
+ }
+ info, err := os.Stat(options.Output)
+ switch {
+ case err == nil:
+ if info.Mode().IsRegular() {
+ return errors.Errorf("%q already exists as a regular file", options.Output)
+ }
+ case os.IsNotExist(err):
+ if err := os.Mkdir(options.Output, 0755); err != nil {
+ return err
+ }
+ default:
+ return err
+ }
+ return utils2.UntarToFileSystem(options.Output, f, nil)
+}
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index 500069d51..dad77284f 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -5,9 +5,205 @@ import (
"github.com/containers/libpod/pkg/bindings/pods"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
)
func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
exists, err := pods.Exists(ic.ClientCxt, nameOrId)
return &entities.BoolReport{Value: exists}, err
}
+
+func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) {
+ var (
+ reports []*entities.PodKillReport
+ )
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range foundPods {
+ response, err := pods.Kill(ic.ClientCxt, p.Id, &options.Signal)
+ if err != nil {
+ report := entities.PodKillReport{
+ Errs: []error{err},
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) {
+ var (
+ reports []*entities.PodPauseReport
+ )
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range foundPods {
+ response, err := pods.Pause(ic.ClientCxt, p.Id)
+ if err != nil {
+ report := entities.PodPauseReport{
+ Errs: []error{err},
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) {
+ var (
+ reports []*entities.PodUnpauseReport
+ )
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range foundPods {
+ response, err := pods.Unpause(ic.ClientCxt, p.Id)
+ if err != nil {
+ report := entities.PodUnpauseReport{
+ Errs: []error{err},
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) {
+ var (
+ reports []*entities.PodStopReport
+ timeout int = -1
+ )
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ if options.Timeout != -1 {
+ timeout = options.Timeout
+ }
+ for _, p := range foundPods {
+ response, err := pods.Stop(ic.ClientCxt, p.Id, &timeout)
+ if err != nil {
+ report := entities.PodStopReport{
+ Errs: []error{err},
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) {
+ var reports []*entities.PodRestartReport
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range foundPods {
+ response, err := pods.Restart(ic.ClientCxt, p.Id)
+ if err != nil {
+ report := entities.PodRestartReport{
+ Errs: []error{err},
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) {
+ var reports []*entities.PodStartReport
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range foundPods {
+ response, err := pods.Start(ic.ClientCxt, p.Id)
+ if err != nil {
+ report := entities.PodStartReport{
+ Errs: []error{err},
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) {
+ var reports []*entities.PodRmReport
+ foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range foundPods {
+ response, err := pods.Remove(ic.ClientCxt, p.Id, &options.Force)
+ if err != nil {
+ report := entities.PodRmReport{
+ Err: err,
+ Id: p.Id,
+ }
+ reports = append(reports, &report)
+ continue
+ }
+ reports = append(reports, response)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
+ podSpec := specgen.NewPodSpecGenerator()
+ opts.ToPodSpecGen(podSpec)
+ return pods.CreatePodFromSpec(ic.ClientCxt, podSpec)
+}
+
+func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOptions) (*entities.StringSliceReport, error) {
+ switch {
+ case options.Latest:
+ return nil, errors.New("latest is not supported")
+ case options.NameOrID == "":
+ return nil, errors.New("NameOrID must be specified")
+ }
+
+ topOutput, err := pods.Top(ic.ClientCxt, options.NameOrID, options.Descriptors)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.StringSliceReport{Value: topOutput}, nil
+}
+
+func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
+ return pods.List(ic.ClientCxt, options.Filters)
+}
+
+func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) {
+ switch {
+ case options.Latest:
+ return nil, errors.New("latest is not supported")
+ case options.NameOrID == "":
+ return nil, errors.New("NameOrID must be specified")
+ }
+ return pods.Inspect(ic.ClientCxt, options.NameOrID)
+}
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
new file mode 100644
index 000000000..5bafef1fe
--- /dev/null
+++ b/pkg/domain/infra/tunnel/system.go
@@ -0,0 +1 @@
+package tunnel
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index 78b55bb2a..14453e7f4 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -101,7 +101,7 @@ func (n UsernsMode) IsPrivate() bool {
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
- case "", hostType, "keep-id", nsType:
+ case "", privateType, hostType, "keep-id", nsType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
@@ -173,7 +173,7 @@ func (n UTSMode) Container() string {
func (n UTSMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
- case "", hostType:
+ case "", privateType, hostType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
@@ -255,7 +255,7 @@ func (n PidMode) IsContainer() bool {
func (n PidMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
- case "", hostType:
+ case "", privateType, hostType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 12dfed8c3..daa997104 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -196,6 +196,7 @@ func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, err
if err != nil {
return nil, err
}
+ storageConfig := runtime.StorageConfig()
// We need a cleanup process for containers in the current model.
// But we can't assume that the caller is Podman - it could be another
@@ -208,23 +209,23 @@ func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, err
}
command := []string{cmd,
- "--root", config.StorageConfig.GraphRoot,
- "--runroot", config.StorageConfig.RunRoot,
+ "--root", storageConfig.GraphRoot,
+ "--runroot", storageConfig.RunRoot,
"--log-level", logrus.GetLevel().String(),
- "--cgroup-manager", config.CgroupManager,
- "--tmpdir", config.TmpDir,
+ "--cgroup-manager", config.Engine.CgroupManager,
+ "--tmpdir", config.Engine.TmpDir,
}
- if config.OCIRuntime != "" {
- command = append(command, []string{"--runtime", config.OCIRuntime}...)
+ if config.Engine.OCIRuntime != "" {
+ command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
}
- if config.StorageConfig.GraphDriverName != "" {
- command = append(command, []string{"--storage-driver", config.StorageConfig.GraphDriverName}...)
+ if storageConfig.GraphDriverName != "" {
+ command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
}
- for _, opt := range config.StorageConfig.GraphDriverOptions {
+ for _, opt := range storageConfig.GraphDriverOptions {
command = append(command, []string{"--storage-opt", opt}...)
}
- if config.EventsLogger != "" {
- command = append(command, []string{"--events-backend", config.EventsLogger}...)
+ if config.Engine.EventsLogger != "" {
+ command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
}
if c.Syslog {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 8f0630b85..5de07fc28 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -4,9 +4,8 @@ import (
"strings"
"github.com/containers/common/pkg/capabilities"
+ cconfig "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- libpodconfig "github.com/containers/libpod/libpod/config"
- "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/rootless"
@@ -81,6 +80,37 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
g.AddLinuxMaskedPaths("/sys/kernel")
}
}
+ var runtimeConfig *cconfig.Config
+
+ if runtime != nil {
+ runtimeConfig, err = runtime.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ g.Config.Process.Capabilities.Bounding = runtimeConfig.Containers.DefaultCapabilities
+ sysctls, err := util.ValidateSysctls(runtimeConfig.Containers.DefaultSysctls)
+ if err != nil {
+ return nil, err
+ }
+
+ for name, val := range config.Security.Sysctl {
+ sysctls[name] = val
+ }
+ config.Security.Sysctl = sysctls
+ if !util.StringInSlice("host", config.Resources.Ulimit) {
+ config.Resources.Ulimit = append(runtimeConfig.Containers.DefaultUlimits, config.Resources.Ulimit...)
+ }
+ if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() {
+ config.Resources.PidsLimit = runtimeConfig.Containers.PidsLimit
+ }
+
+ } else {
+ g.Config.Process.Capabilities.Bounding = cconfig.DefaultCapabilities
+ if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() {
+ config.Resources.PidsLimit = cconfig.DefaultPidsLimit
+ }
+ }
+
gid5Available := true
if isRootless {
nGids, err := GetAvailableGids()
@@ -242,16 +272,6 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
- // SECURITY OPTS
- var runtimeConfig *libpodconfig.Config
-
- if runtime != nil {
- runtimeConfig, err = runtime.GetConfig()
- if err != nil {
- return nil, err
- }
- }
-
g.SetProcessNoNewPrivileges(config.Security.NoNewPrivs)
if !config.Security.Privileged {
@@ -261,7 +281,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// Unless already set via the CLI, check if we need to disable process
// labels or set the defaults.
if len(config.Security.LabelOpts) == 0 && runtimeConfig != nil {
- if !runtimeConfig.EnableLabeling {
+ if !runtimeConfig.Containers.EnableLabeling {
// Disabled in the config.
config.Security.LabelOpts = append(config.Security.LabelOpts, "disable")
} else if err := config.Security.SetLabelOpts(runtime, &config.Pid, &config.Ipc); err != nil {
@@ -284,7 +304,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
if err != nil {
return nil, err
}
- if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.CgroupManager != define.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
+ if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.Engine.CgroupManager != cconfig.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
setPidLimit = false
}
}
@@ -296,7 +316,17 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// Make sure to always set the default variables unless overridden in the
// config.
- config.Env = env.Join(env.DefaultEnvVariables, config.Env)
+ var defaultEnv map[string]string
+ if runtimeConfig == nil {
+ defaultEnv = env.DefaultEnvVariables
+ } else {
+ defaultEnv, err = env.ParseSlice(runtimeConfig.Containers.Env)
+ if err != nil {
+ return nil, errors.Wrap(err, "Env fields in containers.conf failed ot parse")
+ }
+ defaultEnv = env.Join(env.DefaultEnvVariables, defaultEnv)
+ }
+ config.Env = env.Join(defaultEnv, config.Env)
for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
@@ -351,11 +381,9 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// BIND MOUNTS
configSpec.Mounts = SupercedeUserMounts(userMounts, configSpec.Mounts)
// Process mounts to ensure correct options
- finalMounts, err := InitFSMounts(configSpec.Mounts)
- if err != nil {
+ if err := InitFSMounts(configSpec.Mounts); err != nil {
return nil, err
}
- configSpec.Mounts = finalMounts
// BLOCK IO
blkio, err := config.CreateBlockIO()
@@ -376,7 +404,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
configSpec.Linux.Resources = &spec.LinuxResources{}
}
- canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.CgroupManager == define.SystemdCgroupsManager)
+ canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.Engine.CgroupManager == cconfig.SystemdCgroupsManager)
if addedResources && !canUseResources {
return nil, errors.New("invalid configuration, cannot specify resource limits without cgroups v2 and --cgroup-manager=systemd")
@@ -433,6 +461,10 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
return configSpec, nil
}
+func (config *CreateConfig) cgroupDisabled() bool {
+ return config.Cgroup.Cgroups == "disabled"
+}
+
func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) {
if !privileged {
for _, mp := range []string{
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index c365701de..68a84d638 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -10,7 +10,6 @@ import (
"github.com/containers/buildah/pkg/parse"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/util"
- pmount "github.com/containers/storage/pkg/mount"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -124,7 +123,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
if err != nil {
return nil, nil, err
}
- initPath = rtc.InitPath
+ initPath = rtc.Engine.InitPath
}
initMount, err := config.addContainerInitBinary(initPath)
if err != nil {
@@ -855,75 +854,22 @@ func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.M
}
// Ensure mount options on all mounts are correct
-func InitFSMounts(inputMounts []spec.Mount) ([]spec.Mount, error) {
- // We need to look up mounts so we can figure out the proper mount flags
- // to apply.
- systemMounts, err := pmount.GetMounts()
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving system mounts to look up mount options")
- }
-
- // TODO: We probably don't need to re-build the mounts array
- var mounts []spec.Mount
- for _, m := range inputMounts {
- if m.Type == TypeBind {
- baseMnt, err := findMount(m.Destination, systemMounts)
+func InitFSMounts(mounts []spec.Mount) error {
+ for i, m := range mounts {
+ switch {
+ case m.Type == TypeBind:
+ opts, err := util.ProcessOptions(m.Options, false, m.Source)
if err != nil {
- return nil, errors.Wrapf(err, "error looking up mountpoint for mount %s", m.Destination)
- }
- var noexec, nosuid, nodev bool
- for _, baseOpt := range strings.Split(baseMnt.Opts, ",") {
- switch baseOpt {
- case "noexec":
- noexec = true
- case "nosuid":
- nosuid = true
- case "nodev":
- nodev = true
- }
+ return err
}
-
- defaultMountOpts := new(util.DefaultMountOptions)
- defaultMountOpts.Noexec = noexec
- defaultMountOpts.Nosuid = nosuid
- defaultMountOpts.Nodev = nodev
-
- opts, err := util.ProcessOptions(m.Options, false, defaultMountOpts)
+ mounts[i].Options = opts
+ case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
+ opts, err := util.ProcessOptions(m.Options, true, "")
if err != nil {
- return nil, err
+ return err
}
- m.Options = opts
- }
- if m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev" {
- opts, err := util.ProcessOptions(m.Options, true, nil)
- if err != nil {
- return nil, err
- }
- m.Options = opts
- }
-
- mounts = append(mounts, m)
- }
- return mounts, nil
-}
-
-// TODO: We could make this a bit faster by building a tree of the mountpoints
-// and traversing it to identify the correct mount.
-func findMount(target string, mounts []*pmount.Info) (*pmount.Info, error) {
- var err error
- target, err = filepath.Abs(target)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot resolve %s", target)
- }
- var bestSoFar *pmount.Info
- for _, i := range mounts {
- if bestSoFar != nil && len(bestSoFar.Mountpoint) > len(i.Mountpoint) {
- // Won't be better than what we have already found
- continue
- }
- if strings.HasPrefix(target, i.Mountpoint) {
- bestSoFar = i
+ mounts[i].Options = opts
}
}
- return bestSoFar, nil
+ return nil
}
diff --git a/pkg/specgen/config_linux.go b/pkg/specgen/config_linux.go
new file mode 100644
index 000000000..82a371492
--- /dev/null
+++ b/pkg/specgen/config_linux.go
@@ -0,0 +1,93 @@
+package specgen
+
+//func createBlockIO() (*spec.LinuxBlockIO, error) {
+// var ret *spec.LinuxBlockIO
+// bio := &spec.LinuxBlockIO{}
+// if c.Resources.BlkioWeight > 0 {
+// ret = bio
+// bio.Weight = &c.Resources.BlkioWeight
+// }
+// if len(c.Resources.BlkioWeightDevice) > 0 {
+// var lwds []spec.LinuxWeightDevice
+// ret = bio
+// for _, i := range c.Resources.BlkioWeightDevice {
+// wd, err := ValidateweightDevice(i)
+// if err != nil {
+// return ret, errors.Wrapf(err, "invalid values for blkio-weight-device")
+// }
+// wdStat, err := GetStatFromPath(wd.Path)
+// if err != nil {
+// return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path)
+// }
+// lwd := spec.LinuxWeightDevice{
+// Weight: &wd.Weight,
+// }
+// lwd.Major = int64(unix.Major(wdStat.Rdev))
+// lwd.Minor = int64(unix.Minor(wdStat.Rdev))
+// lwds = append(lwds, lwd)
+// }
+// bio.WeightDevice = lwds
+// }
+// if len(c.Resources.DeviceReadBps) > 0 {
+// ret = bio
+// readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleReadBpsDevice = readBps
+// }
+// if len(c.Resources.DeviceWriteBps) > 0 {
+// ret = bio
+// writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleWriteBpsDevice = writeBpds
+// }
+// if len(c.Resources.DeviceReadIOps) > 0 {
+// ret = bio
+// readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleReadIOPSDevice = readIOps
+// }
+// if len(c.Resources.DeviceWriteIOps) > 0 {
+// ret = bio
+// writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
+// if err != nil {
+// return ret, err
+// }
+// bio.ThrottleWriteIOPSDevice = writeIOps
+// }
+// return ret, nil
+//}
+
+//func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
+// var (
+// ltds []spec.LinuxThrottleDevice
+// t *throttleDevice
+// err error
+// )
+// for _, i := range throttleInput {
+// if rateType == bps {
+// t, err = validateBpsDevice(i)
+// } else {
+// t, err = validateIOpsDevice(i)
+// }
+// if err != nil {
+// return []spec.LinuxThrottleDevice{}, err
+// }
+// ltdStat, err := GetStatFromPath(t.path)
+// if err != nil {
+// return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
+// }
+// ltd := spec.LinuxThrottleDevice{
+// Rate: t.rate,
+// }
+// ltd.Major = int64(unix.Major(ltdStat.Rdev))
+// ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
+// ltds = append(ltds, ltd)
+// }
+// return ltds, nil
+//}
diff --git a/pkg/specgen/config_linux_cgo.go b/pkg/specgen/config_linux_cgo.go
index 6f547a40d..ef6c6e951 100644
--- a/pkg/specgen/config_linux_cgo.go
+++ b/pkg/specgen/config_linux_cgo.go
@@ -17,7 +17,6 @@ import (
func (s *SpecGenerator) getSeccompConfig(configSpec *spec.Spec, img *image.Image) (*spec.LinuxSeccomp, error) {
var seccompConfig *spec.LinuxSeccomp
var err error
-
scp, err := seccomp.LookupPolicy(s.SeccompPolicy)
if err != nil {
return nil, err
diff --git a/pkg/specgen/validate.go b/pkg/specgen/container_validate.go
index dd5ca3a55..aad14ddcb 100644
--- a/pkg/specgen/validate.go
+++ b/pkg/specgen/container_validate.go
@@ -3,7 +3,7 @@ package specgen
import (
"strings"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
)
@@ -14,7 +14,7 @@ var (
// SystemDValues describes the only values that SystemD can be
SystemDValues = []string{"true", "false", "always"}
// ImageVolumeModeValues describes the only values that ImageVolumeMode can be
- ImageVolumeModeValues = []string{"ignore", "tmpfs", "anonymous"}
+ ImageVolumeModeValues = []string{"ignore", "tmpfs", "bind"}
)
func exclusiveOptions(opt1, opt2 string) error {
@@ -23,7 +23,7 @@ func exclusiveOptions(opt1, opt2 string) error {
// Validate verifies that the given SpecGenerator is valid and satisfies required
// input for creating a container.
-func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
+func (s *SpecGenerator) Validate() error {
//
// ContainerBasicConfig
@@ -138,9 +138,6 @@ func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
if err := s.IpcNS.validate(); err != nil {
return err
}
- if err := validateNetNS(&s.NetNS); err != nil {
- return err
- }
if err := s.PidNS.validate(); err != nil {
return err
}
@@ -155,5 +152,16 @@ func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
if len(s.WorkDir) < 1 {
s.WorkDir = "/"
}
+
+ // Set defaults if network info is not provided
+ if s.NetNS.NSMode == "" {
+ s.NetNS.NSMode = Bridge
+ if rootless.IsRootless() {
+ s.NetNS.NSMode = Slirp
+ }
+ }
+ if err := validateNetNS(&s.NetNS); err != nil {
+ return err
+ }
return nil
}
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
new file mode 100644
index 000000000..78c77fec1
--- /dev/null
+++ b/pkg/specgen/generate/container.go
@@ -0,0 +1,168 @@
+package generate
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod"
+ ann "github.com/containers/libpod/pkg/annotations"
+ envLib "github.com/containers/libpod/pkg/env"
+ "github.com/containers/libpod/pkg/signal"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+ "golang.org/x/sys/unix"
+)
+
+func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) error {
+
+ newImage, err := r.ImageRuntime().NewFromLocal(s.Image)
+ if err != nil {
+ return err
+ }
+
+ // Image stop signal
+ if s.StopSignal == nil && newImage.Config != nil {
+ sig, err := signal.ParseSignalNameOrNumber(newImage.Config.StopSignal)
+ if err != nil {
+ return err
+ }
+ s.StopSignal = &sig
+ }
+ // Image envs from the image if they don't exist
+ // already
+ if newImage.Config != nil && len(newImage.Config.Env) > 0 {
+ envs, err := envLib.ParseSlice(newImage.Config.Env)
+ if err != nil {
+ return err
+ }
+ for k, v := range envs {
+ if _, exists := s.Env[k]; !exists {
+ s.Env[v] = k
+ }
+ }
+ }
+
+ // labels from the image that dont exist already
+ if config := newImage.Config; config != nil {
+ for k, v := range config.Labels {
+ if _, exists := s.Labels[k]; !exists {
+ s.Labels[k] = v
+ }
+ }
+ }
+
+ // annotations
+ // in the event this container is in a pod, and the pod has an infra container
+ // we will want to configure it as a type "container" instead defaulting to
+ // the behavior of a "sandbox" container
+ // In Kata containers:
+ // - "sandbox" is the annotation that denotes the container should use its own
+ // VM, which is the default behavior
+ // - "container" denotes the container should join the VM of the SandboxID
+ // (the infra container)
+ s.Annotations = make(map[string]string)
+ if len(s.Pod) > 0 {
+ s.Annotations[ann.SandboxID] = s.Pod
+ s.Annotations[ann.ContainerType] = ann.ContainerTypeContainer
+ }
+ //
+ // Next, add annotations from the image
+ annotations, err := newImage.Annotations(ctx)
+ if err != nil {
+ return err
+ }
+ for k, v := range annotations {
+ annotations[k] = v
+ }
+
+ // entrypoint
+ if config := newImage.Config; config != nil {
+ if len(s.Entrypoint) < 1 && len(config.Entrypoint) > 0 {
+ s.Entrypoint = config.Entrypoint
+ }
+ if len(s.Command) < 1 && len(config.Cmd) > 0 {
+ s.Command = config.Cmd
+ }
+ if len(s.Command) < 1 && len(s.Entrypoint) < 1 {
+ return errors.Errorf("No command provided or as CMD or ENTRYPOINT in this image")
+ }
+ // workdir
+ if len(s.WorkDir) < 1 && len(config.WorkingDir) > 1 {
+ s.WorkDir = config.WorkingDir
+ }
+ }
+
+ if len(s.SeccompProfilePath) < 1 {
+ p, err := libpod.DefaultSeccompPath()
+ if err != nil {
+ return err
+ }
+ s.SeccompProfilePath = p
+ }
+
+ if user := s.User; len(user) == 0 {
+ switch {
+ // TODO This should be enabled when namespaces actually work
+ //case usernsMode.IsKeepID():
+ // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
+ case newImage.Config == nil || (newImage.Config != nil && len(newImage.Config.User) == 0):
+ s.User = "0"
+ default:
+ s.User = newImage.Config.User
+ }
+ }
+ if err := finishThrottleDevices(s); err != nil {
+ return err
+ }
+ return nil
+}
+
+// finishThrottleDevices takes the temporary representation of the throttle
+// devices in the specgen and looks up the major and major minors. it then
+// sets the throttle devices proper in the specgen
+func finishThrottleDevices(s *specgen.SpecGenerator) error {
+ if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
+ for k, v := range bps {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
+ }
+ }
+ if bps := s.ThrottleWriteBpsDevice; len(bps) > 0 {
+ for k, v := range bps {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v)
+ }
+ }
+ if iops := s.ThrottleReadIOPSDevice; len(iops) > 0 {
+ for k, v := range iops {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v)
+ }
+ }
+ if iops := s.ThrottleWriteBpsDevice; len(iops) > 0 {
+ for k, v := range iops {
+ statT := unix.Stat_t{}
+ if err := unix.Stat(k, &statT); err != nil {
+ return err
+ }
+ v.Major = (int64(unix.Major(statT.Rdev)))
+ v.Minor = (int64(unix.Minor(statT.Rdev)))
+ s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v)
+ }
+ }
+ return nil
+}
diff --git a/pkg/specgen/create.go b/pkg/specgen/generate/container_create.go
index aefbe7405..aad59a861 100644
--- a/pkg/specgen/create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -1,19 +1,21 @@
-package specgen
+package generate
import (
"context"
"os"
+ "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/config"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// MakeContainer creates a container based on the SpecGenerator
-func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, error) {
- if err := s.validate(rt); err != nil {
+func MakeContainer(rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) {
+ if err := s.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid config provided")
}
rtc, err := rt.GetConfig()
@@ -21,7 +23,7 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
return nil, err
}
- options, err := s.createContainerOptions(rt)
+ options, err := createContainerOptions(rt, s)
if err != nil {
return nil, err
}
@@ -30,7 +32,7 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
if err != nil {
return nil, err
}
- options = append(options, s.createExitCommandOption(rtc, podmanPath))
+ options = append(options, createExitCommandOption(s, rt.StorageConfig(), rtc, podmanPath))
newImage, err := rt.ImageRuntime().NewFromLocal(s.Image)
if err != nil {
return nil, err
@@ -38,14 +40,14 @@ func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, er
options = append(options, libpod.WithRootFSFromImage(newImage.ID(), s.Image, s.RawImageName))
- runtimeSpec, err := s.toOCISpec(rt, newImage)
+ runtimeSpec, err := s.ToOCISpec(rt, newImage)
if err != nil {
return nil, err
}
return rt.NewContainer(context.Background(), runtimeSpec, options...)
}
-func (s *SpecGenerator) createContainerOptions(rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error
@@ -113,7 +115,7 @@ func (s *SpecGenerator) createContainerOptions(rt *libpod.Runtime) ([]libpod.Ctr
options = append(options, libpod.WithPrivileged(s.Privileged))
// Get namespace related options
- namespaceOptions, err := s.generateNamespaceContainerOpts(rt)
+ namespaceOptions, err := s.GenerateNamespaceContainerOpts(rt)
if err != nil {
return nil, err
}
@@ -148,7 +150,7 @@ func (s *SpecGenerator) createContainerOptions(rt *libpod.Runtime) ([]libpod.Ctr
return options, nil
}
-func (s *SpecGenerator) createExitCommandOption(config *config.Config, podmanPath string) libpod.CtrCreateOption {
+func createExitCommandOption(s *specgen.SpecGenerator, storageConfig storage.StoreOptions, config *config.Config, podmanPath string) libpod.CtrCreateOption {
// We need a cleanup process for containers in the current model.
// But we can't assume that the caller is Podman - it could be another
// user of the API.
@@ -156,23 +158,23 @@ func (s *SpecGenerator) createExitCommandOption(config *config.Config, podmanPat
// still invoke a cleanup process.
command := []string{podmanPath,
- "--root", config.StorageConfig.GraphRoot,
- "--runroot", config.StorageConfig.RunRoot,
+ "--root", storageConfig.GraphRoot,
+ "--runroot", storageConfig.RunRoot,
"--log-level", logrus.GetLevel().String(),
- "--cgroup-manager", config.CgroupManager,
- "--tmpdir", config.TmpDir,
+ "--cgroup-manager", config.Engine.CgroupManager,
+ "--tmpdir", config.Engine.TmpDir,
}
- if config.OCIRuntime != "" {
- command = append(command, []string{"--runtime", config.OCIRuntime}...)
+ if config.Engine.OCIRuntime != "" {
+ command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
}
- if config.StorageConfig.GraphDriverName != "" {
- command = append(command, []string{"--storage-driver", config.StorageConfig.GraphDriverName}...)
+ if storageConfig.GraphDriverName != "" {
+ command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
}
- for _, opt := range config.StorageConfig.GraphDriverOptions {
+ for _, opt := range storageConfig.GraphDriverOptions {
command = append(command, []string{"--storage-opt", opt}...)
}
- if config.EventsLogger != "" {
- command = append(command, []string{"--events-backend", config.EventsLogger}...)
+ if config.Engine.EventsLogger != "" {
+ command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
}
// TODO Mheon wants to leave this for now
diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
index fa2dee77d..2a7bb3495 100644
--- a/pkg/specgen/namespaces.go
+++ b/pkg/specgen/namespaces.go
@@ -16,6 +16,9 @@ import (
type NamespaceMode string
const (
+ // Default indicates the spec generator should determine
+ // a sane default
+ Default NamespaceMode = "default"
// Host means the the namespace is derived from
// the host
Host NamespaceMode = "host"
@@ -83,7 +86,7 @@ func validateNetNS(n *Namespace) error {
return nil
}
-// validate perform simple validation on the namespace to make sure it is not
+// Validate perform simple validation on the namespace to make sure it is not
// invalid from the get-go
func (n *Namespace) validate() error {
if n == nil {
@@ -103,7 +106,7 @@ func (n *Namespace) validate() error {
return nil
}
-func (s *SpecGenerator) generateNamespaceContainerOpts(rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+func (s *SpecGenerator) GenerateNamespaceContainerOpts(rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
var portBindings []ocicni.PortMapping
options := make([]libpod.CtrCreateOption, 0)
diff --git a/pkg/specgen/oci.go b/pkg/specgen/oci.go
index 2523f21b3..0756782b4 100644
--- a/pkg/specgen/oci.go
+++ b/pkg/specgen/oci.go
@@ -11,7 +11,7 @@ import (
"github.com/opencontainers/runtime-tools/generate"
)
-func (s *SpecGenerator) toOCISpec(rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) {
+func (s *SpecGenerator) ToOCISpec(rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) {
var (
inUserNS bool
)
@@ -215,11 +215,9 @@ func (s *SpecGenerator) toOCISpec(rt *libpod.Runtime, newImage *image.Image) (*s
// BIND MOUNTS
configSpec.Mounts = createconfig.SupercedeUserMounts(s.Mounts, configSpec.Mounts)
// Process mounts to ensure correct options
- finalMounts, err := createconfig.InitFSMounts(configSpec.Mounts)
- if err != nil {
+ if err := createconfig.InitFSMounts(configSpec.Mounts); err != nil {
return nil, err
}
- configSpec.Mounts = finalMounts
// Add annotations
if configSpec.Annotations == nil {
diff --git a/pkg/specgen/pod_create.go b/pkg/specgen/pod_create.go
new file mode 100644
index 000000000..06aa24e22
--- /dev/null
+++ b/pkg/specgen/pod_create.go
@@ -0,0 +1,83 @@
+package specgen
+
+import (
+ "context"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/sirupsen/logrus"
+)
+
+func (p *PodSpecGenerator) MakePod(rt *libpod.Runtime) (*libpod.Pod, error) {
+ if err := p.validate(); err != nil {
+ return nil, err
+ }
+ options, err := p.createPodOptions()
+ if err != nil {
+ return nil, err
+ }
+ return rt.NewPod(context.Background(), options...)
+}
+
+func (p *PodSpecGenerator) createPodOptions() ([]libpod.PodCreateOption, error) {
+ var (
+ options []libpod.PodCreateOption
+ )
+ if !p.NoInfra {
+ options = append(options, libpod.WithInfraContainer())
+ nsOptions, err := shared.GetNamespaceOptions(p.SharedNamespaces)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, nsOptions...)
+ }
+ if len(p.CgroupParent) > 0 {
+ options = append(options, libpod.WithPodCgroupParent(p.CgroupParent))
+ }
+ if len(p.Labels) > 0 {
+ options = append(options, libpod.WithPodLabels(p.Labels))
+ }
+ if len(p.Name) > 0 {
+ options = append(options, libpod.WithPodName(p.Name))
+ }
+ if len(p.Hostname) > 0 {
+ options = append(options, libpod.WithPodHostname(p.Hostname))
+ }
+ if len(p.HostAdd) > 0 {
+ options = append(options, libpod.WithPodHosts(p.HostAdd))
+ }
+ if len(p.DNSOption) > 0 {
+ options = append(options, libpod.WithPodDNSOption(p.DNSOption))
+ }
+ if len(p.DNSSearch) > 0 {
+ options = append(options, libpod.WithPodDNSSearch(p.DNSSearch))
+ }
+ if p.StaticIP != nil {
+ options = append(options, libpod.WithPodStaticIP(*p.StaticIP))
+ }
+ if p.StaticMAC != nil {
+ options = append(options, libpod.WithPodStaticMAC(*p.StaticMAC))
+ }
+ if p.NoManageResolvConf {
+ options = append(options, libpod.WithPodUseImageResolvConf())
+ }
+ switch p.NetNS.NSMode {
+ case Bridge:
+ logrus.Debugf("Pod using default network mode")
+ case Host:
+ logrus.Debugf("Pod will use host networking")
+ options = append(options, libpod.WithPodHostNetwork())
+ default:
+ logrus.Debugf("Pod joining CNI networks: %v", p.CNINetworks)
+ options = append(options, libpod.WithPodNetworks(p.CNINetworks))
+ }
+
+ if p.NoManageHosts {
+ options = append(options, libpod.WithPodUseImageHosts())
+ }
+ if len(p.PortMappings) > 0 {
+ options = append(options, libpod.WithInfraContainerPorts(p.PortMappings))
+ }
+ options = append(options, libpod.WithPodCgroups())
+ return options, nil
+}
diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go
new file mode 100644
index 000000000..50309f096
--- /dev/null
+++ b/pkg/specgen/pod_validate.go
@@ -0,0 +1,104 @@
+package specgen
+
+import (
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+)
+
+var (
+ // ErrInvalidPodSpecConfig describes an error given when the podspecgenerator is invalid
+ ErrInvalidPodSpecConfig error = errors.New("invalid pod spec")
+)
+
+func exclusivePodOptions(opt1, opt2 string) error {
+ return errors.Wrapf(ErrInvalidPodSpecConfig, "%s and %s are mutually exclusive pod options", opt1, opt2)
+}
+
+func (p *PodSpecGenerator) validate() error {
+ // PodBasicConfig
+ if p.NoInfra {
+ if len(p.InfraCommand) > 0 {
+ return exclusivePodOptions("NoInfra", "InfraCommand")
+ }
+ if len(p.InfraImage) > 0 {
+ return exclusivePodOptions("NoInfra", "InfraImage")
+ }
+ if len(p.SharedNamespaces) > 0 {
+ return exclusivePodOptions("NoInfo", "SharedNamespaces")
+ }
+ }
+
+ // PodNetworkConfig
+ if err := p.NetNS.validate(); err != nil {
+ return err
+ }
+ if p.NoInfra {
+ if p.NetNS.NSMode == NoNetwork {
+ return errors.New("NoInfra and a none network cannot be used toegther")
+ }
+ if p.StaticIP != nil {
+ return exclusivePodOptions("NoInfra", "StaticIP")
+ }
+ if p.StaticMAC != nil {
+ return exclusivePodOptions("NoInfra", "StaticMAC")
+ }
+ if len(p.DNSOption) > 0 {
+ return exclusivePodOptions("NoInfra", "DNSOption")
+ }
+ if len(p.DNSSearch) > 0 {
+ return exclusivePodOptions("NoInfo", "DNSSearch")
+ }
+ if len(p.DNSServer) > 0 {
+ return exclusivePodOptions("NoInfra", "DNSServer")
+ }
+ if len(p.HostAdd) > 0 {
+ return exclusivePodOptions("NoInfra", "HostAdd")
+ }
+ if p.NoManageResolvConf {
+ return exclusivePodOptions("NoInfra", "NoManageResolvConf")
+ }
+ }
+ if p.NetNS.NSMode != Bridge {
+ if len(p.PortMappings) > 0 {
+ return errors.New("PortMappings can only be used with Bridge mode networking")
+ }
+ if len(p.CNINetworks) > 0 {
+ return errors.New("CNINetworks can only be used with Bridge mode networking")
+ }
+ }
+ if p.NoManageResolvConf {
+ if len(p.DNSServer) > 0 {
+ return exclusivePodOptions("NoManageResolvConf", "DNSServer")
+ }
+ if len(p.DNSSearch) > 0 {
+ return exclusivePodOptions("NoManageResolvConf", "DNSSearch")
+ }
+ if len(p.DNSOption) > 0 {
+ return exclusivePodOptions("NoManageResolvConf", "DNSOption")
+ }
+ }
+ if p.NoManageHosts && len(p.HostAdd) > 0 {
+ return exclusivePodOptions("NoManageHosts", "HostAdd")
+ }
+
+ if err := p.NetNS.validate(); err != nil {
+ return err
+ }
+
+ // Set Defaults
+ if p.NetNS.Value == "" {
+ if rootless.IsRootless() {
+ p.NetNS.NSMode = Slirp
+ } else {
+ p.NetNS.NSMode = Bridge
+ }
+ }
+ if len(p.InfraImage) < 1 {
+ p.InfraImage = define.DefaultInfraImage
+ }
+ if len(p.InfraCommand) < 1 {
+ p.InfraCommand = []string{define.DefaultInfraCommand}
+ }
+ return nil
+}
diff --git a/pkg/specgen/pod.go b/pkg/specgen/podspecgen.go
index 1aada83c4..3f830014d 100644
--- a/pkg/specgen/pod.go
+++ b/pkg/specgen/podspecgen.go
@@ -138,3 +138,16 @@ type PodCgroupConfig struct {
// Optional.
CgroupParent string `json:"cgroup_parent,omitempty"`
}
+
+// PodSpecGenerator describes options to create a pod
+// swagger:model PodSpecGenerator
+type PodSpecGenerator struct {
+ PodBasicConfig
+ PodNetworkConfig
+ PodCgroupConfig
+}
+
+// NewPodSpecGenerator creates a new pod spec
+func NewPodSpecGenerator() *PodSpecGenerator {
+ return &PodSpecGenerator{}
+}
diff --git a/pkg/specgen/security.go b/pkg/specgen/security.go
new file mode 100644
index 000000000..158e4a7b3
--- /dev/null
+++ b/pkg/specgen/security.go
@@ -0,0 +1,165 @@
+package specgen
+
+// ToCreateOptions convert the SecurityConfig to a slice of container create
+// options.
+/*
+func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ options = append(options, libpod.WithSecLabels(c.LabelOpts))
+ options = append(options, libpod.WithPrivileged(c.Privileged))
+ return options, nil
+}
+*/
+
+// SetLabelOpts sets the label options of the SecurityConfig according to the
+// input.
+/*
+func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error {
+ if c.Privileged {
+ c.LabelOpts = label.DisableSecOpt()
+ return nil
+ }
+
+ var labelOpts []string
+ if pidConfig.PidMode.IsHost() {
+ labelOpts = append(labelOpts, label.DisableSecOpt()...)
+ } else if pidConfig.PidMode.IsContainer() {
+ ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container())
+ if err != nil {
+ return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container())
+ }
+ secopts, err := label.DupSecOpt(ctr.ProcessLabel())
+ if err != nil {
+ return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
+ }
+ labelOpts = append(labelOpts, secopts...)
+ }
+
+ if ipcConfig.IpcMode.IsHost() {
+ labelOpts = append(labelOpts, label.DisableSecOpt()...)
+ } else if ipcConfig.IpcMode.IsContainer() {
+ ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container())
+ if err != nil {
+ return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container())
+ }
+ secopts, err := label.DupSecOpt(ctr.ProcessLabel())
+ if err != nil {
+ return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
+ }
+ labelOpts = append(labelOpts, secopts...)
+ }
+
+ c.LabelOpts = append(c.LabelOpts, labelOpts...)
+ return nil
+}
+*/
+
+// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.).
+func SetSecurityOpts(securityOpts []string) error {
+ return nil
+}
+
+// ConfigureGenerator configures the generator according to the input.
+/*
+func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error {
+ // HANDLE CAPABILITIES
+ // NOTE: Must happen before SECCOMP
+ if c.Privileged {
+ g.SetupPrivileged(true)
+ }
+
+ useNotRoot := func(user string) bool {
+ if user == "" || user == "root" || user == "0" {
+ return false
+ }
+ return true
+ }
+
+ configSpec := g.Config
+ var err error
+ var defaultCaplist []string
+ bounding := configSpec.Process.Capabilities.Bounding
+ if useNotRoot(user.User) {
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+ }
+ defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
+ if err != nil {
+ return err
+ }
+
+ privCapRequired := []string{}
+
+ if !c.Privileged && len(c.CapRequired) > 0 {
+ // Pass CapRequired in CapAdd field to normalize capabilities names
+ capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil)
+ if err != nil {
+ logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ","))
+ } else {
+ // Verify all capRequiered are in the defaultCapList
+ for _, cap := range capRequired {
+ if !util.StringInSlice(cap, defaultCaplist) {
+ privCapRequired = append(privCapRequired, cap)
+ }
+ }
+ }
+ if len(privCapRequired) == 0 {
+ defaultCaplist = capRequired
+ } else {
+ logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ","))
+ }
+ }
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+ configSpec.Process.Capabilities.Permitted = defaultCaplist
+ configSpec.Process.Capabilities.Inheritable = defaultCaplist
+ configSpec.Process.Capabilities.Effective = defaultCaplist
+ configSpec.Process.Capabilities.Ambient = defaultCaplist
+ if useNotRoot(user.User) {
+ defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
+ if err != nil {
+ return err
+ }
+ }
+ configSpec.Process.Capabilities.Bounding = defaultCaplist
+
+ // HANDLE SECCOMP
+ if c.SeccompProfilePath != "unconfined" {
+ seccompConfig, err := getSeccompConfig(c, configSpec)
+ if err != nil {
+ return err
+ }
+ configSpec.Linux.Seccomp = seccompConfig
+ }
+
+ // Clear default Seccomp profile from Generator for privileged containers
+ if c.SeccompProfilePath == "unconfined" || c.Privileged {
+ configSpec.Linux.Seccomp = nil
+ }
+
+ for _, opt := range c.SecurityOpts {
+ // Split on both : and =
+ splitOpt := strings.Split(opt, "=")
+ if len(splitOpt) == 1 {
+ splitOpt = strings.Split(opt, ":")
+ }
+ if len(splitOpt) < 2 {
+ continue
+ }
+ switch splitOpt[0] {
+ case "label":
+ configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
+ case "seccomp":
+ configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
+ case "apparmor":
+ configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
+ }
+ }
+
+ g.SetRootReadonly(c.ReadOnlyRootfs)
+ for sysctlKey, sysctlVal := range c.Sysctl {
+ g.AddLinuxSysctl(sysctlKey, sysctlVal)
+ }
+
+ return nil
+}
+
+*/
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index b123c1da5..2e6dd9c1d 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -4,8 +4,9 @@ import (
"net"
"syscall"
- "github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod"
+
+ "github.com/containers/image/v5/manifest"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/cri-o/ocicni/pkg/ocicni"
@@ -371,6 +372,16 @@ type ContainerResourceConfig struct {
// processes to kill for the container's process.
// Optional.
OOMScoreAdj *int `json:"oom_score_adj,omitempty"`
+ // Weight per cgroup per device, can override BlkioWeight
+ WeightDevice map[string]spec.LinuxWeightDevice `json:"weightDevice,omitempty"`
+ // IO read rate limit per cgroup per device, bytes per second
+ ThrottleReadBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"`
+ // IO write rate limit per cgroup per device, bytes per second
+ ThrottleWriteBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleWriteBpsDevice,omitempty"`
+ // IO read rate limit per cgroup per device, IO per second
+ ThrottleReadIOPSDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadIOPSDevice,omitempty"`
+ // IO write rate limit per cgroup per device, IO per second
+ ThrottleWriteIOPSDevice map[string]spec.LinuxThrottleDevice `json:"throttleWriteIOPSDevice,omitempty"`
}
// ContainerHealthCheckConfig describes a container healthcheck with attributes
@@ -394,18 +405,18 @@ type SpecGenerator struct {
// NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs
func NewSpecGenerator(image string) *SpecGenerator {
- net := ContainerNetworkConfig{
+ networkConfig := ContainerNetworkConfig{
NetNS: Namespace{
NSMode: Bridge,
},
}
csc := ContainerStorageConfig{Image: image}
if rootless.IsRootless() {
- net.NetNS.NSMode = Slirp
+ networkConfig.NetNS.NSMode = Slirp
}
return &SpecGenerator{
ContainerStorageConfig: csc,
- ContainerNetworkConfig: net,
+ ContainerNetworkConfig: networkConfig,
}
}
diff --git a/pkg/specgen/storage.go b/pkg/specgen/storage.go
new file mode 100644
index 000000000..1b903f608
--- /dev/null
+++ b/pkg/specgen/storage.go
@@ -0,0 +1,885 @@
+package specgen
+
+//nolint
+
+import (
+ "fmt"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/libpod/pkg/util"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ // TypeBind is the type for mounting host dir
+ TypeBind = "bind"
+ // TypeVolume is the type for named volumes
+ TypeVolume = "volume"
+ // TypeTmpfs is the type for mounting tmpfs
+ TypeTmpfs = "tmpfs"
+)
+
+var (
+ errDuplicateDest = errors.Errorf("duplicate mount destination") //nolint
+ optionArgError = errors.Errorf("must provide an argument for option") //nolint
+ noDestError = errors.Errorf("must set volume destination") //nolint
+)
+
+// Parse all volume-related options in the create config into a set of mounts
+// and named volumes to add to the container.
+// Handles --volumes-from, --volumes, --tmpfs, --init, and --init-path flags.
+// TODO: Named volume options - should we default to rprivate? It bakes into a
+// bind mount under the hood...
+// TODO: handle options parsing/processing via containers/storage/pkg/mount
+func (s *SpecGenerator) parseVolumes(mounts, volMounts, tmpMounts []string) error { //nolint
+
+ // TODO this needs to come from the image and erquires a runtime
+
+ // Add image volumes.
+ //baseMounts, baseVolumes, err := config.getImageVolumes()
+ //if err != nil {
+ // return nil, nil, err
+ //}
+
+ // Add --volumes-from.
+ // Overrides image volumes unconditionally.
+ //vFromMounts, vFromVolumes, err := config.getVolumesFrom(runtime)
+ //if err != nil {
+ // return nil, nil, err
+ //}
+ //for dest, mount := range vFromMounts {
+ // baseMounts[dest] = mount
+ //}
+ //for dest, volume := range vFromVolumes {
+ // baseVolumes[dest] = volume
+ //}
+
+ // Next mounts from the --mounts flag.
+ // Do not override yet.
+ //unifiedMounts, _, err := getMounts(mounts)
+ //if err != nil {
+ // return err
+ //}
+ //
+ //// Next --volumes flag.
+ //// Do not override yet.
+ //volumeMounts, _ , err := getVolumeMounts(volMounts)
+ //if err != nil {
+ // return err
+ //}
+ //
+ //// Next --tmpfs flag.
+ //// Do not override yet.
+ //tmpfsMounts, err := getTmpfsMounts(tmpMounts)
+ //if err != nil {
+ // return err
+ //}
+
+ //// Unify mounts from --mount, --volume, --tmpfs.
+ //// Also add mounts + volumes directly from createconfig.
+ //// Start with --volume.
+ //for dest, mount := range volumeMounts {
+ // if _, ok := unifiedMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedMounts[dest] = mount
+ //}
+ //for dest, volume := range volumeVolumes {
+ // if _, ok := unifiedVolumes[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedVolumes[dest] = volume
+ //}
+ //// Now --tmpfs
+ //for dest, tmpfs := range tmpfsMounts {
+ // if _, ok := unifiedMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedMounts[dest] = tmpfs
+ //}
+ //// Now spec mounts and volumes
+ //for _, mount := range config.Mounts {
+ // dest := mount.Destination
+ // if _, ok := unifiedMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedMounts[dest] = mount
+ //}
+ //for _, volume := range config.NamedVolumes {
+ // dest := volume.Dest
+ // if _, ok := unifiedVolumes[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ // }
+ // unifiedVolumes[dest] = volume
+ //}
+ //
+ //// If requested, add container init binary
+ //if config.Init {
+ // initPath := config.InitPath
+ // if initPath == "" {
+ // rtc, err := runtime.GetConfig()
+ // if err != nil {
+ // return nil, nil, err
+ // }
+ // initPath = rtc.Engine.InitPath
+ // }
+ // initMount, err := config.addContainerInitBinary(initPath)
+ // if err != nil {
+ // return nil, nil, err
+ // }
+ // if _, ok := unifiedMounts[initMount.Destination]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
+ // }
+ // unifiedMounts[initMount.Destination] = initMount
+ //}
+ //
+ //// Before superseding, we need to find volume mounts which conflict with
+ //// named volumes, and vice versa.
+ //// We'll delete the conflicts here as we supersede.
+ //for dest := range unifiedMounts {
+ // if _, ok := baseVolumes[dest]; ok {
+ // delete(baseVolumes, dest)
+ // }
+ //}
+ //for dest := range unifiedVolumes {
+ // if _, ok := baseMounts[dest]; ok {
+ // delete(baseMounts, dest)
+ // }
+ //}
+ //
+ //// Supersede volumes-from/image volumes with unified volumes from above.
+ //// This is an unconditional replacement.
+ //for dest, mount := range unifiedMounts {
+ // baseMounts[dest] = mount
+ //}
+ //for dest, volume := range unifiedVolumes {
+ // baseVolumes[dest] = volume
+ //}
+ //
+ //// If requested, add tmpfs filesystems for read-only containers.
+ //if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
+ // readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
+ // options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
+ // for _, dest := range readonlyTmpfs {
+ // if _, ok := baseMounts[dest]; ok {
+ // continue
+ // }
+ // if _, ok := baseVolumes[dest]; ok {
+ // continue
+ // }
+ // localOpts := options
+ // if dest == "/run" {
+ // localOpts = append(localOpts, "noexec", "size=65536k")
+ // } else {
+ // localOpts = append(localOpts, "exec")
+ // }
+ // baseMounts[dest] = spec.Mount{
+ // Destination: dest,
+ // Type: "tmpfs",
+ // Source: "tmpfs",
+ // Options: localOpts,
+ // }
+ // }
+ //}
+ //
+ //// Check for conflicts between named volumes and mounts
+ //for dest := range baseMounts {
+ // if _, ok := baseVolumes[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ // }
+ //}
+ //for dest := range baseVolumes {
+ // if _, ok := baseMounts[dest]; ok {
+ // return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ // }
+ //}
+ //
+ //// Final step: maps to arrays
+ //finalMounts := make([]spec.Mount, 0, len(baseMounts))
+ //for _, mount := range baseMounts {
+ // if mount.Type == TypeBind {
+ // absSrc, err := filepath.Abs(mount.Source)
+ // if err != nil {
+ // return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
+ // }
+ // mount.Source = absSrc
+ // }
+ // finalMounts = append(finalMounts, mount)
+ //}
+ //finalVolumes := make([]*define.ContainerNamedVolume, 0, len(baseVolumes))
+ //for _, volume := range baseVolumes {
+ // finalVolumes = append(finalVolumes, volume)
+ //}
+
+ //return finalMounts, finalVolumes, nil
+ return nil
+}
+
+// Parse volumes from - a set of containers whose volumes we will mount in.
+// Grab the containers, retrieve any user-created spec mounts and all named
+// volumes, and return a list of them.
+// Conflicts are resolved simply - the last container specified wins.
+// Container names may be suffixed by mount options after a colon.
+// TODO: We should clean these paths if possible
+// TODO deferred baude
+func getVolumesFrom() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ // Both of these are maps of mount destination to mount type.
+ // We ensure that each destination is only mounted to once in this way.
+ //finalMounts := make(map[string]spec.Mount)
+ //finalNamedVolumes := make(map[string]*define.ContainerNamedVolume)
+ //
+ //for _, vol := range config.VolumesFrom {
+ // var (
+ // options = []string{}
+ // err error
+ // splitVol = strings.SplitN(vol, ":", 2)
+ // )
+ // if len(splitVol) == 2 {
+ // splitOpts := strings.Split(splitVol[1], ",")
+ // for _, checkOpt := range splitOpts {
+ // switch checkOpt {
+ // case "z", "ro", "rw":
+ // // Do nothing, these are valid options
+ // default:
+ // return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1])
+ // }
+ // }
+ //
+ // if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil {
+ // return nil, nil, err
+ // }
+ // }
+ // ctr, err := runtime.LookupContainer(splitVol[0])
+ // if err != nil {
+ // return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0])
+ // }
+ //
+ // logrus.Debugf("Adding volumes from container %s", ctr.ID())
+ //
+ // // Look up the container's user volumes. This gets us the
+ // // destinations of all mounts the user added to the container.
+ // userVolumesArr := ctr.UserVolumes()
+ //
+ // // We're going to need to access them a lot, so convert to a map
+ // // to reduce looping.
+ // // We'll also use the map to indicate if we missed any volumes along the way.
+ // userVolumes := make(map[string]bool)
+ // for _, dest := range userVolumesArr {
+ // userVolumes[dest] = false
+ // }
+ //
+ // // Now we get the container's spec and loop through its volumes
+ // // and append them in if we can find them.
+ // spec := ctr.Spec()
+ // if spec == nil {
+ // return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID())
+ // }
+ // for _, mnt := range spec.Mounts {
+ // if mnt.Type != TypeBind {
+ // continue
+ // }
+ // if _, exists := userVolumes[mnt.Destination]; exists {
+ // userVolumes[mnt.Destination] = true
+ //
+ // if len(options) != 0 {
+ // mnt.Options = options
+ // }
+ //
+ // if _, ok := finalMounts[mnt.Destination]; ok {
+ // logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID())
+ // }
+ // finalMounts[mnt.Destination] = mnt
+ // }
+ // }
+ //
+ // // We're done with the spec mounts. Add named volumes.
+ // // Add these unconditionally - none of them are automatically
+ // // part of the container, as some spec mounts are.
+ // namedVolumes := ctr.NamedVolumes()
+ // for _, namedVol := range namedVolumes {
+ // if _, exists := userVolumes[namedVol.Dest]; exists {
+ // userVolumes[namedVol.Dest] = true
+ // }
+ //
+ // if len(options) != 0 {
+ // namedVol.Options = options
+ // }
+ //
+ // if _, ok := finalMounts[namedVol.Dest]; ok {
+ // logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID())
+ // }
+ // finalNamedVolumes[namedVol.Dest] = namedVol
+ // }
+ //
+ // // Check if we missed any volumes
+ // for volDest, found := range userVolumes {
+ // if !found {
+ // logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
+ // }
+ // }
+ //}
+ //
+ //return finalMounts, finalNamedVolumes, nil
+ return nil, nil, nil
+}
+
+// getMounts takes user-provided input from the --mount flag and creates OCI
+// spec mounts and Libpod named volumes.
+// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
+// podman run --mount type=tmpfs,target=/dev/shm ...
+// podman run --mount type=volume,source=test-volume, ...
+func getMounts(mounts []string) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ finalMounts := make(map[string]spec.Mount)
+ finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
+
+ errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
+
+ // TODO(vrothberg): the manual parsing can be replaced with a regular expression
+ // to allow a more robust parsing of the mount format and to give
+ // precise errors regarding supported format versus supported options.
+ for _, mount := range mounts {
+ arr := strings.SplitN(mount, ",", 2)
+ if len(arr) < 2 {
+ return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
+ }
+ kv := strings.Split(arr[0], "=")
+ // TODO: type is not explicitly required in Docker.
+ // If not specified, it defaults to "volume".
+ if len(kv) != 2 || kv[0] != "type" {
+ return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
+ }
+
+ tokens := strings.Split(arr[1], ",")
+ switch kv[1] {
+ case TypeBind:
+ mount, err := getBindMount(tokens)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := finalMounts[mount.Destination]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
+ }
+ finalMounts[mount.Destination] = mount
+ case TypeTmpfs:
+ mount, err := getTmpfsMount(tokens)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := finalMounts[mount.Destination]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
+ }
+ finalMounts[mount.Destination] = mount
+ case "volume":
+ volume, err := getNamedVolume(tokens)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := finalNamedVolumes[volume.Dest]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest)
+ }
+ finalNamedVolumes[volume.Dest] = volume
+ default:
+ return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1])
+ }
+ }
+
+ return finalMounts, finalNamedVolumes, nil
+}
+
+// Parse a single bind mount entry from the --mount flag.
+func getBindMount(args []string) (spec.Mount, error) { //nolint
+ newMount := spec.Mount{
+ Type: TypeBind,
+ }
+
+ var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ switch kv[0] {
+ case "bind-nonrecursive":
+ newMount.Options = append(newMount.Options, "bind")
+ case "ro", "rw":
+ if setRORW {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once")
+ }
+ setRORW = true
+ // Can be formatted as one of:
+ // ro
+ // ro=[true|false]
+ // rw
+ // rw=[true|false]
+ switch len(kv) {
+ case 1:
+ newMount.Options = append(newMount.Options, kv[0])
+ case 2:
+ switch strings.ToLower(kv[1]) {
+ case "true":
+ newMount.Options = append(newMount.Options, kv[0])
+ case "false":
+ // Set the opposite only for rw
+ // ro's opposite is the default
+ if kv[0] == "rw" {
+ newMount.Options = append(newMount.Options, "ro")
+ }
+ default:
+ return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1])
+ }
+ default:
+ return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val)
+ }
+ case "nosuid", "suid":
+ if setSuid {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
+ newMount.Options = append(newMount.Options, kv[0])
+ case "bind-propagation":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ newMount.Options = append(newMount.Options, kv[1])
+ case "src", "source":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeHostDir(kv[1]); err != nil {
+ return newMount, err
+ }
+ newMount.Source = kv[1]
+ setSource = true
+ case "target", "dst", "destination":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
+ return newMount, err
+ }
+ newMount.Destination = filepath.Clean(kv[1])
+ setDest = true
+ case "relabel":
+ if setRelabel {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once")
+ }
+ setRelabel = true
+ if len(kv) != 2 {
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
+ }
+ switch kv[1] {
+ case "private":
+ newMount.Options = append(newMount.Options, "z")
+ case "shared":
+ newMount.Options = append(newMount.Options, "Z")
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
+ }
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ }
+ }
+
+ if !setDest {
+ return newMount, noDestError
+ }
+
+ if !setSource {
+ newMount.Source = newMount.Destination
+ }
+
+ options, err := parse.ValidateVolumeOpts(newMount.Options)
+ if err != nil {
+ return newMount, err
+ }
+ newMount.Options = options
+ return newMount, nil
+}
+
+// Parse a single tmpfs mount entry from the --mount flag
+func getTmpfsMount(args []string) (spec.Mount, error) { //nolint
+ newMount := spec.Mount{
+ Type: TypeTmpfs,
+ Source: TypeTmpfs,
+ }
+
+ var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ switch kv[0] {
+ case "tmpcopyup", "notmpcopyup":
+ if setTmpcopyup {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once")
+ }
+ setTmpcopyup = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "ro", "rw":
+ if setRORW {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
+ }
+ setRORW = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nosuid", "suid":
+ if setSuid {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "tmpfs-mode":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1]))
+ case "tmpfs-size":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1]))
+ case "src", "source":
+ return newMount, errors.Errorf("source is not supported with tmpfs mounts")
+ case "target", "dst", "destination":
+ if len(kv) == 1 {
+ return newMount, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
+ return newMount, err
+ }
+ newMount.Destination = filepath.Clean(kv[1])
+ setDest = true
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ }
+ }
+
+ if !setDest {
+ return newMount, noDestError
+ }
+
+ return newMount, nil
+}
+
+// Parse a single volume mount entry from the --mount flag.
+// Note that the volume-label option for named volumes is currently NOT supported.
+// TODO: add support for --volume-label
+func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { //nolint
+ newVolume := new(libpod.ContainerNamedVolume)
+
+ var setSource, setDest, setRORW, setSuid, setDev, setExec bool
+
+ for _, val := range args {
+ kv := strings.Split(val, "=")
+ switch kv[0] {
+ case "ro", "rw":
+ if setRORW {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
+ }
+ setRORW = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "nosuid", "suid":
+ if setSuid {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "volume-label":
+ return nil, errors.Errorf("the --volume-label option is not presently implemented")
+ case "src", "source":
+ if len(kv) == 1 {
+ return nil, errors.Wrapf(optionArgError, kv[0])
+ }
+ newVolume.Name = kv[1]
+ setSource = true
+ case "target", "dst", "destination":
+ if len(kv) == 1 {
+ return nil, errors.Wrapf(optionArgError, kv[0])
+ }
+ if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
+ return nil, err
+ }
+ newVolume.Dest = filepath.Clean(kv[1])
+ setDest = true
+ default:
+ return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ }
+ }
+
+ if !setSource {
+ return nil, errors.Errorf("must set source volume")
+ }
+ if !setDest {
+ return nil, noDestError
+ }
+
+ return newVolume, nil
+}
+
+func getVolumeMounts(vols []string) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ mounts := make(map[string]spec.Mount)
+ volumes := make(map[string]*libpod.ContainerNamedVolume)
+
+ volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
+
+ for _, vol := range vols {
+ var (
+ options []string
+ src string
+ dest string
+ err error
+ )
+
+ splitVol := strings.Split(vol, ":")
+ if len(splitVol) > 3 {
+ return nil, nil, errors.Wrapf(volumeFormatErr, vol)
+ }
+
+ src = splitVol[0]
+ if len(splitVol) == 1 {
+ // This is an anonymous named volume. Only thing given
+ // is destination.
+ // Name/source will be blank, and populated by libpod.
+ src = ""
+ dest = splitVol[0]
+ } else if len(splitVol) > 1 {
+ dest = splitVol[1]
+ }
+ if len(splitVol) > 2 {
+ if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
+ return nil, nil, err
+ }
+ }
+
+ // Do not check source dir for anonymous volumes
+ if len(splitVol) > 1 {
+ if err := parse.ValidateVolumeHostDir(src); err != nil {
+ return nil, nil, err
+ }
+ }
+ if err := parse.ValidateVolumeCtrDir(dest); err != nil {
+ return nil, nil, err
+ }
+
+ cleanDest := filepath.Clean(dest)
+
+ if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
+ // This is not a named volume
+ newMount := spec.Mount{
+ Destination: cleanDest,
+ Type: string(TypeBind),
+ Source: src,
+ Options: options,
+ }
+ if _, ok := mounts[newMount.Destination]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
+ }
+ mounts[newMount.Destination] = newMount
+ } else {
+ // This is a named volume
+ newNamedVol := new(libpod.ContainerNamedVolume)
+ newNamedVol.Name = src
+ newNamedVol.Dest = cleanDest
+ newNamedVol.Options = options
+
+ if _, ok := volumes[newNamedVol.Dest]; ok {
+ return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
+ }
+ volumes[newNamedVol.Dest] = newNamedVol
+ }
+
+ logrus.Debugf("User mount %s:%s options %v", src, dest, options)
+ }
+
+ return mounts, volumes, nil
+}
+
+// Get mounts for container's image volumes
+// TODO deferred baude
+func getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { //nolint
+ //mounts := make(map[string]spec.Mount)
+ //volumes := make(map[string]*define.ContainerNamedVolume)
+ //
+ //if config.ImageVolumeType == "ignore" {
+ // return mounts, volumes, nil
+ //}
+ //
+ //for vol := range config.BuiltinImgVolumes {
+ // cleanDest := filepath.Clean(vol)
+ // logrus.Debugf("Adding image volume at %s", cleanDest)
+ // if config.ImageVolumeType == "tmpfs" {
+ // // Tmpfs image volumes are handled as mounts
+ // mount := spec.Mount{
+ // Destination: cleanDest,
+ // Source: TypeTmpfs,
+ // Type: TypeTmpfs,
+ // Options: []string{"rprivate", "rw", "nodev", "exec"},
+ // }
+ // mounts[cleanDest] = mount
+ // } else {
+ // // Anonymous volumes have no name.
+ // namedVolume := new(define.ContainerNamedVolume)
+ // namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
+ // namedVolume.Dest = cleanDest
+ // volumes[cleanDest] = namedVolume
+ // }
+ //}
+ //
+ //return mounts, volumes, nil
+ return nil, nil, nil
+}
+
+// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
+func getTmpfsMounts(mounts []string) (map[string]spec.Mount, error) { //nolint
+ m := make(map[string]spec.Mount)
+ for _, i := range mounts {
+ // Default options if nothing passed
+ var options []string
+ spliti := strings.Split(i, ":")
+ destPath := spliti[0]
+ if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil {
+ return nil, err
+ }
+ if len(spliti) > 1 {
+ options = strings.Split(spliti[1], ",")
+ }
+
+ if _, ok := m[destPath]; ok {
+ return nil, errors.Wrapf(errDuplicateDest, destPath)
+ }
+
+ mount := spec.Mount{
+ Destination: filepath.Clean(destPath),
+ Type: string(TypeTmpfs),
+ Options: options,
+ Source: string(TypeTmpfs),
+ }
+ m[destPath] = mount
+ }
+ return m, nil
+}
+
+// AddContainerInitBinary adds the init binary specified by path iff the
+// container will run in a private PID namespace that is not shared with the
+// host or another pre-existing container, where an init-like process is
+// already running.
+//
+// Note that AddContainerInitBinary prepends "/dev/init" "--" to the command
+// to execute the bind-mounted binary as PID 1.
+// TODO this needs to be worked on to work in new env
+func addContainerInitBinary(path string) (spec.Mount, error) { //nolint
+ mount := spec.Mount{
+ Destination: "/dev/init",
+ Type: TypeBind,
+ Source: path,
+ Options: []string{TypeBind, "ro"},
+ }
+
+ //if path == "" {
+ // return mount, fmt.Errorf("please specify a path to the container-init binary")
+ //}
+ //if !config.Pid.PidMode.IsPrivate() {
+ // return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
+ //}
+ //if config.Systemd {
+ // return mount, fmt.Errorf("cannot use container-init binary with systemd")
+ //}
+ //if _, err := os.Stat(path); os.IsNotExist(err) {
+ // return mount, errors.Wrap(err, "container-init binary not found on the host")
+ //}
+ //config.Command = append([]string{"/dev/init", "--"}, config.Command...)
+ return mount, nil
+}
+
+// Supersede existing mounts in the spec with new, user-specified mounts.
+// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
+// one mount, and we already have /tmp/a and /tmp/b, should we remove
+// the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
+func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
+ if len(mounts) > 0 {
+ // If we have overlappings mounts, remove them from the spec in favor of
+ // the user-added volume mounts
+ destinations := make(map[string]bool)
+ for _, mount := range mounts {
+ destinations[path.Clean(mount.Destination)] = true
+ }
+ // Copy all mounts from spec to defaultMounts, except for
+ // - mounts overridden by a user supplied mount;
+ // - all mounts under /dev if a user supplied /dev is present;
+ mountDev := destinations["/dev"]
+ for _, mount := range configMount {
+ if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
+ if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
+ // filter out everything under /dev if /dev is user-mounted
+ continue
+ }
+
+ logrus.Debugf("Adding mount %s", mount.Destination)
+ mounts = append(mounts, mount)
+ }
+ }
+ return mounts
+ }
+ return configMount
+}
+
+func InitFSMounts(mounts []spec.Mount) error {
+ for i, m := range mounts {
+ switch {
+ case m.Type == TypeBind:
+ opts, err := util.ProcessOptions(m.Options, false, m.Source)
+ if err != nil {
+ return err
+ }
+ mounts[i].Options = opts
+ case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
+ opts, err := util.ProcessOptions(m.Options, true, "")
+ if err != nil {
+ return err
+ }
+ mounts[i].Options = opts
+ }
+ }
+ return nil
+}
diff --git a/pkg/systemd/generate/systemdgen.go b/pkg/systemd/generate/systemdgen.go
index eb15d4927..73fe52c0e 100644
--- a/pkg/systemd/generate/systemdgen.go
+++ b/pkg/systemd/generate/systemdgen.go
@@ -31,7 +31,7 @@ type ContainerInfo struct {
InfraContainer string
// StopTimeout sets the timeout Podman waits before killing the container
// during service stop.
- StopTimeout int
+ StopTimeout uint
// RestartPolicy of the systemd unit (e.g., no, on-failure, always).
RestartPolicy string
// PIDFile of the service. Required for forking services. Must point to the
diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go
index d21800bc3..329a7c913 100644
--- a/pkg/util/mountOpts.go
+++ b/pkg/util/mountOpts.go
@@ -13,19 +13,17 @@ var (
ErrDupeMntOption = errors.Errorf("duplicate mount option passed")
)
-// DefaultMountOptions sets default mount options for ProcessOptions.
-type DefaultMountOptions struct {
- Noexec bool
- Nosuid bool
- Nodev bool
+type defaultMountOptions struct {
+ noexec bool
+ nosuid bool
+ nodev bool
}
// ProcessOptions parses the options for a bind or tmpfs mount and ensures that
// they are sensible and follow convention. The isTmpfs variable controls
// whether extra, tmpfs-specific options will be allowed.
-// The defaults variable controls default mount options that will be set. If it
-// is not included, they will be set unconditionally.
-func ProcessOptions(options []string, isTmpfs bool, defaults *DefaultMountOptions) ([]string, error) {
+// The sourcePath variable, if not empty, contains a bind mount source.
+func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
var (
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ bool
)
@@ -122,13 +120,17 @@ func ProcessOptions(options []string, isTmpfs bool, defaults *DefaultMountOption
if !foundProp {
newOptions = append(newOptions, "rprivate")
}
- if !foundExec && (defaults == nil || defaults.Noexec) {
+ defaults, err := getDefaultMountOptions(sourcePath)
+ if err != nil {
+ return nil, err
+ }
+ if !foundExec && defaults.noexec {
newOptions = append(newOptions, "noexec")
}
- if !foundSuid && (defaults == nil || defaults.Nosuid) {
+ if !foundSuid && defaults.nosuid {
newOptions = append(newOptions, "nosuid")
}
- if !foundDev && (defaults == nil || defaults.Nodev) {
+ if !foundDev && defaults.nodev {
newOptions = append(newOptions, "nodev")
}
if isTmpfs && !foundCopyUp {
diff --git a/pkg/util/mountOpts_linux.go b/pkg/util/mountOpts_linux.go
new file mode 100644
index 000000000..3eac4dd25
--- /dev/null
+++ b/pkg/util/mountOpts_linux.go
@@ -0,0 +1,23 @@
+package util
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+func getDefaultMountOptions(path string) (defaultMountOptions, error) {
+ opts := defaultMountOptions{true, true, true}
+ if path == "" {
+ return opts, nil
+ }
+ var statfs unix.Statfs_t
+ if e := unix.Statfs(path, &statfs); e != nil {
+ return opts, &os.PathError{Op: "statfs", Path: path, Err: e}
+ }
+ opts.nodev = (statfs.Flags&unix.MS_NODEV == unix.MS_NODEV)
+ opts.noexec = (statfs.Flags&unix.MS_NOEXEC == unix.MS_NOEXEC)
+ opts.nosuid = (statfs.Flags&unix.MS_NOSUID == unix.MS_NOSUID)
+
+ return opts, nil
+}
diff --git a/pkg/util/mountOpts_other.go b/pkg/util/mountOpts_other.go
new file mode 100644
index 000000000..6a34942e5
--- /dev/null
+++ b/pkg/util/mountOpts_other.go
@@ -0,0 +1,7 @@
+// +build !linux
+
+package util
+
+func getDefaultMountOptions(path string) (opts defaultMountOptions, err error) {
+ return
+}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 3e11c010a..0c055745d 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -609,3 +609,46 @@ func Tmpdir() string {
return tmpdir
}
+
+// ValidateSysctls validates a list of sysctl and returns it.
+func ValidateSysctls(strSlice []string) (map[string]string, error) {
+ sysctl := make(map[string]string)
+ validSysctlMap := map[string]bool{
+ "kernel.msgmax": true,
+ "kernel.msgmnb": true,
+ "kernel.msgmni": true,
+ "kernel.sem": true,
+ "kernel.shmall": true,
+ "kernel.shmmax": true,
+ "kernel.shmmni": true,
+ "kernel.shm_rmid_forced": true,
+ }
+ validSysctlPrefixes := []string{
+ "net.",
+ "fs.mqueue.",
+ }
+
+ for _, val := range strSlice {
+ foundMatch := false
+ arr := strings.Split(val, "=")
+ if len(arr) < 2 {
+ return nil, errors.Errorf("%s is invalid, sysctl values must be in the form of KEY=VALUE", val)
+ }
+ if validSysctlMap[arr[0]] {
+ sysctl[arr[0]] = arr[1]
+ continue
+ }
+
+ for _, prefix := range validSysctlPrefixes {
+ if strings.HasPrefix(arr[0], prefix) {
+ sysctl[arr[0]] = arr[1]
+ foundMatch = true
+ break
+ }
+ }
+ if !foundMatch {
+ return nil, errors.Errorf("sysctl '%s' is not whitelisted", arr[0])
+ }
+ }
+ return sysctl, nil
+}
diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go
index 0995d1e20..a9b37844e 100644
--- a/pkg/util/utils_test.go
+++ b/pkg/util/utils_test.go
@@ -245,3 +245,15 @@ func TestGetImageConfigMisc(t *testing.T) {
_, err = GetImageConfig([]string{"BADINST testvalue"})
assert.NotNil(t, err)
}
+
+func TestValidateSysctls(t *testing.T) {
+ strSlice := []string{"net.core.test1=4", "kernel.msgmax=2"}
+ result, _ := ValidateSysctls(strSlice)
+ assert.Equal(t, result["net.core.test1"], "4")
+}
+
+func TestValidateSysctlBadSysctl(t *testing.T) {
+ strSlice := []string{"BLAU=BLUE", "GELB^YELLOW"}
+ _, err := ValidateSysctls(strSlice)
+ assert.Error(t, err)
+}
diff --git a/pkg/varlink/generate.go b/pkg/varlink/generate.go
new file mode 100644
index 000000000..b3f58d4a5
--- /dev/null
+++ b/pkg/varlink/generate.go
@@ -0,0 +1,3 @@
+package iopodman
+
+//go:generate go run ../../vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go io.podman.varlink
diff --git a/pkg/varlink/io.podman.varlink b/pkg/varlink/io.podman.varlink
new file mode 100644
index 000000000..0cb95ef97
--- /dev/null
+++ b/pkg/varlink/io.podman.varlink
@@ -0,0 +1,1398 @@
+# Podman Service Interface and API description. The master version of this document can be found
+# in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository.
+interface io.podman
+
+type Volume (
+ name: string,
+ labels: [string]string,
+ mountPoint: string,
+ driver: string,
+ options: [string]string
+)
+
+type NotImplemented (
+ comment: string
+)
+
+type StringResponse (
+ message: string
+)
+
+type RemoveImageResponse (
+ untagged: []string,
+ deleted: string
+)
+
+type LogLine (
+ device: string,
+ parseLogType : string,
+ time: string,
+ msg: string,
+ cid: string
+)
+
+# ContainerChanges describes the return struct for ListContainerChanges
+type ContainerChanges (
+ changed: []string,
+ added: []string,
+ deleted: []string
+)
+
+type ImageSaveOptions (
+ name: string,
+ format: string,
+ output: string,
+ outputType: string,
+ moreTags: []string,
+ quiet: bool,
+ compress: bool
+)
+
+type VolumeCreateOpts (
+ volumeName: string,
+ driver: string,
+ labels: [string]string,
+ options: [string]string
+)
+
+type VolumeRemoveOpts (
+ volumes: []string,
+ all: bool,
+ force: bool
+)
+
+type Image (
+ id: string,
+ digest: string,
+ digests: []string,
+ parentId: string,
+ repoTags: []string,
+ repoDigests: []string,
+ created: string, # as RFC3339
+ size: int,
+ virtualSize: int,
+ containers: int,
+ labels: [string]string,
+ isParent: bool,
+ topLayer: string,
+ readOnly: bool,
+ history: []string
+)
+
+# ImageHistory describes the returned structure from ImageHistory.
+type ImageHistory (
+ id: string,
+ created: string, # as RFC3339
+ createdBy: string,
+ tags: []string,
+ size: int,
+ comment: string
+)
+
+# Represents a single search result from SearchImages
+type ImageSearchResult (
+ description: string,
+ is_official: bool,
+ is_automated: bool,
+ registry: string,
+ name: string,
+ star_count: int
+)
+
+type ImageSearchFilter (
+ is_official: ?bool,
+ is_automated: ?bool,
+ star_count: int
+)
+
+type AuthConfig (
+ username: string,
+ password: string
+)
+
+type KubePodService (
+ pod: string,
+ service: string
+)
+
+type Container (
+ id: string,
+ image: string,
+ imageid: string,
+ command: []string,
+ createdat: string, # as RFC3339
+ runningfor: string,
+ status: string,
+ ports: []ContainerPortMappings,
+ rootfssize: int,
+ rwsize: int,
+ names: string,
+ labels: [string]string,
+ mounts: []ContainerMount,
+ containerrunning: bool,
+ namespaces: ContainerNameSpace
+)
+
+# ContainerStats is the return struct for the stats of a container
+type ContainerStats (
+ id: string,
+ name: string,
+ cpu: float,
+ cpu_nano: int,
+ system_nano: int,
+ mem_usage: int,
+ mem_limit: int,
+ mem_perc: float,
+ net_input: int,
+ net_output: int,
+ block_output: int,
+ block_input: int,
+ pids: int
+)
+
+type PsOpts (
+ all: bool,
+ filters: ?[]string,
+ last: ?int,
+ latest: ?bool,
+ noTrunc: ?bool,
+ pod: ?bool,
+ quiet: ?bool,
+ size: ?bool,
+ sort: ?string,
+ sync: ?bool
+)
+
+type PsContainer (
+ id: string,
+ image: string,
+ command: string,
+ created: string,
+ ports: string,
+ names: string,
+ isInfra: bool,
+ status: string,
+ state: string,
+ pidNum: int,
+ rootFsSize: int,
+ rwSize: int,
+ pod: string,
+ createdAt: string,
+ exitedAt: string,
+ startedAt: string,
+ labels: [string]string,
+ nsPid: string,
+ cgroup: string,
+ ipc: string,
+ mnt: string,
+ net: string,
+ pidNs: string,
+ user: string,
+ uts: string,
+ mounts: string
+)
+
+# ContainerMount describes the struct for mounts in a container
+type ContainerMount (
+ destination: string,
+ type: string,
+ source: string,
+ options: []string
+)
+
+# ContainerPortMappings describes the struct for portmappings in an existing container
+type ContainerPortMappings (
+ host_port: string,
+ host_ip: string,
+ protocol: string,
+ container_port: string
+)
+
+# ContainerNamespace describes the namespace structure for an existing container
+type ContainerNameSpace (
+ user: string,
+ uts: string,
+ pidns: string,
+ pid: string,
+ cgroup: string,
+ net: string,
+ mnt: string,
+ ipc: string
+)
+
+# InfoDistribution describes the host's distribution
+type InfoDistribution (
+ distribution: string,
+ version: string
+)
+
+# InfoHost describes the host stats portion of PodmanInfo
+type InfoHost (
+ buildah_version: string,
+ distribution: InfoDistribution,
+ mem_free: int,
+ mem_total: int,
+ swap_free: int,
+ swap_total: int,
+ arch: string,
+ cpus: int,
+ hostname: string,
+ kernel: string,
+ os: string,
+ uptime: string,
+ eventlogger: string
+)
+
+# InfoGraphStatus describes the detailed status of the storage driver
+type InfoGraphStatus (
+ backing_filesystem: string,
+ native_overlay_diff: string,
+ supports_d_type: string
+)
+
+# InfoRegistry describes the host's registry information
+type InfoRegistry (
+ search: []string,
+ insecure: []string,
+ blocked: []string
+)
+
+# InfoStore describes the host's storage informatoin
+type InfoStore (
+ containers: int,
+ images: int,
+ graph_driver_name: string,
+ graph_driver_options: string,
+ graph_root: string,
+ graph_status: InfoGraphStatus,
+ run_root: string
+)
+
+# InfoPodman provides details on the Podman binary
+type InfoPodmanBinary (
+ compiler: string,
+ go_version: string,
+ podman_version: string,
+ git_commit: string
+)
+
+# PodmanInfo describes the Podman host and build
+type PodmanInfo (
+ host: InfoHost,
+ registries: InfoRegistry,
+ store: InfoStore,
+ podman: InfoPodmanBinary
+)
+
+# Sockets describes sockets location for a container
+type Sockets(
+ container_id: string,
+ io_socket: string,
+ control_socket: string
+)
+
+# Create is an input structure for creating containers.
+# args[0] is the image name or id
+# args[1-] are the new commands if changed
+type Create (
+ args: []string,
+ addHost: ?[]string,
+ annotation: ?[]string,
+ attach: ?[]string,
+ blkioWeight: ?string,
+ blkioWeightDevice: ?[]string,
+ capAdd: ?[]string,
+ capDrop: ?[]string,
+ cgroupParent: ?string,
+ cidFile: ?string,
+ conmonPidfile: ?string,
+ command: ?[]string,
+ cpuPeriod: ?int,
+ cpuQuota: ?int,
+ cpuRtPeriod: ?int,
+ cpuRtRuntime: ?int,
+ cpuShares: ?int,
+ cpus: ?float,
+ cpuSetCpus: ?string,
+ cpuSetMems: ?string,
+ detach: ?bool,
+ detachKeys: ?string,
+ device: ?[]string,
+ deviceReadBps: ?[]string,
+ deviceReadIops: ?[]string,
+ deviceWriteBps: ?[]string,
+ deviceWriteIops: ?[]string,
+ dns: ?[]string,
+ dnsOpt: ?[]string,
+ dnsSearch: ?[]string,
+ dnsServers: ?[]string,
+ entrypoint: ?string,
+ env: ?[]string,
+ envFile: ?[]string,
+ expose: ?[]string,
+ gidmap: ?[]string,
+ groupadd: ?[]string,
+ healthcheckCommand: ?string,
+ healthcheckInterval: ?string,
+ healthcheckRetries: ?int,
+ healthcheckStartPeriod: ?string,
+ healthcheckTimeout:?string,
+ hostname: ?string,
+ imageVolume: ?string,
+ init: ?bool,
+ initPath: ?string,
+ interactive: ?bool,
+ ip: ?string,
+ ipc: ?string,
+ kernelMemory: ?string,
+ label: ?[]string,
+ labelFile: ?[]string,
+ logDriver: ?string,
+ logOpt: ?[]string,
+ macAddress: ?string,
+ memory: ?string,
+ memoryReservation: ?string,
+ memorySwap: ?string,
+ memorySwappiness: ?int,
+ name: ?string,
+ network: ?string,
+ noHosts: ?bool,
+ oomKillDisable: ?bool,
+ oomScoreAdj: ?int,
+ overrideArch: ?string,
+ overrideOS: ?string,
+ pid: ?string,
+ pidsLimit: ?int,
+ pod: ?string,
+ privileged: ?bool,
+ publish: ?[]string,
+ publishAll: ?bool,
+ pull: ?string,
+ quiet: ?bool,
+ readonly: ?bool,
+ readonlytmpfs: ?bool,
+ restart: ?string,
+ rm: ?bool,
+ rootfs: ?bool,
+ securityOpt: ?[]string,
+ shmSize: ?string,
+ stopSignal: ?string,
+ stopTimeout: ?int,
+ storageOpt: ?[]string,
+ subuidname: ?string,
+ subgidname: ?string,
+ sysctl: ?[]string,
+ systemd: ?string,
+ tmpfs: ?[]string,
+ tty: ?bool,
+ uidmap: ?[]string,
+ ulimit: ?[]string,
+ user: ?string,
+ userns: ?string,
+ uts: ?string,
+ mount: ?[]string,
+ volume: ?[]string,
+ volumesFrom: ?[]string,
+ workDir: ?string
+)
+
+# BuildOptions are are used to describe describe physical attributes of the build
+type BuildOptions (
+ addHosts: []string,
+ cgroupParent: string,
+ cpuPeriod: int,
+ cpuQuota: int,
+ cpuShares: int,
+ cpusetCpus: string,
+ cpusetMems: string,
+ memory: int,
+ memorySwap: int,
+ shmSize: string,
+ ulimit: []string,
+ volume: []string
+)
+
+# BuildInfo is used to describe user input for building images
+type BuildInfo (
+ architecture: string,
+ addCapabilities: []string,
+ additionalTags: []string,
+ annotations: []string,
+ buildArgs: [string]string,
+ buildOptions: BuildOptions,
+ cniConfigDir: string,
+ cniPluginDir: string,
+ compression: string,
+ contextDir: string,
+ defaultsMountFilePath: string,
+ devices: []string,
+ dockerfiles: []string,
+ dropCapabilities: []string,
+ err: string,
+ forceRmIntermediateCtrs: bool,
+ iidfile: string,
+ label: []string,
+ layers: bool,
+ nocache: bool,
+ os: string,
+ out: string,
+ output: string,
+ outputFormat: string,
+ pullPolicy: string,
+ quiet: bool,
+ remoteIntermediateCtrs: bool,
+ reportWriter: string,
+ runtimeArgs: []string,
+ signBy: string,
+ squash: bool,
+ target: string,
+ transientMounts: []string
+)
+
+# MoreResponse is a struct for when responses from varlink requires longer output
+type MoreResponse (
+ logs: []string,
+ id: string
+)
+
+# ListPodContainerInfo is a returned struct for describing containers
+# in a pod.
+type ListPodContainerInfo (
+ name: string,
+ id: string,
+ status: string
+)
+
+# PodCreate is an input structure for creating pods.
+# It emulates options to podman pod create. The infraCommand and
+# infraImage options are currently NotSupported.
+type PodCreate (
+ name: string,
+ cgroupParent: string,
+ labels: [string]string,
+ share: []string,
+ infra: bool,
+ infraCommand: string,
+ infraImage: string,
+ publish: []string
+)
+
+# ListPodData is the returned struct for an individual pod
+type ListPodData (
+ id: string,
+ name: string,
+ createdat: string,
+ cgroup: string,
+ status: string,
+ labels: [string]string,
+ numberofcontainers: string,
+ containersinfo: []ListPodContainerInfo
+)
+
+type PodContainerErrorData (
+ containerid: string,
+ reason: string
+)
+
+# Runlabel describes the required input for container runlabel
+type Runlabel(
+ image: string,
+ authfile: string,
+ display: bool,
+ name: string,
+ pull: bool,
+ label: string,
+ extraArgs: []string,
+ opts: [string]string
+)
+
+# Event describes a libpod struct
+type Event(
+ # TODO: make status and type a enum at some point?
+ # id is the container, volume, pod, image ID
+ id: string,
+ # image is the image name where applicable
+ image: string,
+ # name is the name of the pod, container, image
+ name: string,
+ # status describes the event that happened (i.e. create, remove, ...)
+ status: string,
+ # time the event happened
+ time: string,
+ # type describes object the event happened with (image, container...)
+ type: string
+)
+
+type DiffInfo(
+ # path that is different
+ path: string,
+ # Add, Delete, Modify
+ changeType: string
+)
+
+type ExecOpts(
+ # container name or id
+ name: string,
+ # Create pseudo tty
+ tty: bool,
+ # privileged access in container
+ privileged: bool,
+ # command to execute in container
+ cmd: []string,
+ # user to use in container
+ user: ?string,
+ # workdir to run command in container
+ workdir: ?string,
+ # slice of keyword=value environment variables
+ env: ?[]string,
+ # string of detach keys
+ detachKeys: ?string
+)
+
+# GetVersion returns version and build information of the podman service
+method GetVersion() -> (
+ version: string,
+ go_version: string,
+ git_commit: string,
+ built: string, # as RFC3339
+ os_arch: string,
+ remote_api_version: int
+)
+
+# Reset resets Podman back to its initial state.
+# Removes all Pods, Containers, Images and Volumes
+method Reset() -> ()
+
+# GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats,
+# build information of Podman, and system-wide registries.
+method GetInfo() -> (info: PodmanInfo)
+
+# ListContainers returns information about all containers.
+# See also [GetContainer](#GetContainer).
+method ListContainers() -> (containers: []Container)
+
+method Ps(opts: PsOpts) -> (containers: []PsContainer)
+
+method GetContainersByStatus(status: []string) -> (containerS: []Container)
+
+method Top (nameOrID: string, descriptors: []string) -> (top: []string)
+
+# HealthCheckRun executes defined container's healthcheck command
+# and returns the container's health status.
+method HealthCheckRun (nameOrID: string) -> (healthCheckStatus: string)
+
+# GetContainer returns information about a single container. If a container
+# with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound)
+# error will be returned. See also [ListContainers](ListContainers) and
+# [InspectContainer](#InspectContainer).
+method GetContainer(id: string) -> (container: Container)
+
+# GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of
+# container names. The definition of latest container means the latest by creation date. In a multi-
+# user environment, results might differ from what you expect.
+method GetContainersByContext(all: bool, latest: bool, args: []string) -> (containers: []string)
+
+# CreateContainer creates a new container from an image. It uses a [Create](#Create) type for input.
+method CreateContainer(create: Create) -> (container: string)
+
+# InspectContainer data takes a name or ID of a container returns the inspection
+# data in string format. You can then serialize the string into JSON. A [ContainerNotFound](#ContainerNotFound)
+# error will be returned if the container cannot be found. See also [InspectImage](#InspectImage).
+method InspectContainer(name: string) -> (container: string)
+
+# ListContainerProcesses takes a name or ID of a container and returns the processes
+# running inside the container as array of strings. It will accept an array of string
+# arguments that represent ps options. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
+# error will be returned.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.ListContainerProcesses '{"name": "135d71b9495f", "opts": []}'
+# {
+# "container": [
+# " UID PID PPID C STIME TTY TIME CMD",
+# " 0 21220 21210 0 09:05 pts/0 00:00:00 /bin/sh",
+# " 0 21232 21220 0 09:05 pts/0 00:00:00 top",
+# " 0 21284 21220 0 09:05 pts/0 00:00:00 vi /etc/hosts"
+# ]
+# }
+# ~~~
+method ListContainerProcesses(name: string, opts: []string) -> (container: []string)
+
+# GetContainerLogs takes a name or ID of a container and returns the logs of that container.
+# If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) error will be returned.
+# The container logs are returned as an array of strings. GetContainerLogs will honor the streaming
+# capability of varlink if the client invokes it.
+method GetContainerLogs(name: string) -> (container: []string)
+
+method GetContainersLogs(names: []string, follow: bool, latest: bool, since: string, tail: int, timestamps: bool) -> (log: LogLine)
+
+# ListContainerChanges takes a name or ID of a container and returns changes between the container and
+# its base image. It returns a struct of changed, deleted, and added path names.
+method ListContainerChanges(name: string) -> (container: ContainerChanges)
+
+# ExportContainer creates an image from a container. It takes the name or ID of a container and a
+# path representing the target tarfile. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
+# error will be returned.
+# The return value is the written tarfile.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.ExportContainer '{"name": "flamboyant_payne", "path": "/tmp/payne.tar" }'
+# {
+# "tarfile": "/tmp/payne.tar"
+# }
+# ~~~
+method ExportContainer(name: string, path: string) -> (tarfile: string)
+
+# GetContainerStats takes the name or ID of a container and returns a single ContainerStats structure which
+# contains attributes like memory and cpu usage. If the container cannot be found, a
+# [ContainerNotFound](#ContainerNotFound) error will be returned. If the container is not running, a [NoContainerRunning](#NoContainerRunning)
+# error will be returned
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.GetContainerStats '{"name": "c33e4164f384"}'
+# {
+# "container": {
+# "block_input": 0,
+# "block_output": 0,
+# "cpu": 2.571123918839990154678e-08,
+# "cpu_nano": 49037378,
+# "id": "c33e4164f384aa9d979072a63319d66b74fd7a128be71fa68ede24f33ec6cfee",
+# "mem_limit": 33080606720,
+# "mem_perc": 2.166828456524753747370e-03,
+# "mem_usage": 716800,
+# "name": "competent_wozniak",
+# "net_input": 768,
+# "net_output": 5910,
+# "pids": 1,
+# "system_nano": 10000000
+# }
+# }
+# ~~~
+method GetContainerStats(name: string) -> (container: ContainerStats)
+
+# GetContainerStatsWithHistory takes a previous set of container statistics and uses libpod functions
+# to calculate the containers statistics based on current and previous measurements.
+method GetContainerStatsWithHistory(previousStats: ContainerStats) -> (container: ContainerStats)
+
+# This method has not be implemented yet.
+# method ResizeContainerTty() -> (notimplemented: NotImplemented)
+
+# StartContainer starts a created or stopped container. It takes the name or ID of container. It returns
+# the container ID once started. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
+# error will be returned. See also [CreateContainer](#CreateContainer).
+method StartContainer(name: string) -> (container: string)
+
+# StopContainer stops a container given a timeout. It takes the name or ID of a container as well as a
+# timeout value. The timeout value the time before a forcible stop to the container is applied. It
+# returns the container ID once stopped. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
+# error will be returned instead. See also [KillContainer](KillContainer).
+# #### Error
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.StopContainer '{"name": "135d71b9495f", "timeout": 5}'
+# {
+# "container": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6"
+# }
+# ~~~
+method StopContainer(name: string, timeout: int) -> (container: string)
+
+# InitContainer initializes the given container. It accepts a container name or
+# ID, and will initialize the container matching that ID if possible, and error
+# if not. Containers can only be initialized when they are in the Created or
+# Exited states. Initialization prepares a container to be started, but does not
+# start the container. It is intended to be used to debug a container's state
+# prior to starting it.
+method InitContainer(name: string) -> (container: string)
+
+# RestartContainer will restart a running container given a container name or ID and timeout value. The timeout
+# value is the time before a forcible stop is used to stop the container. If the container cannot be found by
+# name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise, the ID of the
+# container will be returned.
+method RestartContainer(name: string, timeout: int) -> (container: string)
+
+# KillContainer takes the name or ID of a container as well as a signal to be applied to the container. Once the
+# container has been killed, the container's ID is returned. If the container cannot be found, a
+# [ContainerNotFound](#ContainerNotFound) error is returned. See also [StopContainer](StopContainer).
+method KillContainer(name: string, signal: int) -> (container: string)
+
+# This method has not be implemented yet.
+# method UpdateContainer() -> (notimplemented: NotImplemented)
+
+# This method has not be implemented yet.
+# method RenameContainer() -> (notimplemented: NotImplemented)
+
+# PauseContainer takes the name or ID of container and pauses it. If the container cannot be found,
+# a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise the ID of the container is returned.
+# See also [UnpauseContainer](#UnpauseContainer).
+method PauseContainer(name: string) -> (container: string)
+
+# UnpauseContainer takes the name or ID of container and unpauses a paused container. If the container cannot be
+# found, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise the ID of the container is returned.
+# See also [PauseContainer](#PauseContainer).
+method UnpauseContainer(name: string) -> (container: string)
+
+# Attach takes the name or ID of a container and sets up the ability to remotely attach to its console. The start
+# bool is whether you wish to start the container in question first.
+method Attach(name: string, detachKeys: string, start: bool) -> ()
+
+method AttachControl(name: string) -> ()
+
+# GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed
+# to properly communicate with a container. The first is the actual I/O socket that the container uses. The
+# second is a "control" socket where things like resizing the TTY events are sent. If the container cannot be
+# found, a [ContainerNotFound](#ContainerNotFound) error will be returned.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/io.podman/io.podman.GetAttachSockets '{"name": "b7624e775431219161"}'
+# {
+# "sockets": {
+# "container_id": "b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca",
+# "control_socket": "/var/lib/containers/storage/overlay-containers/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/userdata/ctl",
+# "io_socket": "/var/run/libpod/socket/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/attach"
+# }
+# }
+# ~~~
+method GetAttachSockets(name: string) -> (sockets: Sockets)
+
+# WaitContainer takes the name or ID of a container and waits the given interval in milliseconds until the container
+# stops. Upon stopping, the return code of the container is returned. If the container container cannot be found by ID
+# or name, a [ContainerNotFound](#ContainerNotFound) error is returned.
+method WaitContainer(name: string, interval: int) -> (exitcode: int)
+
+# RemoveContainer requires the name or ID of a container as well as a boolean that
+# indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean
+# indicating whether to remove builtin volumes. Upon successful removal of the
+# container, its ID is returned. If the
+# container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
+# See also [EvictContainer](EvictContainer).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
+# {
+# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
+# }
+# ~~~
+method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string)
+
+# EvictContainer requires the name or ID of a container as well as a boolean that
+# indicates to remove builtin volumes. Upon successful eviction of the container,
+# its ID is returned. If the container cannot be found by name or ID,
+# a [ContainerNotFound](#ContainerNotFound) error will be returned.
+# See also [RemoveContainer](RemoveContainer).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}'
+# {
+# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
+# }
+# ~~~
+method EvictContainer(name: string, removeVolumes: bool) -> (container: string)
+
+# DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted
+# container IDs. See also [RemoveContainer](RemoveContainer).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteStoppedContainers
+# {
+# "containers": [
+# "451410b931d00def8aa9b4f8084e4d4a39e5e04ea61f358cf53a5cf95afcdcee",
+# "8b60f754a3e01389494a9581ade97d35c2765b6e2f19acd2d3040c82a32d1bc0",
+# "cf2e99d4d3cad6073df199ed32bbe64b124f3e1aba6d78821aa8460e70d30084",
+# "db901a329587312366e5ecff583d08f0875b4b79294322df67d90fc6eed08fc1"
+# ]
+# }
+# ~~~
+method DeleteStoppedContainers() -> (containers: []string)
+
+# ListImages returns information about the images that are currently in storage.
+# See also [InspectImage](#InspectImage).
+method ListImages() -> (images: []Image)
+
+# ListImagesWithFilters returns information about the images that are currently in storage
+# after one or more filters has been applied.
+# See also [InspectImage](#InspectImage).
+method ListImagesWithFilters(filters: []string) -> (images: []Image)
+
+# GetImage returns information about a single image in storage.
+# If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will be returned.
+method GetImage(id: string) -> (image: Image)
+
+# BuildImage takes a [BuildInfo](#BuildInfo) structure and builds an image. At a minimum, you must provide the
+# contextDir tarball path, the 'dockerfiles' path, and 'output' option in the BuildInfo structure. The 'output'
+# options is the name of the of the resulting build. It will return a [MoreResponse](#MoreResponse) structure
+# that contains the build logs and resulting image ID.
+# #### Example
+# ~~~
+# $ sudo varlink call -m unix:///run/podman/io.podman/io.podman.BuildImage '{"build":{"contextDir":"/tmp/t/context.tar","dockerfiles":["Dockerfile"], "output":"foobar"}}'
+# {
+# "image": {
+# "id": "",
+# "logs": [
+# "STEP 1: FROM alpine\n"
+# ]
+# }
+# }
+# {
+# "image": {
+# "id": "",
+# "logs": [
+# "STEP 2: COMMIT foobar\n"
+# ]
+# }
+# }
+# {
+# "image": {
+# "id": "",
+# "logs": [
+# "b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9\n"
+# ]
+# }
+# }
+# {
+# "image": {
+# "id": "b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9",
+# "logs": []
+# }
+# }
+# ~~~
+method BuildImage(build: BuildInfo) -> (image: MoreResponse)
+
+# This function is not implemented yet.
+# method CreateImage() -> (notimplemented: NotImplemented)
+
+# InspectImage takes the name or ID of an image and returns a string representation of data associated with the
+#image. You must serialize the string into JSON to use it further. An [ImageNotFound](#ImageNotFound) error will
+# be returned if the image cannot be found.
+method InspectImage(name: string) -> (image: string)
+
+# HistoryImage takes the name or ID of an image and returns information about its history and layers. The returned
+# history is in the form of an array of ImageHistory structures. If the image cannot be found, an
+# [ImageNotFound](#ImageNotFound) error is returned.
+method HistoryImage(name: string) -> (history: []ImageHistory)
+
+# PushImage takes two input arguments: the name or ID of an image, the fully-qualified destination name of the image,
+# It will return an [ImageNotFound](#ImageNotFound) error if
+# the image cannot be found in local storage; otherwise it will return a [MoreResponse](#MoreResponse)
+method PushImage(name: string, tag: string, compress: bool, format: string, removeSignatures: bool, signBy: string) -> (reply: MoreResponse)
+
+# TagImage takes the name or ID of an image in local storage as well as the desired tag name. If the image cannot
+# be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success.
+method TagImage(name: string, tagged: string) -> (image: string)
+
+# UntagImage takes the name or ID of an image in local storage as well as the
+# tag name to be removed. If the image cannot be found, an
+# [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of
+# the image is returned on success.
+method UntagImage(name: string, tag: string) -> (image: string)
+
+# RemoveImage takes the name or ID of an image as well as a boolean that determines if containers using that image
+# should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The
+# ID of the removed image is returned when complete. See also [DeleteUnusedImages](DeleteUnusedImages).
+# #### Example
+# ~~~
+# varlink call -m unix:/run/podman/io.podman/io.podman.RemoveImage '{"name": "registry.fedoraproject.org/fedora", "force": true}'
+# {
+# "image": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e"
+# }
+# ~~~
+method RemoveImage(name: string, force: bool) -> (image: string)
+
+# RemoveImageWithResponse takes the name or ID of an image as well as a boolean that determines if containers using that image
+# should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The response is
+# in the form of a RemoveImageResponse .
+method RemoveImageWithResponse(name: string, force: bool) -> (response: RemoveImageResponse)
+
+# SearchImages searches available registries for images that contain the
+# contents of "query" in their name. If "limit" is given, limits the amount of
+# search results per registry.
+method SearchImages(query: string, limit: ?int, filter: ImageSearchFilter) -> (results: []ImageSearchResult)
+
+# DeleteUnusedImages deletes any images not associated with a container. The IDs of the deleted images are returned
+# in a string array.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages
+# {
+# "images": [
+# "166ea6588079559c724c15223f52927f514f73dd5c5cf2ae2d143e3b2e6e9b52",
+# "da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e",
+# "3ef70f7291f47dfe2b82931a993e16f5a44a0e7a68034c3e0e086d77f5829adc",
+# "59788edf1f3e78cd0ebe6ce1446e9d10788225db3dedcfd1a59f764bad2b2690"
+# ]
+# }
+# ~~~
+method DeleteUnusedImages() -> (images: []string)
+
+# Commit, creates an image from an existing container. It requires the name or
+# ID of the container as well as the resulting image name. Optionally, you can define an author and message
+# to be added to the resulting image. You can also define changes to the resulting image for the following
+# attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOLUME, and WORKDIR_. To pause the
+# container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot
+# be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise,
+# the resulting image's ID will be returned as a string inside a MoreResponse.
+method Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) -> (reply: MoreResponse)
+
+# ImportImage imports an image from a source (like tarball) into local storage. The image can have additional
+# descriptions added to it using the message and changes options. See also [ExportImage](ExportImage).
+method ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) -> (image: string)
+
+# ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also
+# a boolean option to force compression. It also takes in a string array of tags to be able to save multiple
+# tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID
+# of the image is returned. If the image cannot be found in local storage, an [ImageNotFound](#ImageNotFound)
+# error will be returned. See also [ImportImage](ImportImage).
+method ExportImage(name: string, destination: string, compress: bool, tags: []string) -> (image: string)
+
+# PullImage pulls an image from a repository to local storage. After a successful pull, the image id and logs
+# are returned as a [MoreResponse](#MoreResponse). This connection also will handle a WantsMores request to send
+# status as it occurs.
+method PullImage(name: string, creds: AuthConfig) -> (reply: MoreResponse)
+
+# CreatePod creates a new empty pod. It uses a [PodCreate](#PodCreate) type for input.
+# On success, the ID of the newly created pod will be returned.
+# #### Example
+# ~~~
+# $ varlink call unix:/run/podman/io.podman/io.podman.CreatePod '{"create": {"name": "test"}}'
+# {
+# "pod": "b05dee7bd4ccfee688099fe1588a7a898d6ddd6897de9251d4671c9b0feacb2a"
+# }
+#
+# $ varlink call unix:/run/podman/io.podman/io.podman.CreatePod '{"create": {"infra": true, "share": ["ipc", "net", "uts"]}}'
+# {
+# "pod": "d7697449a8035f613c1a8891286502aca68fff7d5d49a85279b3bda229af3b28"
+# }
+# ~~~
+method CreatePod(create: PodCreate) -> (pod: string)
+
+# ListPods returns a list of pods in no particular order. They are
+# returned as an array of ListPodData structs. See also [GetPod](#GetPod).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.ListPods
+# {
+# "pods": [
+# {
+# "cgroup": "machine.slice",
+# "containersinfo": [
+# {
+# "id": "00c130a45de0411f109f1a0cfea2e298df71db20fa939de5cab8b2160a36be45",
+# "name": "1840835294cf-infra",
+# "status": "running"
+# },
+# {
+# "id": "49a5cce72093a5ca47c6de86f10ad7bb36391e2d89cef765f807e460865a0ec6",
+# "name": "upbeat_murdock",
+# "status": "running"
+# }
+# ],
+# "createdat": "2018-12-07 13:10:15.014139258 -0600 CST",
+# "id": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f",
+# "name": "foobar",
+# "numberofcontainers": "2",
+# "status": "Running"
+# },
+# {
+# "cgroup": "machine.slice",
+# "containersinfo": [
+# {
+# "id": "1ca4b7bbba14a75ba00072d4b705c77f3df87db0109afaa44d50cb37c04a477e",
+# "name": "784306f655c6-infra",
+# "status": "running"
+# }
+# ],
+# "createdat": "2018-12-07 13:09:57.105112457 -0600 CST",
+# "id": "784306f655c6200aea321dd430ba685e9b2cc1f7d7528a72f3ff74ffb29485a2",
+# "name": "nostalgic_pike",
+# "numberofcontainers": "1",
+# "status": "Running"
+# }
+# ]
+# }
+# ~~~
+method ListPods() -> (pods: []ListPodData)
+
+# GetPod takes a name or ID of a pod and returns single [ListPodData](#ListPodData)
+# structure. A [PodNotFound](#PodNotFound) error will be returned if the pod cannot be found.
+# See also [ListPods](ListPods).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.GetPod '{"name": "foobar"}'
+# {
+# "pod": {
+# "cgroup": "machine.slice",
+# "containersinfo": [
+# {
+# "id": "00c130a45de0411f109f1a0cfea2e298df71db20fa939de5cab8b2160a36be45",
+# "name": "1840835294cf-infra",
+# "status": "running"
+# },
+# {
+# "id": "49a5cce72093a5ca47c6de86f10ad7bb36391e2d89cef765f807e460865a0ec6",
+# "name": "upbeat_murdock",
+# "status": "running"
+# }
+# ],
+# "createdat": "2018-12-07 13:10:15.014139258 -0600 CST",
+# "id": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f",
+# "name": "foobar",
+# "numberofcontainers": "2",
+# "status": "Running"
+# }
+# }
+# ~~~
+method GetPod(name: string) -> (pod: ListPodData)
+
+# InspectPod takes the name or ID of an image and returns a string representation of data associated with the
+# pod. You must serialize the string into JSON to use it further. A [PodNotFound](#PodNotFound) error will
+# be returned if the pod cannot be found.
+method InspectPod(name: string) -> (pod: string)
+
+# StartPod starts containers in a pod. It takes the name or ID of pod. If the pod cannot be found, a [PodNotFound](#PodNotFound)
+# error will be returned. Containers in a pod are started independently. If there is an error starting one container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was started with no errors, the pod ID is returned.
+# See also [CreatePod](#CreatePod).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.StartPod '{"name": "135d71b9495f"}'
+# {
+# "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6",
+# }
+# ~~~
+method StartPod(name: string) -> (pod: string)
+
+# StopPod stops containers in a pod. It takes the name or ID of a pod and a timeout.
+# If the pod cannot be found, a [PodNotFound](#PodNotFound) error will be returned instead.
+# Containers in a pod are stopped independently. If there is an error stopping one container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was stopped with no errors, the pod ID is returned.
+# See also [KillPod](KillPod).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.StopPod '{"name": "135d71b9495f"}'
+# {
+# "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6"
+# }
+# ~~~
+method StopPod(name: string, timeout: int) -> (pod: string)
+
+# RestartPod will restart containers in a pod given a pod name or ID. Containers in
+# the pod that are running will be stopped, then all stopped containers will be run.
+# If the pod cannot be found by name or ID, a [PodNotFound](#PodNotFound) error will be returned.
+# Containers in a pod are restarted independently. If there is an error restarting one container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was restarted with no errors, the pod ID is returned.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.RestartPod '{"name": "135d71b9495f"}'
+# {
+# "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6"
+# }
+# ~~~
+method RestartPod(name: string) -> (pod: string)
+
+# KillPod takes the name or ID of a pod as well as a signal to be applied to the pod. If the pod cannot be found, a
+# [PodNotFound](#PodNotFound) error is returned.
+# Containers in a pod are killed independently. If there is an error killing one container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was killed with no errors, the pod ID is returned.
+# See also [StopPod](StopPod).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.KillPod '{"name": "foobar", "signal": 15}'
+# {
+# "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
+# }
+# ~~~
+method KillPod(name: string, signal: int) -> (pod: string)
+
+# PausePod takes the name or ID of a pod and pauses the running containers associated with it. If the pod cannot be found,
+# a [PodNotFound](#PodNotFound) error will be returned.
+# Containers in a pod are paused independently. If there is an error pausing one container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was paused with no errors, the pod ID is returned.
+# See also [UnpausePod](#UnpausePod).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "foobar"}'
+# {
+# "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
+# }
+# ~~~
+method PausePod(name: string) -> (pod: string)
+
+# UnpausePod takes the name or ID of a pod and unpauses the paused containers associated with it. If the pod cannot be
+# found, a [PodNotFound](#PodNotFound) error will be returned.
+# Containers in a pod are unpaused independently. If there is an error unpausing one container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was unpaused with no errors, the pod ID is returned.
+# See also [PausePod](#PausePod).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.UnpausePod '{"name": "foobar"}'
+# {
+# "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
+# }
+# ~~~
+method UnpausePod(name: string) -> (pod: string)
+
+# RemovePod takes the name or ID of a pod as well a boolean representing whether a running
+# container in the pod can be stopped and removed. If a pod has containers associated with it, and force is not true,
+# an error will occur.
+# If the pod cannot be found by name or ID, a [PodNotFound](#PodNotFound) error will be returned.
+# Containers in a pod are removed independently. If there is an error removing any container, the ID of those containers
+# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError).
+# If the pod was removed with no errors, the pod ID is returned.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.RemovePod '{"name": "62f4fd98cb57", "force": "true"}'
+# {
+# "pod": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
+# }
+# ~~~
+method RemovePod(name: string, force: bool) -> (pod: string)
+
+# This method has not be implemented yet.
+# method WaitPod() -> (notimplemented: NotImplemented)
+
+method TopPod(pod: string, latest: bool, descriptors: []string) -> (stats: []string)
+
+# GetPodStats takes the name or ID of a pod and returns a pod name and slice of ContainerStats structure which
+# contains attributes like memory and cpu usage. If the pod cannot be found, a [PodNotFound](#PodNotFound)
+# error will be returned. If the pod has no running containers associated with it, a [NoContainerRunning](#NoContainerRunning)
+# error will be returned.
+# #### Example
+# ~~~
+# $ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b508b6f12b11d8fe02e"}'
+# {
+# "containers": [
+# {
+# "block_input": 0,
+# "block_output": 0,
+# "cpu": 2.833470544016107524276e-08,
+# "cpu_nano": 54363072,
+# "id": "a64b51f805121fe2c5a3dc5112eb61d6ed139e3d1c99110360d08b58d48e4a93",
+# "mem_limit": 12276146176,
+# "mem_perc": 7.974359265237864966003e-03,
+# "mem_usage": 978944,
+# "name": "quirky_heisenberg",
+# "net_input": 866,
+# "net_output": 7388,
+# "pids": 1,
+# "system_nano": 20000000
+# }
+# ],
+# "pod": "7f62b508b6f12b11d8fe02e0db4de6b9e43a7d7699b33a4fc0d574f6e82b4ebd"
+# }
+# ~~~
+method GetPodStats(name: string) -> (pod: string, containers: []ContainerStats)
+
+# GetPodsByStatus searches for pods whose status is included in statuses
+method GetPodsByStatus(statuses: []string) -> (pods: []string)
+
+# ImageExists talks a full or partial image ID or name and returns an int as to whether
+# the image exists in local storage. An int result of 0 means the image does exist in
+# local storage; whereas 1 indicates the image does not exists in local storage.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "imageddoesntexist"}'
+# {
+# "exists": 1
+# }
+# ~~~
+method ImageExists(name: string) -> (exists: int)
+
+# ImageTree returns the image tree for the provided image name or ID
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}'
+# {
+# "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\nā””ā”€ā”€ ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n"
+# }
+# ~~~
+method ImageTree(name: string, whatRequires: bool) -> (tree: string)
+
+# ContainerExists takes a full or partial container ID or name and returns an int as to
+# whether the container exists in local storage. A result of 0 means the container does
+# exists; whereas a result of 1 means it could not be found.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.ContainerExists '{"name": "flamboyant_payne"}'{
+# "exists": 0
+# }
+# ~~~
+method ContainerExists(name: string) -> (exists: int)
+
+# ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container
+# ID. On successful checkpoint, the id of the checkpointed container is returned.
+method ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) -> (id: string)
+
+# ContainerRestore restores a container that has been checkpointed. The container to be restored can
+# be identified by its name or full/partial container ID. A successful restore will result in the return
+# of the container's ID.
+method ContainerRestore(name: string, keep: bool, tcpEstablished: bool) -> (id: string)
+
+# ContainerRunlabel runs executes a command as described by a given container image label.
+method ContainerRunlabel(runlabel: Runlabel) -> ()
+
+# ExecContainer executes a command in the given container.
+method ExecContainer(opts: ExecOpts) -> ()
+
+# ListContainerMounts gathers all the mounted container mount points and returns them as an array
+# of strings
+# #### Example
+# ~~~
+# $ varlink call unix:/run/podman/io.podman/io.podman.ListContainerMounts
+# {
+# "mounts": {
+# "04e4c255269ed2545e7f8bd1395a75f7949c50c223415c00c1d54bfa20f3b3d9": "/var/lib/containers/storage/overlay/a078925828f57e20467ca31cfca8a849210d21ec7e5757332b72b6924f441c17/merged",
+# "1d58c319f9e881a644a5122ff84419dccf6d138f744469281446ab243ef38924": "/var/lib/containers/storage/overlay/948fcf93f8cb932f0f03fd52e3180a58627d547192ffe3b88e0013b98ddcd0d2/merged"
+# }
+# }
+# ~~~
+method ListContainerMounts() -> (mounts: [string]string)
+
+# MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination
+# mount is returned as a string.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.MountContainer '{"name": "jolly_shannon"}'{
+# "path": "/var/lib/containers/storage/overlay/419eeb04e783ea159149ced67d9fcfc15211084d65e894792a96bedfae0470ca/merged"
+# }
+# ~~~
+method MountContainer(name: string) -> (path: string)
+
+# UnmountContainer umounts a container by its name or full/partial container ID.
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.UnmountContainer '{"name": "jolly_shannon", "force": false}'
+# {}
+# ~~~
+method UnmountContainer(name: string, force: bool) -> ()
+
+# ImagesPrune removes all unused images from the local store. Upon successful pruning,
+# the IDs of the removed images are returned.
+method ImagesPrune(all: bool, filter: []string) -> (pruned: []string)
+
+# This function is not implemented yet.
+# method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
+
+# GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
+# and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
+method GenerateKube(name: string, service: bool) -> (pod: KubePodService)
+
+# ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML)
+# like that created by GenerateKube. See also [GenerateKube](GenerateKube).
+# method ReplayKube() -> (notimplemented: NotImplemented)
+
+# ContainerConfig returns a container's config in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerConfig(name: string) -> (config: string)
+
+# ContainerArtifacts returns a container's artifacts in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerArtifacts(name: string, artifactName: string) -> (config: string)
+
+# ContainerInspectData returns a container's inspect data in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerInspectData(name: string, size: bool) -> (config: string)
+
+# ContainerStateData returns a container's state config in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerStateData(name: string) -> (config: string)
+
+# PodStateData returns inspectr level information of a given pod in string form. This call is for
+# development of Podman only and generally should not be used.
+method PodStateData(name: string) -> (config: string)
+
+# This call is for the development of Podman only and should not be used.
+method CreateFromCC(in: []string) -> (id: string)
+
+# Spec returns the oci spec for a container. This call is for development of Podman only and generally should not be used.
+method Spec(name: string) -> (config: string)
+
+# Sendfile allows a remote client to send a file to the host
+method SendFile(type: string, length: int) -> (file_handle: string)
+
+# ReceiveFile allows the host to send a remote client a file
+method ReceiveFile(path: string, delete: bool) -> (len: int)
+
+# VolumeCreate creates a volume on a remote host
+method VolumeCreate(options: VolumeCreateOpts) -> (volumeName: string)
+
+# VolumeRemove removes a volume on a remote host
+method VolumeRemove(options: VolumeRemoveOpts) -> (successes: []string, failures: [string]string)
+
+# GetVolumes gets slice of the volumes on a remote host
+method GetVolumes(args: []string, all: bool) -> (volumes: []Volume)
+
+# InspectVolume inspects a single volume. Returns inspect JSON in the form of a
+# string.
+method InspectVolume(name: string) -> (volume: string)
+
+# VolumesPrune removes unused volumes on the host
+method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string)
+
+# ImageSave allows you to save an image from the local image storage to a tarball
+method ImageSave(options: ImageSaveOptions) -> (reply: MoreResponse)
+
+# GetPodsByContext allows you to get a list pod ids depending on all, latest, or a list of
+# pod names. The definition of latest pod means the latest by creation date. In a multi-
+# user environment, results might differ from what you expect.
+method GetPodsByContext(all: bool, latest: bool, args: []string) -> (pods: []string)
+
+# LoadImage allows you to load an image into local storage from a tarball.
+method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) -> (reply: MoreResponse)
+
+# GetEvents returns known libpod events filtered by the options provided.
+method GetEvents(filter: []string, since: string, until: string) -> (events: Event)
+
+# Diff returns a diff between libpod objects
+method Diff(name: string) -> (diffs: []DiffInfo)
+
+# GetLayersMapWithImageInfo is for the development of Podman and should not be used.
+method GetLayersMapWithImageInfo() -> (layerMap: string)
+
+# BuildImageHierarchyMap is for the development of Podman and should not be used.
+method BuildImageHierarchyMap(name: string) -> (imageInfo: string)
+
+# ImageNotFound means the image could not be found by the provided name or ID in local storage.
+error ImageNotFound (id: string, reason: string)
+
+# ContainerNotFound means the container could not be found by the provided name or ID in local storage.
+error ContainerNotFound (id: string, reason: string)
+
+# NoContainerRunning means none of the containers requested are running in a command that requires a running container.
+error NoContainerRunning ()
+
+# PodNotFound means the pod could not be found by the provided name or ID in local storage.
+error PodNotFound (name: string, reason: string)
+
+# VolumeNotFound means the volume could not be found by the name or ID in local storage.
+error VolumeNotFound (id: string, reason: string)
+
+# PodContainerError means a container associated with a pod failed to perform an operation. It contains
+# a container ID of the container that failed.
+error PodContainerError (podname: string, errors: []PodContainerErrorData)
+
+# NoContainersInPod means a pod has no containers on which to perform the operation. It contains
+# the pod ID.
+error NoContainersInPod (name: string)
+
+# InvalidState indicates that a container or pod was in an improper state for the requested operation
+error InvalidState (id: string, reason: string)
+
+# ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message
+# is includes as part of the error's text.
+error ErrorOccurred (reason: string)
+
+# RuntimeErrors generally means a runtime could not be found or gotten.
+error RuntimeError (reason: string)
+
+# The Podman endpoint requires that you use a streaming connection.
+error WantsMoreRequired (reason: string)
+
+# Container is already stopped
+error ErrCtrStopped (id: string)
+
+# This function requires CGroupsV2 to run in rootless mode.
+error ErrRequiresCgroupsV2ForRootless(reason: string)
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index 5beca3c6f..34f351669 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -6,17 +6,17 @@ import (
"bufio"
"io"
- "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/libpod/pkg/varlinkapi/virtwriter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/client-go/tools/remotecommand"
)
-func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *libpod.AttachStreams) {
+func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *define.AttachStreams) {
// These are the varlink sockets
reader := call.Call.Reader
@@ -28,9 +28,9 @@ func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.
stdoutWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdout)
// TODO if runc ever starts passing stderr, we can too
- //stderrWriter := NewVirtWriteCloser(writer, ToStderr)
+ // stderrWriter := NewVirtWriteCloser(writer, ToStderr)
- streams := libpod.AttachStreams{
+ streams := define.AttachStreams{
OutputStream: stdoutWriter,
InputStream: bufio.NewReader(pr),
// Runc eats the error stream
@@ -117,7 +117,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
return call.Writer.Flush()
}
-func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
+func attach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
go func() {
if err := ctr.Attach(streams, detachKeys, resize); err != nil {
errChan <- err
@@ -127,7 +127,7 @@ func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys str
return attachError
}
-func startAndAttach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
+func startAndAttach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
var finalErr error
attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false)
if err != nil {
diff --git a/pkg/varlinkapi/config.go b/pkg/varlinkapi/config.go
index e75170547..c69dc794a 100644
--- a/pkg/varlinkapi/config.go
+++ b/pkg/varlinkapi/config.go
@@ -4,8 +4,8 @@ package varlinkapi
import (
"github.com/containers/libpod/cmd/podman/cliconfig"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/spf13/cobra"
)
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 55427771c..2d051470f 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -15,13 +15,13 @@ import (
"time"
"github.com/containers/libpod/cmd/podman/shared"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/adapter/shortcuts"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/libpod/pkg/varlinkapi/virtwriter"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
@@ -900,7 +900,7 @@ func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecO
return ecErr.Error
}
-//HealthCheckRun executes defined container's healthcheck command and returns the container's health status.
+// HealthCheckRun executes defined container's healthcheck command and returns the container's health status.
func (i *LibpodAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error {
hcStatus, err := i.Runtime.HealthCheck(nameOrID)
if err != nil && hcStatus != libpod.HealthCheckFailure {
diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go
index 6b23dce5e..bbd4d59f1 100644
--- a/pkg/varlinkapi/containers_create.go
+++ b/pkg/varlinkapi/containers_create.go
@@ -4,7 +4,7 @@ package varlinkapi
import (
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// CreateContainer ...
diff --git a/pkg/varlinkapi/events.go b/pkg/varlinkapi/events.go
index f9a9d9321..4ae2d1cb2 100644
--- a/pkg/varlinkapi/events.go
+++ b/pkg/varlinkapi/events.go
@@ -6,8 +6,8 @@ import (
"fmt"
"time"
- "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod/events"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// GetEvents is a remote endpoint to get events from the event log
diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go
index 19010097d..c19c8dede 100644
--- a/pkg/varlinkapi/generate.go
+++ b/pkg/varlinkapi/generate.go
@@ -6,7 +6,7 @@ import (
"encoding/json"
"github.com/containers/libpod/cmd/podman/shared"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// GenerateKube ...
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 2dfb84e58..c3b4bd9ae 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -21,16 +21,15 @@ import (
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/shared"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/channelwriter"
"github.com/containers/libpod/pkg/util"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -147,7 +146,6 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
err error
)
- systemContext := types.SystemContext{}
contextDir := config.ContextDir
newContextDir, err := ioutil.TempDir("", "buildTarball")
@@ -175,6 +173,8 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
logrus.Errorf("unable to delete directory '%s': %q", newContextDir, err)
}
}()
+
+ systemContext := types.SystemContext{}
// All output (stdout, stderr) is captured in output as well
var output bytes.Buffer
@@ -192,40 +192,40 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
Volumes: config.BuildOptions.Volume,
}
- hostNetwork := buildah.NamespaceOption{
- Name: string(specs.NetworkNamespace),
- Host: true,
- }
-
- namespace = append(namespace, hostNetwork)
-
options := imagebuildah.BuildOptions{
- CommonBuildOpts: commonOpts,
+ AddCapabilities: config.AddCapabilities,
AdditionalTags: config.AdditionalTags,
Annotations: config.Annotations,
+ Architecture: config.Architecture,
Args: config.BuildArgs,
CNIConfigDir: config.CniConfigDir,
CNIPluginPath: config.CniPluginDir,
+ CommonBuildOpts: commonOpts,
Compression: stringCompressionToArchiveType(config.Compression),
ContextDirectory: newContextDir,
DefaultMountsFilePath: config.DefaultsMountFilePath,
+ Devices: config.Devices,
Err: &output,
ForceRmIntermediateCtrs: config.ForceRmIntermediateCtrs,
IIDFile: config.Iidfile,
Labels: config.Label,
Layers: config.Layers,
+ NamespaceOptions: namespace,
NoCache: config.Nocache,
+ OS: config.Os,
Out: &output,
Output: config.Output,
- NamespaceOptions: namespace,
OutputFormat: config.OutputFormat,
PullPolicy: stringPullPolicyToType(config.PullPolicy),
Quiet: config.Quiet,
RemoveIntermediateCtrs: config.RemoteIntermediateCtrs,
ReportWriter: &output,
RuntimeArgs: config.RuntimeArgs,
+ SignBy: config.SignBy,
Squash: config.Squash,
SystemContext: &systemContext,
+ Target: config.Target,
+ TransientMounts: config.TransientMounts,
}
if call.WantsMore() {
@@ -587,7 +587,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
switch manifestType {
case "oci", "": // nolint
mimeType = buildah.OCIv1ImageManifest
@@ -597,7 +597,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
return call.ReplyErrorOccurred(fmt.Sprintf("unrecognized image format %q", manifestType))
}
coptions := buildah.CommitOptions{
- SignaturePolicyPath: rtc.SignaturePolicyPath,
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
ReportWriter: output,
SystemContext: sc,
PreferredManifestType: mimeType,
diff --git a/pkg/varlinkapi/mount.go b/pkg/varlinkapi/mount.go
index 63ce44291..2450f6fd9 100644
--- a/pkg/varlinkapi/mount.go
+++ b/pkg/varlinkapi/mount.go
@@ -2,9 +2,7 @@
package varlinkapi
-import (
- "github.com/containers/libpod/cmd/podman/varlink"
-)
+import iopodman "github.com/containers/libpod/pkg/varlink"
// ListContainerMounts ...
func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error {
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index 2ec45f7a1..79ffb6677 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -8,9 +8,9 @@ import (
"syscall"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/adapter/shortcuts"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// CreatePod ...
diff --git a/pkg/varlinkapi/remote_client.go b/pkg/varlinkapi/remote_client.go
index dd0613494..a16d11dec 100644
--- a/pkg/varlinkapi/remote_client.go
+++ b/pkg/varlinkapi/remote_client.go
@@ -3,8 +3,8 @@
package varlinkapi
import (
- "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// ContainerStatsToLibpodContainerStats converts the varlink containerstats to a libpod
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index e88d010c5..04fb9f648 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -10,8 +10,8 @@ import (
"time"
"github.com/containers/image/v5/pkg/sysregistriesv2"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod/define"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/sirupsen/logrus"
)
diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go
index 31d26c3aa..654da276e 100644
--- a/pkg/varlinkapi/transfers.go
+++ b/pkg/varlinkapi/transfers.go
@@ -9,7 +9,7 @@ import (
"io/ioutil"
"os"
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/sirupsen/logrus"
)
diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go
index d3a41f7ab..6b196f384 100644
--- a/pkg/varlinkapi/util.go
+++ b/pkg/varlinkapi/util.go
@@ -10,10 +10,10 @@ import (
"github.com/containers/buildah"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/channelwriter"
+ iopodman "github.com/containers/libpod/pkg/varlink"
"github.com/containers/storage/pkg/archive"
)
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
index cbb4a70cc..b0c3608c4 100644
--- a/pkg/varlinkapi/volumes.go
+++ b/pkg/varlinkapi/volumes.go
@@ -6,8 +6,8 @@ import (
"encoding/json"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ iopodman "github.com/containers/libpod/pkg/varlink"
)
// VolumeCreate creates a libpod volume based on input from a varlink connection
@@ -113,11 +113,11 @@ func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
if err != nil {
return call.ReplyVolumesPrune([]string{}, []string{err.Error()})
}
- for _, i := range responses {
- if i.Err == nil {
- prunedNames = append(prunedNames, i.Id)
+ for k, v := range responses {
+ if v == nil {
+ prunedNames = append(prunedNames, k)
} else {
- prunedErrors = append(prunedErrors, i.Err.Error())
+ prunedErrors = append(prunedErrors, v.Error())
}
}
return call.ReplyVolumesPrune(prunedNames, prunedErrors)