summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/runtime.go4
-rw-r--r--pkg/adapter/runtime_remote.go6
-rw-r--r--pkg/network/config.go1
-rw-r--r--pkg/rootless/rootless.go3
-rw-r--r--pkg/rootless/rootless_linux.go2
-rw-r--r--pkg/rootlessport/rootlessport_linux.go262
-rw-r--r--pkg/spec/namespaces.go20
-rw-r--r--pkg/spec/security.go6
-rw-r--r--pkg/util/utils.go2
-rw-r--r--pkg/varlinkapi/images.go12
-rw-r--r--pkg/varlinkapi/virtwriter/virtwriter.go10
11 files changed, 321 insertions, 7 deletions
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index dd4f0f35f..8933e826f 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -84,7 +84,7 @@ func getRuntime(runtime *libpod.Runtime) (*LocalRuntime, error) {
}, nil
}
-// GetFilterImages returns a slice of images in containerimages that are "filtered"
+// GetFilteredImages returns a slice of images in containerimages that are "filtered"
func (r *LocalRuntime) GetFilteredImages(filters []string, rwOnly bool) ([]*ContainerImage, error) {
images, err := r.ImageRuntime().GetImagesWithFilters(filters)
if err != nil {
@@ -111,6 +111,8 @@ func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) {
return r.ImagestoContainerImages(images, rwOnly)
}
+// ImagestoContainerImages converts the slice of *image.Image to a slice of
+// *ContainerImage. ReadOnly images are skipped when rwOnly is set.
func (r *LocalRuntime) ImagestoContainerImages(images []*image.Image, rwOnly bool) ([]*ContainerImage, error) {
var containerImages []*ContainerImage
for _, i := range images {
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index fe5cc4fef..9c10b31c0 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -413,6 +413,12 @@ func (ci *ContainerImage) TagImage(tag string) error {
return err
}
+// UntagImage removes a single tag from an image
+func (ci *ContainerImage) UntagImage(tag string) error {
+ _, err := iopodman.UntagImage().Call(ci.Runtime.Conn, ci.ID(), tag)
+ return err
+}
+
// RemoveImage calls varlink to remove an image
func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) {
ir := image.ImageDeleteResponse{}
diff --git a/pkg/network/config.go b/pkg/network/config.go
index e47b16143..a41455f68 100644
--- a/pkg/network/config.go
+++ b/pkg/network/config.go
@@ -90,6 +90,7 @@ func (p PortMapConfig) Bytes() ([]byte, error) {
return json.MarshalIndent(p, "", "\t")
}
+// IPAMDHCP describes the ipamdhcp config
type IPAMDHCP struct {
DHCP string `json:"type"`
}
diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go
index 7e9fe9db6..d02721ea9 100644
--- a/pkg/rootless/rootless.go
+++ b/pkg/rootless/rootless.go
@@ -7,6 +7,9 @@ import (
"github.com/pkg/errors"
)
+// TryJoinPauseProcess attempts to join the namespaces of the pause PID via
+// TryJoinFromFilePaths. If joining fails, it attempts to delete the specified
+// file.
func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {
if _, err := os.Stat(pausePidPath); err != nil {
return false, -1, nil
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 94c42f7d0..182a39f6b 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -514,6 +514,8 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st
return joinUserAndMountNS(uint(pausePid), pausePidPath)
}
+
+// ReadMappingsProc parses and returns the ID mappings at the specified path.
func ReadMappingsProc(path string) ([]idtools.IDMap, error) {
file, err := os.Open(path)
if err != nil {
diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go
new file mode 100644
index 000000000..655d1a448
--- /dev/null
+++ b/pkg/rootlessport/rootlessport_linux.go
@@ -0,0 +1,262 @@
+// +build linux
+
+// Package rootlessport provides reexec for RootlessKit-based port forwarder.
+//
+// init() contains reexec.Register() for ReexecKey .
+//
+// The reexec requires Config to be provided via stdin.
+//
+// The reexec writes human-readable error message on stdout on error.
+//
+// Debug log is printed on stderr.
+package rootlessport
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "syscall"
+
+ "github.com/containernetworking/plugins/pkg/ns"
+ "github.com/containers/storage/pkg/reexec"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/pkg/errors"
+ rkport "github.com/rootless-containers/rootlesskit/pkg/port"
+ rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin"
+ rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ // ReexecKey is the reexec key for the parent process.
+ ReexecKey = "containers-rootlessport"
+ // reexecChildKey is used internally for the second reexec
+ reexecChildKey = "containers-rootlessport-child"
+ reexecChildEnvOpaque = "_CONTAINERS_ROOTLESSPORT_CHILD_OPAQUE"
+)
+
+// Config needs to be provided to the process via stdin as a JSON string.
+// stdin needs to be closed after the message has been written.
+type Config struct {
+ Mappings []ocicni.PortMapping
+ NetNSPath string
+ ExitFD int
+ ReadyFD int
+}
+
+func init() {
+ reexec.Register(ReexecKey, func() {
+ if err := parent(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ })
+ reexec.Register(reexecChildKey, func() {
+ if err := child(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ })
+
+}
+
+func loadConfig(r io.Reader) (*Config, io.ReadCloser, io.WriteCloser, error) {
+ stdin, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ var cfg Config
+ if err := json.Unmarshal(stdin, &cfg); err != nil {
+ return nil, nil, nil, err
+ }
+ if cfg.NetNSPath == "" {
+ return nil, nil, nil, errors.New("missing NetNSPath")
+ }
+ if cfg.ExitFD <= 0 {
+ return nil, nil, nil, errors.New("missing ExitFD")
+ }
+ exitFile := os.NewFile(uintptr(cfg.ExitFD), "exitfile")
+ if exitFile == nil {
+ return nil, nil, nil, errors.New("invalid ExitFD")
+ }
+ if cfg.ReadyFD <= 0 {
+ return nil, nil, nil, errors.New("missing ReadyFD")
+ }
+ readyFile := os.NewFile(uintptr(cfg.ReadyFD), "readyfile")
+ if readyFile == nil {
+ return nil, nil, nil, errors.New("invalid ReadyFD")
+ }
+ return &cfg, exitFile, readyFile, nil
+}
+
+func parent() error {
+ // load config from stdin
+ cfg, exitR, readyW, err := loadConfig(os.Stdin)
+ if err != nil {
+ return err
+ }
+
+ // create the parent driver
+ stateDir, err := ioutil.TempDir("", "rootlessport")
+ if err != nil {
+ return err
+ }
+ driver, err := rkbuiltin.NewParentDriver(&logrusWriter{prefix: "parent: "}, stateDir)
+ if err != nil {
+ return err
+ }
+ initComplete := make(chan struct{})
+ quit := make(chan struct{})
+ errCh := make(chan error)
+ // start the parent driver. initComplete will be closed when the child connected to the parent.
+ logrus.Infof("starting parent driver")
+ go func() {
+ driverErr := driver.RunParentDriver(initComplete, quit, nil)
+ if driverErr != nil {
+ logrus.WithError(driverErr).Warn("parent driver exited")
+ }
+ errCh <- driverErr
+ }()
+ opaque := driver.OpaqueForChild()
+ logrus.Infof("opaque=%+v", opaque)
+ opaqueJSON, err := json.Marshal(opaque)
+ if err != nil {
+ return err
+ }
+ childQuitR, childQuitW, err := os.Pipe()
+ if err != nil {
+ return err
+ }
+ defer func() {
+ // stop the child
+ logrus.Info("stopping child driver")
+ if err := childQuitW.Close(); err != nil {
+ logrus.WithError(err).Warn("unable to close childQuitW")
+ }
+ }()
+
+ // reexec the child process in the child netns
+ cmd := exec.Command(fmt.Sprintf("/proc/%d/exe", os.Getpid()))
+ cmd.Args = []string{reexecChildKey}
+ cmd.Stdin = childQuitR
+ cmd.Stdout = &logrusWriter{prefix: "child"}
+ cmd.Stderr = cmd.Stdout
+ cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON))
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Pdeathsig: syscall.SIGTERM,
+ }
+ childNS, err := ns.GetNS(cfg.NetNSPath)
+ if err != nil {
+ return err
+ }
+ if err := childNS.Do(func(_ ns.NetNS) error {
+ logrus.Infof("starting child driver in child netns (%q %v)", cmd.Path, cmd.Args)
+ return cmd.Start()
+ }); err != nil {
+ return err
+ }
+
+ logrus.Info("waiting for initComplete")
+ // wait for the child to connect to the parent
+ select {
+ case <-initComplete:
+ logrus.Infof("initComplete is closed; parent and child established the communication channel")
+ case err := <-errCh:
+ return err
+ }
+ defer func() {
+ logrus.Info("stopping parent driver")
+ quit <- struct{}{}
+ if err := <-errCh; err != nil {
+ logrus.WithError(err).Warn("parent driver returned error on exit")
+ }
+ }()
+
+ // let parent expose ports
+ logrus.Infof("exposing ports %v", cfg.Mappings)
+ if err := exposePorts(driver, cfg.Mappings); err != nil {
+ return err
+ }
+
+ // write and close ReadyFD (convention is same as slirp4netns --ready-fd)
+ logrus.Info("ready")
+ if _, err := readyW.Write([]byte("1")); err != nil {
+ return err
+ }
+ if err := readyW.Close(); err != nil {
+ return err
+ }
+
+ // wait for ExitFD to be closed
+ logrus.Info("waiting for exitfd to be closed")
+ if _, err := ioutil.ReadAll(exitR); err != nil {
+ return err
+ }
+ return nil
+}
+
+func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping) error {
+ ctx := context.TODO()
+ for _, i := range portMappings {
+ hostIP := i.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ spec := rkport.Spec{
+ Proto: i.Protocol,
+ ParentIP: hostIP,
+ ParentPort: int(i.HostPort),
+ ChildPort: int(i.ContainerPort),
+ }
+ if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
+ return err
+ }
+ if _, err := pm.AddPort(ctx, spec); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func child() error {
+ // load the config from the parent
+ var opaque map[string]string
+ if err := json.Unmarshal([]byte(os.Getenv(reexecChildEnvOpaque)), &opaque); err != nil {
+ return err
+ }
+
+ // start the child driver
+ quit := make(chan struct{})
+ errCh := make(chan error)
+ go func() {
+ d := rkbuiltin.NewChildDriver(os.Stderr)
+ dErr := d.RunChildDriver(opaque, quit)
+ errCh <- dErr
+ }()
+ defer func() {
+ logrus.Info("stopping child driver")
+ quit <- struct{}{}
+ if err := <-errCh; err != nil {
+ logrus.WithError(err).Warn("child driver returned error on exit")
+ }
+ }()
+
+ // wait for stdin to be closed
+ if _, err := ioutil.ReadAll(os.Stdin); err != nil {
+ return err
+ }
+ return nil
+}
+
+type logrusWriter struct {
+ prefix string
+}
+
+func (w *logrusWriter) Write(p []byte) (int, error) {
+ logrus.Infof("%s%s", w.prefix, string(p))
+ return len(p), nil
+}
diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go
index a45137416..8e95a3ca0 100644
--- a/pkg/spec/namespaces.go
+++ b/pkg/spec/namespaces.go
@@ -17,6 +17,7 @@ import (
"github.com/sirupsen/logrus"
)
+// ToCreateOptions converts the input to a slice of container create options.
func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
var portBindings []ocicni.PortMapping
var err error
@@ -97,6 +98,8 @@ func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserCon
return options, nil
}
+// ConfigureGenerator configures the generator based according to the current
+// state of the NetworkConfig.
func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
netMode := c.NetMode
if netMode.IsHost() {
@@ -183,6 +186,7 @@ func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
return portBindings, nil
}
+// ToCreateOptions converts the input to container create options.
func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.CgroupMode.IsNS() {
@@ -213,6 +217,7 @@ func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCre
return options, nil
}
+// ToCreateOptions converts the input to container create options.
func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.UsernsMode.IsNS() {
@@ -241,6 +246,8 @@ func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreat
return options, nil
}
+// ConfigureGenerator configures the generator according to the current state
+// of the UserConfig.
func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
if IsNS(string(c.UsernsMode)) {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
@@ -271,11 +278,14 @@ func (c *UserConfig) getPostConfigureNetNS() bool {
return postConfigureNetNS
}
+// InNS returns true if the UserConfig indicates to be in a dedicated user
+// namespace.
func (c *UserConfig) InNS(isRootless bool) bool {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
return isRootless || (hasUserns && !c.UsernsMode.IsHost())
}
+// ToCreateOptions converts the input to container create options.
func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.IpcMode.IsHost() {
@@ -293,6 +303,8 @@ func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreate
return options, nil
}
+// ConfigureGenerator configures the generator according to the current state
+// of the IpcConfig.
func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
ipcMode := c.IpcMode
if IsNS(string(ipcMode)) {
@@ -308,6 +320,8 @@ func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
return nil
}
+// ConfigureGenerator configures the generator according to the current state
+// of the CgroupConfig.
func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
cgroupMode := c.CgroupMode
if cgroupMode.IsDefaultValue() {
@@ -337,6 +351,7 @@ func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
return nil
}
+// ToCreateOptions converts the input to container create options.
func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.PidMode.IsContainer() {
@@ -351,6 +366,8 @@ func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreate
return options, nil
}
+// ConfigureGenerator configures the generator according to the current state
+// of the PidConfig.
func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
pidMode := c.PidMode
if IsNS(string(pidMode)) {
@@ -368,6 +385,7 @@ func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
return nil
}
+// ToCreateOptions converts the input to container create options.
func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if IsPod(string(c.UtsMode)) {
@@ -391,6 +409,8 @@ func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([
return options, nil
}
+// ConfigureGenerator configures the generator according to the current state
+// of the UtsConfig.
func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
hostname := c.Hostname
var err error
diff --git a/pkg/spec/security.go b/pkg/spec/security.go
index 05ed94e66..372fe87c6 100644
--- a/pkg/spec/security.go
+++ b/pkg/spec/security.go
@@ -11,6 +11,8 @@ import (
"github.com/pkg/errors"
)
+// 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))
@@ -18,6 +20,8 @@ func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
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()
@@ -57,6 +61,7 @@ func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidCon
return nil
}
+// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.).
func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error {
for _, opt := range securityOpts {
if opt == "no-new-privileges" {
@@ -91,6 +96,7 @@ func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts [
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
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index f7d04c73b..c9d09b8b5 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -304,7 +304,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) {
return config, nil
}
-// Parse and validate a signal name or number
+// ParseSignal parses and validates a signal name or number.
func ParseSignal(rawSignal string) (syscall.Signal, error) {
// Strip off leading dash, to allow -1 or -HUP
basename := strings.TrimPrefix(rawSignal, "-")
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index ac92343d0..bc644f87c 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -450,6 +450,18 @@ func (i *LibpodAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error
return call.ReplyTagImage(newImage.ID())
}
+// UntagImage accepts an image name and tag as strings and removes the tag from the local store.
+func (i *LibpodAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) error {
+ newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return call.ReplyImageNotFound(name, err.Error())
+ }
+ if err := newImage.UntagImage(tag); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyUntagImage(newImage.ID())
+}
+
// RemoveImage accepts a image name or ID as a string and force bool to determine if it should
// remove the image even if being used by stopped containers
func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bool) error {
diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go
index dd171943f..d96e82a3f 100644
--- a/pkg/varlinkapi/virtwriter/virtwriter.go
+++ b/pkg/varlinkapi/virtwriter/virtwriter.go
@@ -27,13 +27,13 @@ const (
TerminalResize SocketDest = iota
// Quit and detach
Quit SocketDest = iota
- // Quit from the client
+ // HangUpFromClient hangs up from the client
HangUpFromClient SocketDest = iota
)
-// ClientHangup signifies that the client wants to drop its
-// connection from the server
-var ClientHangup = errors.New("client hangup")
+// ErrClientHangup signifies that the client wants to drop its connection from
+// the server.
+var ErrClientHangup = errors.New("client hangup")
// IntToSocketDest returns a socketdest based on integer input
func IntToSocketDest(i int) SocketDest {
@@ -177,7 +177,7 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
//
// reproducer: echo hello | (podman-remote run -i alpine cat)
time.Sleep(1 * time.Second)
- return ClientHangup
+ return ErrClientHangup
default:
// Something really went wrong
return errors.New("unknown multiplex destination")