summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/adapter/containers_remote.go50
-rw-r--r--libpod/adapter/images_remote.go19
-rw-r--r--libpod/adapter/runtime_remote.go98
-rw-r--r--libpod/boltdb_state.go12
-rw-r--r--libpod/boltdb_state_linux.go2
-rw-r--r--libpod/boltdb_state_unsupported.go2
-rw-r--r--libpod/common_test.go8
-rw-r--r--libpod/container.go21
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/container_internal_test.go2
-rw-r--r--libpod/networking_linux.go107
-rw-r--r--libpod/oci.go2
-rw-r--r--libpod/runtime_ctr.go2
13 files changed, 256 insertions, 71 deletions
diff --git a/libpod/adapter/containers_remote.go b/libpod/adapter/containers_remote.go
new file mode 100644
index 000000000..9623304e5
--- /dev/null
+++ b/libpod/adapter/containers_remote.go
@@ -0,0 +1,50 @@
+// +build remoteclient
+
+package adapter
+
+import (
+ "encoding/json"
+
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/inspect"
+)
+
+// Inspect returns an inspect struct from varlink
+func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) {
+ reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID())
+ if err != nil {
+ return nil, err
+ }
+ data := inspect.ContainerInspectData{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return &data, err
+}
+
+// ID returns the ID of the container
+func (c *Container) ID() string {
+ return c.config.ID
+}
+
+// GetArtifact returns a container's artifacts
+func (c *Container) GetArtifact(name string) ([]byte, error) {
+ var data []byte
+ reply, err := iopodman.ContainerArtifacts().Call(c.Runtime.Conn, c.ID(), name)
+ if err != nil {
+ return nil, err
+ }
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return data, err
+}
+
+// Config returns a container's Config ... same as ctr.Config()
+func (c *Container) Config() *libpod.ContainerConfig {
+ if c.config != nil {
+ return c.config
+ }
+ return c.Runtime.Config(c.ID())
+}
diff --git a/libpod/adapter/images_remote.go b/libpod/adapter/images_remote.go
index 77b0629a7..e7b38dccc 100644
--- a/libpod/adapter/images_remote.go
+++ b/libpod/adapter/images_remote.go
@@ -3,15 +3,22 @@
package adapter
import (
- "github.com/containers/libpod/libpod"
+ "context"
+ "encoding/json"
+
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/pkg/inspect"
)
-// Images returns information for the host system and its components
-func (r RemoteRuntime) Images() ([]libpod.InfoData, error) {
- conn, err := r.Connect()
+// Inspect returns returns an ImageData struct from over a varlink connection
+func (i *ContainerImage) Inspect(ctx context.Context) (*inspect.ImageData, error) {
+ reply, err := iopodman.InspectImage().Call(i.Runtime.Conn, i.ID())
if err != nil {
return nil, err
}
- _ = conn
- return nil, nil
+ data := inspect.ImageData{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return &data, nil
}
diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go
index 8ef8fe167..7189348bc 100644
--- a/libpod/adapter/runtime_remote.go
+++ b/libpod/adapter/runtime_remote.go
@@ -4,15 +4,18 @@ package adapter
import (
"context"
+ "encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/containers/image/types"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
+ "github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/varlink/go/varlink"
)
@@ -80,21 +83,9 @@ type Container struct {
// remoteContainer ....
type remoteContainer struct {
- ID string
- Image string
- ImageID string
- Command []string
- Created time.Time
- RunningFor string
- Status string
- //Ports []ocicni.PortMapping
- RootFsSize int64
- RWSize int64
- Names string
- Labels []map[string]string
- // Mounts []string
- // ContainerRunning bool
- //Namespaces []LinuxNameSpace
+ Runtime *LocalRuntime
+ config *libpod.ContainerConfig
+ state *libpod.ContainerState
}
// GetImages returns a slice of containerimages over a varlink connection
@@ -272,39 +263,60 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error)
// LookupContainer gets basic information about container over a varlink
// connection and then translates it to a *Container
-func (r *RemoteRuntime) LookupContainer(idOrName string) (*Container, error) {
- container, err := iopodman.GetContainer().Call(r.Conn, idOrName)
+func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
+ state, err := r.ContainerState(idOrName)
if err != nil {
return nil, err
}
- return listContainerDataToContainer(container)
+ config := r.Config(idOrName)
+ if err != nil {
+ return nil, err
+ }
+
+ rc := remoteContainer{
+ r,
+ config,
+ state,
+ }
+
+ c := Container{
+ rc,
+ }
+ return &c, nil
+}
+
+func (r *LocalRuntime) GetLatestContainer() (*Container, error) {
+ return nil, libpod.ErrNotImplemented
}
-// listContainerDataToContainer takes a varlink listcontainerData struct and makes
-// an "adapted" Container
-func listContainerDataToContainer(listData iopodman.ListContainerData) (*Container, error) {
- created, err := splitStringDate(listData.Createdat)
+// ContainerState returns the "state" of the container.
+func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, error) { //no-lint
+ reply, err := iopodman.ContainerStateData().Call(r.Conn, name)
if err != nil {
return nil, err
}
- rc := remoteContainer{
- // TODO commented out attributes will be populated when podman-remote ps
- // is implemented. They are not needed yet for basic container operations.
- ID: listData.Id,
- Image: listData.Image,
- ImageID: listData.Imageid,
- Command: listData.Command,
- Created: created,
- RunningFor: listData.Runningfor,
- Status: listData.Status,
- //ports:
- RootFsSize: listData.Rootfssize,
- RWSize: listData.Rwsize,
- Names: listData.Names,
- //Labels:
- //Mounts
- //ContainerRunning:
- //namespaces:
+ data := libpod.ContainerState{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
}
- return &Container{rc}, nil
+ return &data, err
+
+}
+
+// Config returns a container config
+func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig {
+ // TODO the Spec being returned is not populated. Matt and I could not figure out why. Will defer
+ // further looking into it for after devconf.
+ // The libpod function for this has no errors so we are kind of in a tough
+ // spot here. Logging the errors for now.
+ reply, err := iopodman.ContainerConfig().Call(r.Conn, name)
+ if err != nil {
+ logrus.Error("call to container.config failed")
+ }
+ data := libpod.ContainerConfig{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ logrus.Error("failed to unmarshal container inspect data")
+ }
+ return &data
+
}
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index e7a07a9a8..5bc15dd7f 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -205,7 +205,7 @@ func (s *BoltState) Refresh() error {
return errors.Wrapf(ErrInternal, "container %s missing state in DB", string(id))
}
- state := new(containerState)
+ state := new(ContainerState)
if err := json.Unmarshal(stateBytes, state); err != nil {
return errors.Wrapf(err, "error unmarshalling state for container %s", string(id))
@@ -325,7 +325,7 @@ func (s *BoltState) Container(id string) (*Container, error) {
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
db, err := s.getDBCon()
if err != nil {
@@ -361,7 +361,7 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
db, err := s.getDBCon()
if err != nil {
@@ -542,7 +542,7 @@ func (s *BoltState) UpdateContainer(ctr *Container) error {
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
}
- newState := new(containerState)
+ newState := new(ContainerState)
netNSPath := ""
ctrID := []byte(ctr.ID())
@@ -754,7 +754,7 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
if err := s.getContainerFromDB(id, ctr, ctrBucket); err != nil {
// If the error is a namespace mismatch, we can
@@ -1140,7 +1140,7 @@ func (s *BoltState) PodContainers(pod *Pod) ([]*Container, error) {
err = podCtrs.ForEach(func(id, val []byte) error {
newCtr := new(Container)
newCtr.config = new(ContainerConfig)
- newCtr.state = new(containerState)
+ newCtr.state = new(ContainerState)
ctrs = append(ctrs, newCtr)
return s.getContainerFromDB(id, newCtr, ctrBkt)
diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go
index d91f311e5..09a9be606 100644
--- a/libpod/boltdb_state_linux.go
+++ b/libpod/boltdb_state_linux.go
@@ -8,7 +8,7 @@ import (
// replaceNetNS handle network namespace transitions after updating a
// container's state.
-func replaceNetNS(netNSPath string, ctr *Container, newState *containerState) error {
+func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error {
if netNSPath != "" {
// Check if the container's old state has a good netns
if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() {
diff --git a/libpod/boltdb_state_unsupported.go b/libpod/boltdb_state_unsupported.go
index 64610d304..244dc51a0 100644
--- a/libpod/boltdb_state_unsupported.go
+++ b/libpod/boltdb_state_unsupported.go
@@ -3,7 +3,7 @@
package libpod
// replaceNetNS is exclusive to the Linux platform and is a no-op elsewhere
-func replaceNetNS(netNSPath string, ctr *Container, newState *containerState) error {
+func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error {
return nil
}
diff --git a/libpod/common_test.go b/libpod/common_test.go
index 4af68a040..df730098e 100644
--- a/libpod/common_test.go
+++ b/libpod/common_test.go
@@ -48,7 +48,7 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error)
},
},
},
- state: &containerState{
+ state: &ContainerState{
State: ContainerStateRunning,
ConfigPath: "/does/not/exist/specs/" + id,
RunDir: "/does/not/exist/tmp/",
@@ -166,10 +166,10 @@ func testContainersEqual(t *testing.T, a, b *Container, allowedEmpty bool) {
aConfig := new(ContainerConfig)
bConfig := new(ContainerConfig)
- aState := new(containerState)
- bState := new(containerState)
+ aState := new(ContainerState)
+ bState := new(ContainerState)
- blankState := new(containerState)
+ blankState := new(ContainerState)
assert.Equal(t, a.valid, b.valid)
diff --git a/libpod/container.go b/libpod/container.go
index b5f6a29ba..b0589be3b 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -116,7 +116,7 @@ func (ns LinuxNS) String() string {
type Container struct {
config *ContainerConfig
- state *containerState
+ state *ContainerState
// Batched indicates that a container has been locked as part of a
// Batch() operation
@@ -136,10 +136,10 @@ type Container struct {
requestedIP net.IP
}
-// containerState contains the current state of the container
+// ContainerState contains the current state of the container
// It is stored on disk in a tmpfs and recreated on reboot
// easyjson:json
-type containerState struct {
+type ContainerState struct {
// The current state of the running container
State ContainerStatus `json:"state"`
// The path to the JSON OCI runtime spec for this container
@@ -1063,3 +1063,18 @@ func networkDisabled(c *Container) (bool, error) {
}
return false, nil
}
+
+// ContainerState returns containerstate struct
+func (c *Container) ContainerState() (*ContainerState, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
+ }
+ returnConfig := new(ContainerState)
+ deepcopier.Copy(c.state).To(returnConfig)
+ return c.state, nil
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index ce8791f08..39c1501da 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -388,7 +388,7 @@ func (c *Container) teardownStorage() error {
// Reset resets state fields to default values
// It is performed before a refresh and clears the state after a reboot
// It does not save the results - assumes the database will do that for us
-func resetState(state *containerState) error {
+func resetState(state *ContainerState) error {
state.PID = 0
state.Mountpoint = ""
state.Mounted = false
diff --git a/libpod/container_internal_test.go b/libpod/container_internal_test.go
index 124f1d20e..f1e2b70a7 100644
--- a/libpod/container_internal_test.go
+++ b/libpod/container_internal_test.go
@@ -37,7 +37,7 @@ func TestPostDeleteHooks(t *testing.T) {
},
StaticDir: dir, // not the bundle, but good enough for this test
},
- state: &containerState{
+ state: &ContainerState{
ExtensionStageHooks: map[string][]rspec.Hook{
"poststop": {
rspec.Hook{
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index a343bee6a..f9caf26d1 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -121,6 +121,19 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result,
return ctrNS, networkStatus, err
}
+type slirp4netnsCmdArg struct {
+ Proto string `json:"proto,omitempty"`
+ HostAddr string `json:"host_addr"`
+ HostPort int32 `json:"host_port"`
+ GuestAddr string `json:"guest_addr"`
+ GuestPort int32 `json:"guest_port"`
+}
+
+type slirp4netnsCmd struct {
+ Execute string `json:"execute"`
+ Args slirp4netnsCmdArg `json:"arguments"`
+}
+
// Configure the network namespace for a rootless container
func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
defer ctr.rootlessSlirpSyncR.Close()
@@ -139,7 +152,15 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
defer syncR.Close()
defer syncW.Close()
- cmd := exec.Command(path, "-c", "-e", "3", "-r", "4", fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ havePortMapping := len(ctr.Config().PortMappings) > 0
+ apiSocket := filepath.Join(r.ociRuntime.tmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
+ var cmd *exec.Cmd
+ if havePortMapping {
+ // if we need ports to be mapped from the host, create a API socket to use for communicating with slirp4netns.
+ cmd = exec.Command(path, "-c", "-e", "3", "-r", "4", "--api-socket", apiSocket, fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ } else {
+ cmd = exec.Command(path, "-c", "-e", "3", "-r", "4", fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ }
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
@@ -162,19 +183,99 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
if os.IsTimeout(err) {
// Check if the process is still running.
var status syscall.WaitStatus
- _, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
+ pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
if err != nil {
return errors.Wrapf(err, "failed to read slirp4netns process status")
}
+ if pid != cmd.Process.Pid {
+ continue
+ }
if status.Exited() || status.Signaled() {
return errors.New("slirp4netns failed")
}
-
continue
}
return errors.Wrapf(err, "failed to read from slirp4netns sync pipe")
}
}
+
+ if havePortMapping {
+ const pidWaitTimeout = 60 * time.Second
+ chWait := make(chan error)
+ go func() {
+ interval := 25 * time.Millisecond
+ for i := time.Duration(0); i < pidWaitTimeout; i += interval {
+ // Check if the process is still running.
+ var status syscall.WaitStatus
+ pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
+ if err != nil {
+ break
+ }
+ if pid != cmd.Process.Pid {
+ continue
+ }
+ if status.Exited() || status.Signaled() {
+ chWait <- fmt.Errorf("slirp4netns exited with status %d", status.ExitStatus())
+ }
+ time.Sleep(interval)
+ }
+ }()
+ defer close(chWait)
+
+ // wait that API socket file appears before trying to use it.
+ if _, err := WaitForFile(apiSocket, chWait, pidWaitTimeout*time.Millisecond); err != nil {
+ return errors.Wrapf(err, "waiting for slirp4nets to create the api socket file %s", apiSocket)
+ }
+
+ // for each port we want to add we need to open a connection to the slirp4netns control socket
+ // and send the add_hostfwd command.
+ for _, i := range ctr.config.PortMappings {
+ conn, err := net.Dial("unix", apiSocket)
+ if err != nil {
+ return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
+ }
+ defer conn.Close()
+ hostIP := i.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ cmd := slirp4netnsCmd{
+ Execute: "add_hostfwd",
+ Args: slirp4netnsCmdArg{
+ Proto: i.Protocol,
+ HostAddr: hostIP,
+ HostPort: i.HostPort,
+ GuestPort: i.ContainerPort,
+ },
+ }
+ // create the JSON payload and send it. Mark the end of request shutting down writes
+ // to the socket, as requested by slirp4netns.
+ data, err := json.Marshal(&cmd)
+ if err != nil {
+ return errors.Wrapf(err, "cannot marshal JSON for slirp4netns")
+ }
+ if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil {
+ return errors.Wrapf(err, "cannot write to control socket %s", apiSocket)
+ }
+ if err := conn.(*net.UnixConn).CloseWrite(); err != nil {
+ return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket)
+ }
+ buf := make([]byte, 2048)
+ len, err := conn.Read(buf)
+ if err != nil {
+ return errors.Wrapf(err, "cannot read from control socket %s", apiSocket)
+ }
+ // if there is no 'error' key in the received JSON data, then the operation was
+ // successful.
+ var y map[string]interface{}
+ if err := json.Unmarshal(buf[0:len], &y); err != nil {
+ return errors.Wrapf(err, "error parsing error status from slirp4netns")
+ }
+ if e, found := y["error"]; found {
+ return errors.Errorf("error from slirp4netns while setting up port redirection: %v", e)
+ }
+ }
+ }
return nil
}
diff --git a/libpod/oci.go b/libpod/oci.go
index b0c19d5e8..e55bd57dc 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -323,7 +323,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", os.Getenv("HOME")))
cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
- if r.reservePorts {
+ if r.reservePorts && !ctr.config.NetMode.IsSlirp4netns() {
ports, err := bindPorts(ctr.config.PortMappings)
if err != nil {
return err
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 68599fe6d..6d5ce5a7e 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -48,7 +48,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
ctr.config.ID = stringid.GenerateNonCryptoID()