summaryrefslogtreecommitdiff
path: root/pkg/domain
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/domain')
-rw-r--r--pkg/domain/entities/containers.go69
-rw-r--r--pkg/domain/entities/engine_container.go7
-rw-r--r--pkg/domain/entities/engine_image.go4
-rw-r--r--pkg/domain/entities/images.go69
-rw-r--r--pkg/domain/infra/abi/containers.go202
-rw-r--r--pkg/domain/infra/abi/images.go98
-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/tunnel/containers.go105
-rw-r--r--pkg/domain/infra/tunnel/images.go84
-rw-r--r--pkg/domain/infra/tunnel/system.go1
12 files changed, 902 insertions, 10 deletions
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index b7f1cd812..cf907eb1b 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -2,6 +2,7 @@ package entities
import (
"io"
+ "os"
"time"
"github.com/containers/libpod/libpod/define"
@@ -117,3 +118,71 @@ type CommitOptions struct {
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
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index ce0c161e9..9bf3d51de 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -4,12 +4,19 @@ 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)
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 2ca48e795..a28bfc548 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -14,4 +14,8 @@ type ImageEngine interface {
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/images.go b/pkg/domain/entities/images.go
index 20682b05b..bc8a34c13 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -144,6 +144,43 @@ 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:"Filter,omitempty"`
@@ -172,3 +209,35 @@ 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/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 172c7d1a3..b93e7665a 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -5,6 +5,7 @@ package abi
import (
"context"
"io/ioutil"
+ "strconv"
"strings"
"github.com/containers/buildah"
@@ -12,13 +13,45 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
+ "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)
@@ -32,7 +65,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
}
@@ -58,7 +91,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
@@ -79,7 +112,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
@@ -103,7 +136,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
}
@@ -139,7 +172,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
}
@@ -155,7 +188,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
}
@@ -197,7 +230,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
@@ -245,7 +278,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
var reports []*entities.ContainerInspectReport
- 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
}
@@ -325,3 +358,154 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string,
}
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
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 5a7acb2f7..9d706a112 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -12,6 +12,7 @@ import (
"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"
@@ -20,6 +21,7 @@ import (
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"
)
@@ -260,6 +262,64 @@ func (ir *ImageEngine) Inspect(ctx context.Context, names []string, opts entitie
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 {
@@ -303,6 +363,7 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string,
}
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 {
@@ -315,3 +376,40 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string
}
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/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/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index c1bade4ba..1e71cba2c 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -2,11 +2,15 @@ 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"
)
@@ -210,3 +214,102 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string,
}
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")
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 6a8b9be37..516914a68 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,11 +2,14 @@ package tunnel
import (
"context"
+ "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"
)
@@ -157,3 +160,84 @@ func (ir *ImageEngine) Inspect(_ context.Context, names []string, opts entities.
}
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/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